Objective-C Basics
1.请说明并比较以下关键词:strong, weak, assign, copy
- strong 表示指向并拥有该对象。其修饰的对象引用计数会增加1,该对象只要引用计数不为0就不会被销毁,当然强行将其设置为nil,可以销毁它。
- weak 表示指向但不拥有该对象。其修饰的对象引用计数不会增加,无需手动设置,该对象会自行在内存中销毁。
- assign 主要用于修饰基本数据类型。如NSInteger和CGFloat,这些数值主要存在栈上。
- weak 一般用来修饰对象,assign一般用来修饰基本数据类型。原因是assign修饰的对象被释放后,指针的地址依然存在,造成野指正,在堆上容易造成奔溃,而栈上的内存系统会自动处理,不会造成野指针。
- copy与strong类似。不同之处是strong的复制是多个指针指向同一个地址,而copy的复制每次会在内存中拷贝一份对象,指针指向不同地址。copy一般用在修饰有可变对应类型的不可变对象上,如NSString,NSArray,NSDictionary。
- Objective-C 中,基本数据类型的默认关键字是atomic, readwrite, assign;普通属性的默认关键字是atomic, readwrite, strong。
2.请说明并比较以下关键词:__weak,__block
- __weak 与weak基本相同。前者用于修饰变量,后者用于修饰属性,__weak 主要用于防止block中的循环引用。
- __block 也用于修饰变量,它是引用修饰,所以其修饰的值是动态变化的,即可以被重新赋值的。__block用于修饰某些block内部将要修改的外部变量。
- __weak和__block 的使用场景几乎与block息息相关。而所谓block,就是Objective-C对于闭包的实现。闭包就是没有名字的函数,或者理解为指向函数的指针。
3.请说明并比较以下关键词:atomatic,nonatomic
- atomatic修饰的对象会保证setter和getter的完整性,任何线程对其访问都可以得到完成的初始化后的对象。因为要保证操作完成,所以速度慢。它比nonatomic安全,但也并不是绝对的线程安全,例如多个线程同时调用set和get就会导致获得的对象值不一样。绝对的线程安全要用关键词synchronized。
- nonatomic修饰的对象不保证setter和getter的完整性,所以多个线程对它进行访问,它可能会返回为初始化的对象。正因为如此,它比atomic快,但也是线程不安全的。
4.什么是ARC
ARC全称是Automatic Reference Counting,是Objective-C的内存管理机制。简单来说,就是代码自动加了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动的由编译器完成了。
ARC的使用是为了解决对象retain和release匹配的问题。以前手动管理造成内存泄露或者重复释放的问题将不复存在。
以前需要手动的通过retain去为对象获取内存,并用release释放内存。所以以前的操作称为MRC(Manual Reference Counting)。
5.什么情况下会出现循环引用?
循环引用是指2个或以上对象互相强引用,导致所有对象无法释放的现象。举个例子:
class Father
@interface Father:NSobject
@property (strong,nonatomic) Son *son;
@end
class Son
@interface Son:NSobject
@property (strong,nonatomic) Father *father;
@end
上述代码有两个类,分别为Father和Son,Father对Son强引用,Son对Father强引用。这样释放Son必须要先释放Father,要释放Father必须先释放Son。如此一来,两个对象都无法释放。
解决方法是将Father中的Son对象属性从strong改为weak。
6.以scheduledTimerWithTimeInterval的方式触发的timer,在滑动页面上的列表时,timer会暂停,为什么?该如何解决?
原因在于滑动时当前线程的runloop切换了model用于列表滑动,导致timer暂停。
runloop中的model主要用来指定事件在runloop中的优先级,有以下几种:
- Default(NSDefaultRunLoopMode):默认,一般情况下使用;
- Connection(NSConnectionReplyMode):一般系统用来处理NSConnection相关事件,开发者一般用不到;
- Modal(NSModalPanelRunLoopMode):处理modal panels事件;
- Event Tracking (NSEventTrackingRunLoopMode):用于处理拖拽和用户交互的模式;
- Common (NSRunloopCommonModes):模式合集。默认包括Default、Modal、Event Tracking三大模式,可以处理几乎所有事件。
回到题中的情境。滑动列表时,runloop的model由原来的Default模式切换到了Event Tracking模式,timer原来好好的运行在Default模式中,被关闭后自然就停止工作了。
解决方法其一是将timer加入到NSRunloopCommonModes中。其二是将timer放到另一个线程中然后开启另一个线程的runloop,这样可以保证与主线程互不干扰,而现在主线程正在处理页面滑动。示例代码如下:
//方法1
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
//方法2
dispath_async(dispath_get_global_queue(0,0),^{
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(repeat:) userInfo:nil repeats:true];
[[NSRunLoop currentRunLoop] run];
});
7.说一下ios触摸事件是怎么传递并响应的(响应者链 responder chain)
事件传递链
假设用户触摸了上图的 View E 区域,那么 ios 将会按下面的顺序
反复检测 subview 来寻找 Hit-Test View
- 触摸区域在视图 A 内,所以检测视图 A 的 subview B 和 C ;
- 触摸区域不在视图 B 内 ,但是在视图 C 内,所以检查视图 C 的subview D 和 E;
- 触摸区域不在视图 D 内,在视图 E 中;
视图 E 在整个视图体系中是 lowest view,所以视图 E 就是 Hit-Test View(能够响应事件的那个最合适的view)。
总结:事件的传递链由系统向离用户最近的view传递(父视图向子视图传递)。UIKit -> Application event queue -> window -> root view -> ... -> lowest view
事件响应链
- 响应者链通常是由 initial view 开始;
- UIView 的 nextResponder 是它的 superview ;如果 UIView 已经是其所在的UIViewController 的top view,那么 UIView 的 nextResponder 就是 UIViewController;
- UIViewController 如果有 Super ViewController,那么它的 nextResponder 为其 Super ViewController 最表层的 view;如果没有,那么它的 nextResponder 就是 UIWindow;
- UIWindow 的 contentView 指向 UIApplication,将其作为 nextResponder;
- UIApplication 是一个响应者链的终点,它的 nextResponder 指向 nil,整个响应链结束。
总结:响应链由离用户最近的view向系统传递(事件传递链的反方向)。initial view -> super view -> ... view controller -> window -> Application
网友评论