iOS 开发总结(精华篇一)

1. Objective-C是动态运行时语言是什么意思?

主要是将数据类型的确定由编译时, 推迟到了运行时。
主要涉及两个概念: 运行时多态

运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象的指定方法。
多态:不同对象以自己的方式响应相同的消息的能力叫多态。
例如:生物类都有一个共同方法 - (void)eat;人属于生物,狗也属于生物,都继承相同东西,实现各自的eat,但是调用时我们只需调用各自eat方法。

不同对象以自己的方式响应了相同的消息,因此可以说运行时是多态的基础。

2. 讲一下MVC和MVVM,MVP?

MVC:

iOS 开发总结(精华篇一)_第1张图片
MVC 模式
  • Model 与 View 永远不能相互通信,只能通过 Controller 传递。
  • Controller 可以直接与 Model 对话(读写调用 Model),Model 通过 Notification 和 KVO 机制与 Controller 间接通信。
  • Controller 可以直接与 View 对话,通过 outlet,直接操作 View,outlet 直接对应到 View 中的控件,View 通过 action 向 Controller 报告事件的发生(如用户 Touch 了我),Controller 是 View 的直接数据源(数据很可能是 Controller 从 Model 中取得并经过加工了)。Controller 是 View 的代理(delegate),以同步 View 和 Controller。

MVP:

MVP 模式将 Controller 改名为 Presenter(提出者,任命者),同时改变了通信方向。


iOS 开发总结(精华篇一)_第2张图片
MVP 模式
  • 各部分之间的通信都是双向的。
  • View 和 Model 不发生关系,都通过 Presenter 传递。
  • View 非常薄,不部署任何业务逻辑,称为"滚动视图"(Passive View),即没有任何主动性,而 Presenter 非常厚,所有部署都部署在这里。

3. 为什么代理要用weak?代理的delegate和dataSource有什么区别?block和代理的区别?

  • weak 修饰的对象不持有 delegate 这个对象,delegate 这个对象的销毁由外部控制。
    strong修饰的对象强引用,持有这个对象,外部不能销毁 delegate 对象,引起循环引用。

  • DataSource 是数据源,用来连接数据的,提供获取数据方法。
    Delegate 是代理函数,提供一些操作方法。

  • 代理和 block 都是回调机制,代理方法比较多,分散,block 代码比较集中统一。
    block 出栈需要将数据从栈内存拷贝到堆内存,对象引用计数加1,使用完或者 block 置为nil 后才清除。
    delegate 只是保存了一个对象指针,直接回调,没有额外消耗。

4.属性的实质是什么?包括哪几个部分?属性默认的关键字都有哪些?@dynamic关键字和@synthesize关键字是用来做什么的?

属性的本质:
@property = ivar + setter + getter;

属性包括三部分:

  • 线程安全:nonatomic(线程不安全),atomic(线程安全,setter、getter)。

  • 内存管理:strongretainassignweakcopy
    - strong:ARC 模式下,对对象进行强引用,释放旧值,设置新值,引用计数加1。
    - retain:MRC 模式下,对对象进行强引用,释放旧值,设置新值,引用计数加1;
    - assign:修饰基本数据类型,也可修饰 OC 对象,释放旧值,不保留新值,修饰的属性被释放后内存地址仍在,没有被释放,没被置为 nil,造成野指针,当再次使用这个属性,发生崩溃。
    - weak:只能修饰 OC 对象,弱引用,修饰对象被释放后,指针地址也被置为 nil。

  • 读写权限:readwrite(可读可写)、readonly(只读)。

@dynamic 系统不会自动生成 settergetter 方法,编译时不会报错,运行时用到 settergetter 方法会崩溃。
@synthesize(默认) 系统自动生成 settergetter 方法。

5. 属性的默认关键字是什么?

对于基本数据类型:atomicreadwriteassign;
对于普通 OC 对象:atomicreadwritestrong

6. NSString为什么要用copy关键字,如果用strong会有什么问题?(注意:这里没有说用strong就一定不行。使用copy和strong是看情况而定的)

因为 NSStringNSDictionaryNSArray 有对应的可变类型,他们经常会用到赋值操作,为保证字符串数值不会无意间被改动,应该在设置新属性值是拷贝一份。
若使用 strong,那么这个属性就可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性。

7. 如何令自己所写的对象具有拷贝功能?

若想另自己写的代码具有拷贝功能,则需要实现 NSCoping 协议,如果自定义的对象分为可变版本和不可变版本,那么就要同时实现NSCopingNSMutableCoping协议。
例子:

ViewController.m

#import "ViewController.h"
#import "SecondViewController.h"
#import "PersonModel.h"

@interface ViewController ()

@end

@implementation ViewController
{
    PersonModel *model;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    model = [[PersonModel alloc] init];
    model.name = @"张三";
    model.sex = @"男";
}

- (void)viewWillAppear:(BOOL)animated
{
    NSLog(@"%@---%@---%@", [self class], model.name, model.sex);
}

- (IBAction)buttonClicked:(UIButton *)sender
{
    SecondViewController *vc = [[SecondViewController alloc] init];
    vc.model = model;
    [self.navigationController pushViewController:vc animated:YES];
}

Person.h

#import 

@interface PersonModel : NSObject 

@property (nonatomic, copy) NSString *name;

@property (nonatomic, copy) NSString *sex;

@end

Person.m

#import "PersonModel.h"

@implementation PersonModel

//- (id)copyWithZone:(NSZone *)zone
//{
//    PersonModel *model = [PersonModel allocWithZone:zone];
//    return model;
//}

@end

SecondViewController.h


#import 
#import "PersonModel.h"

@interface SecondViewController : UIViewController

@property (nonatomic, strong) PersonModel *model;

//@property (nonatomic, copy) PersonModel *model;

@end

SecondViewController.m

#import "SecondViewController.h"

@interface SecondViewController ()

@end

@implementation SecondViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor redColor];
    
    _model.name = @"哈哈";
    
    NSLog(@"%@---%@", _model.name, _model.sex);
}
@end

使用 Strong 传输自定义 Model 输出结果:
2017-06-13 18:03:06.739 NSCoping[8834:261731] ViewController---张三---男
2017-06-13 18:03:53.154 NSCoping[8834:261731] 哈哈---男
2017-06-13 18:04:12.714 NSCoping[8834:261731] ViewController---哈哈---男

分析:第一次打印为进入首页时数据,
第二次打印为跳转后修改了 name 后打印的结果,
第三次打印为返回首页打印的结果,
第三次打印结果中因为 SecondViewcontroller.h 中 Person 使用了 strong, 不需要遵循 NSCoping 协议,返回首页时把原来的值也给修改了。

使用 copy 传输自定义 Model 输出结果:
2017-06-13 18:10:07.668 NSCoping[8979:265669] ViewController---张三---男
2017-06-13 18:10:09.105 NSCoping[8979:265669] 哈哈---(null)
2017-06-13 18:10:10.997 NSCoping[8979:265669] ViewController---张三---男

发现第三次的打印结果和第一次相比没有变化。

代码demo

8. 可变集合类 和 不可变集合类的 copy 和 mutablecopy有什么区别?如果是集合是内容复制的话,集合里面的元素也是内容复制么?

例子:

mutablecopy:

ViewController.h

- (IBAction)buttonClicked:(UIButton *)sender
{
    NSArray *dataSource = @[@"张三", @"李四", @"王五"];
    NSLog(@"%p", dataSource);
    
    ThirdViewController *vc = [[ThirdViewController alloc] init];
    vc.dataSource = [dataSource mutableCopy];
    [self.navigationController pushViewController:vc animated:YES];
}

ThirdViewController.h

#import 

@interface ThirdViewController : UIViewController

@property (nonatomic, copy) NSArray *dataSource;

@end

ThirdViewController.m

#import "ThirdViewController.h"

@interface ThirdViewController ()

@end

@implementation ThirdViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    NSLog(@"%p", _dataSource);
}


@end

输出结果
2017-06-14 10:28:58.155 NSCoping[2369:51665] 0x600000057850
2017-06-14 10:28:58.164 NSCoping[2369:51665] 0x60000004bd00

copy

ViewController.h

- (IBAction)buttonClicked:(UIButton *)sender
{
    NSArray *dataSource = @[@"张三", @"李四", @"王五"];
    NSLog(@"%p", dataSource);
    
    ThirdViewController *vc = [[ThirdViewController alloc] init];
    vc.dataSource = [dataSource mutableCopy];
    [self.navigationController pushViewController:vc animated:YES];
}

输出结果:
2017-06-14 10:33:06.690 NSCoping[2433:53481] 0x600000052660
2017-06-14 10:33:06.698 NSCoping[2433:53481] 0x600000052660

由此得出结论:

**不可变集合的 copy 是浅拷贝,mutablecopy 是深拷贝,重新拷贝了对象的指针地址。
同理实验可以得出:可变集合的 copy 和 mutablecopy 都是深拷贝,因为对象的指针地址都发生了改变。
**

9. 为什么IBOutlet修饰的UIView也使用weak关键字?

当我们把控件拖到 StoryBoard 上时,相当于把这个控件加到了视图控制器的 View上,View 上有一个 addSubView 属性,这个属性是一个数组,里面所有的 View 都添加在这个数组中, View 对加到它上面的对象是强引用,当我们使用 Outlet属性时,我们仅仅在 ViewController中使用,没有必要拥有它,所有是weak

10. nonatomic和atomic的区别?atomic是绝对的线程安全么?为什么?如果不是,那应该如何实现?

nonatomicatomic主要区别在于系统生成的 setter/getter 方法不一样。如果自己写 setter/getter,那 atomic/nonatomic/retain/assign/copy 这些关键字只起提示作用,写不写都一样。

对于atomic的属性,系统生成的setter/getter会保证getset操作的完整性,不受其他线程影响。比如,线程 A 的 getter方法运行到一半,线程 B 调用了setter方法,那么线程 A 的getter还是能得到一个完整的对象。

nonatomic就不能保证线程完整了,所有,nonatomicatomic速度要快。

不过atomic并不能保证线程安全,如果 A 调了getter,与此同时,线程 B、线程 C 都调了setter,---那最后线程 A get 到的值,3种都有可能:可能 B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同时,最终这个属性的值,可能是 B set 的值, 也可能是 C set 的值。

11. 了解hitText 方法事件传递?

iOS 开发总结(精华篇一)_第3张图片

hitText作用: 当在一个屏幕上添加一个屏幕罩,但又不影响下面 view 的操作时,也就是透过屏幕罩对下面的 view 进行操作。

举例说明:(陌陌面试题)

问题描述:将 UIScrollView 和 UIView 都添加到 Controller 的 view 上,可以拖动 UIView, UIScrollView 也可以滚动。


示例图

调用过程:

iOS系统检测到手指触摸(Touch)操作时会将其放入当前活动Application的事件队列,UIApplication会从事件队列中取出触摸事件并传递给key window(当前接收用户事件的窗口)处理,window对象首先会使用hitTest:withEvent:方法寻找此次Touch操作初始点所在的视图(View),即需要将触摸事件传递给其处理的视图,称之为hit-test view。

window对象会在首先在view hierarchy的顶级view上调用hitTest:withEvent:,此方法会在视图层级结构中的每个视图上调用pointInside:withEvent:,如果pointInside:withEvent:返回YES,则继续逐级调用,直到找到touch操作发生的位置,这个视图也就是hit-test view。

hitText:withEvent:调用方法处理流程:

  1. 首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内;
  1. 若返回NO,则hitTest:withEvent:返回nil;
  2. 若返回YES,则向当前视图的所有子视图(subviews)发送hitTest:withEvent:消息,所有子视图的遍历顺序是从top到bottom,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕;
  3. 若第一次有子视图返回非空对象,则hitTest:withEvent:方法返回此对象,处理结束;
  4. 如所有子视图都返回非,则hitTest:withEvent:方法返回自身 self 。

主要代码:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    //1.判断下自己能否接收事件
    if (self.alpha <= 0.01) {
        return nil;
    }

    //2.判断是否在当前区域内点击
    if ([self pointInside:point withEvent:event] == NO) {
        return nil;
    }
    else
    {
        //3.把当前坐标系上的点转换成父视图上的点
        CGPoint newPoint  = [self convertPoint:point toView:self.superview];
        
        //4.获取想要响应的 view
        UIView *view = self.superview.subviews[0];
        
        if (CGRectContainsPoint(view.frame, newPoint)) {
            return view;
        }
        else
        {
            //如果没有比自己合适的子控件,最适合的 View 就是自己
            return self;
        }
    }
}

demo 下载地址

你可能感兴趣的:(iOS 开发总结(精华篇一))