1.什么是 ARC?
ARC 是 iOS 5 引入的内存管理新功能 – 自动引用计数 。它的工作原理大致是这样:当我们编译源码时,编译器会分析源码中每个对象的生命周期,然后基于这些对象的生命周期,来添加相应的引用计数操作代码。所以,ARC 是工作在编译期的一种技术方案。
这样的好处是:编译之后,ARC 与非 MRC 代码是没有什么差别的,所以二者可以在源码中共存。实际上,你可以通过编译参数 -fno-objc-arc 来关闭部分源代码的 ARC 特性。由于 ARC 能够深度分析每一个对象的生命周期,它能够做到比 MRC 更加高效。
例如在一个函数中,对一个对象刚开始有一个引用计数 +1 的操作,之后又紧接着有一个 -1 的操作,那么编译器就可以把这两个操作都优化掉。
2 ARC 的核心思想?
自己生成的对象,自己持有
非自己生成的对象,自己可以持有
自己持有的对象不再需要时,需要对其进行释放
非自己持有的对象无法释放
3 ARC 在使用时应该遵循的原则?
不能使用 retain、release、retainCount、autorelease。
不可以使用 NSAllocateObject、NSDeallocateObject。
必须遵守内存管理方法的命名规则。
不需要显示的调用 Dealloc。
使用 @autoreleasePool 来代替 NSAutoreleasePool。
不可以使用区域 NSZone。
对象性变量不可以作为 C 语言的结构体成员。
显示转换 id 和 void*。
4 ARC 在编译时做了哪些工作?
自动调用 保留(retain) 与 释放(release) 的方法
相对于垃圾回收这类内存管理方案,ARC 不会带来运行时的额外开销,所以对于应用的运行效率不会有影响。 ARC 会把能够互相抵消 retain、release、autorelease,操作简化,如果发现在同一个对象上执行了多次保留与释放操作,那么 ARC 有时可以成对的移除这两个操作。
5 ARC 在运行时做了哪些工作?
主要是指 weak 关键字。weak 修饰的变量能够在引用计数为0 时被自动设置成 nil,显然是有运行时逻辑在工作的。关于原因会单独开一个问题
为了保证向后兼容性,ARC 在运行时检测到类函数中的 autorelease 后紧跟其后 retain,此时不直接调用对象的 autorelease 方法,而是改为调用 objc_autoreleaseReturnValue。
objc_autoreleaseReturnValue 会检视当前方法返回之后即将要执行的那段代码,若那段代码要在返回对象上执行 retain 操作,则设置全局数据结构中的一个标志位,而不执行 autorelease 操作,与之相似,如果方法返回了一个自动释放的对象,而调用方法的代码要保留此对象,那么此时不直接执行 retain ,而是改为执行 objc_retainAoutoreleasedReturnValue函数。此函数要检测刚才提到的标志位,若已经置位,则不执行 retain 操作,设置并检测标志位,要比调用 autorelease 和retain更快。
_myPerson = [ECOPerson personWithName:@“Bob”];
ECOPerson * tmp = [ECOPerson personWithName:@“Bob”];
_myPerson = [tmp retain];
6 函数返回一个对象时,会对对象autorelease么?为什么?autorelease是什么时候释放的?
会对对象 autorelease,因为需要在稍后释放对象,从而给调用者留下足够长的时间,使其可以在需要时先保留返回值。此方法可以保证对象在跨越方法调用边界时一定存活。
除非你有自己的自动释放池,否则这个时机就是当前线程,当前事件循环结束时,就是 RunLoop 结束时(observer -> beforeWaiting)。
// 情况一:
@autoreleasepool {
NSObject * obj = [NSObject new];
[obj autorelease];
NSLog(@"%d",[obj retainCount]); //1
}
// 情况二:
NSObject * obj = [NSObject new];
[obj autorelease];
[obj autorelease];
NSLog(@"%d",[obj retainCount]);
NSLog(@"%d",[obj retainCount]);
// 崩溃
// 情况三:
NSObject * obj;
@autoreleasepool {
obj = [NSObject new];
[obj autorelease];
NSLog(@"%d",[obj retainCount]); // 1
}
//crash 出了大括号就已经被销毁了
NSLog(@"%d",[obj retainCount]);
7 为什么已经有了 ARC ,还需要 @autoreleasePool?
提到 OC 的 RC,首先要横向对比一下 Android 的 GC(垃圾回收机制),GC 的内存回收是集中式回收(定期回收),而 RC 的回收是伴随整个运行时的,所以 android 机器有种 时“卡”时“流畅” 的感觉,而 iOS 总体比较均匀,缺乏像 GC 的集中式回收内存的类似机制,所以猜测 Pool的产生也是弥补 RC 的这一不足,在 RC 基础上进行内存优化的一种手段。
8 简要阐述内存相关的关键字?
Strong
Strong 修饰符表示指向并持有该对象,其修饰对象的引用计数会加1。该对象只要引用计数不为0就不会被销毁。当然可以通过将变量强制赋值 nil 来进行销毁。
Weak
weak 修饰符指向但是并不持有该对象,引用计数也不会加1。在 Runtime 中对该属性进行了相关操作,无需处理,可以自动销毁。weak用来修饰对象,多用于避免循环引用的地方。weak 不可以修饰基本数据类型。
assign
assign主要用于修饰基本数据类型,
例如NSInteger,CGFloat,存储在栈中,内存不用程序员管理。assign是可以修饰对象的,但是会出现问题。
copy
copy关键字和 strong类似,copy 多用于修饰有可变类型的不可变对象上 NSString,NSArray,NSDictionary上。
__unsafe_unretain
__unsafe_unretain 类似于 weak ,但是当对象被释放后,指针已然保存着之前的地址,被释放后的地址变为 僵尸对象,访问被释放的地址就会出问题,所以说他是不安全的。
__autoreleasing