关于一些 iOS面试问题的解答
1. 什么是 ARC? (ARC 是为了解决什么问题而诞生的?)
ARC 是 Automatic Reference Counting 的缩写, 即自动引用计数. 这是苹果在 iOS5 中引入的内存管理机制. Objective-C 和 Swift 使用 ARC 追踪和管理应用的内存使用. 这一机制使得开发者无需键入 retain
和 release
, 这不仅能够降低程序崩溃和内存泄露的风险, 而且可以减少开发者的工作量, 能够大幅度提升程序的流畅性和可预测性. 但是 ARC 不适用于 Core Foundation 框架中, 仍然需要手动管理内存.
2. 以下 keywords 有什么区别: assign vs weak
, __block vs __weak
assign
和 weak
是用于在声明属性时, 为属性指定内存管理的语义.
assign
用于简单的赋值, 不改变属性的引用计数, 用于 Objective-C 中的 NSInteger
, CGFloat
以及 C 语言中 int
, float
, double
等数据类型.weak
用于对象类型, 由于 weak
同样不改变对象的引用计数且不持有对象实例, 当该对象废弃时, 该弱引用自动失效并且被赋值为 nil
, 所以它可以用于避免两个强引用产生的循环引用导致内存无法释放的问题.__block
和 __weak
之间的却是确实极大的, 不过它们都用于修饰变量.
__block
说明符就为 block 提供了变量的修改权.__weak
与 weak
的区别只在于, 前者用于变量的声明, 而后者用于属性的声明.3. __block
在 ARC 和非 ARC 下含义一样吗?
__block
在 ARC 下捕获的变量会被 block retain, 这样可能导致循环引用, 所以必须要使用弱引用才能解决该问题. 而在非 ARC 下, 可以直接使用 __block
说明符修饰变量, 因为在非 ARC 下, block 不会 retain 捕获的变量.
4. 使用 nonatomic
一定是线程安全的吗?
nonatomic
的内存管理语义是非原子的, 非原子的操作本来就是线程不安全的, 而 atomic
的操作是原子的, 但是并不意味着它是线程安全的, 它会增加正确的几率, 能够更好的避免线程的错误, 但是它仍然是线程不安全的.
当使用 nonatomic
的时候, 属性的 setter 和 getter 操作是非原子的, 所以当多个线程同时对某一属性进行读和写的操作, 属性的最终结果是不能预测的.
当使用 atomic
时, 虽然对属性的读和写是原子的, 但是仍然可能出现线程错误: 当线程 A 进行写操作, 这时其他线程的读或写操作会因为该操作的进行而等待. 当 A 线程的写操作结束后, B 线程进行写操作, 然后当 A 线程进行读操作时, 却获得了在 B 线程中的值, 这就破坏了线程安全, 如果有线程 C 在 A 线程读操作前 release 了该属性, 那么还会导致程序崩溃. 所以仅仅使用 atomic
并不会使得线程安全, 我们还需要为线程添加 lock
来确保线程的安全.
atomic
都不是一定线程安全的, nonatomic
就更不必多说了.
5. 描述一个你遇到过的 retain cycle 例子.
6. + (void)load;
和 + (void)initialize;
有什么用处?
当类对象被引入项目时, runtime 会向每一个类对象发送 load
消息. load
方法还是非常的神奇的, 因为它会在每一个类甚至分类被引入时仅调用一次, 调用的顺序是父类优先于子类, 子类优先于分类. 而且 load
方法不会被类自动继承, 每一个类中的 load
方法都不需要像 viewDidLoad
方法一样调用父类的方法. 由于 load
方法会在类被 import
时调用一次, 而这时往往是改变类的行为的最佳时机. 我在 DKNightVersion 中使用 method swizlling
来修改原有的方法时, 就是在分类 load
中实现的.