4.内存管理
4.1 手动管理
- 自己生成的对象自己持有,可以调用release方法减少retain数量。
- 非自己生成的对象也可以持有,通过调用retain方法可以持有对象,引用数+1。
- 不再需要自己持有的对象要及时释放,注意类中的property要在- (void)dealloc方法中赋值nil,这样写相当于release了。
| 1 | - (void)dealloc | 
- 无法释放非自己持有的对象,注意当一个变量持有一次对象后,只能释放一次。也就是说retainCount必须+1和-1对称。 
- alloc的实现。其实就是调用calloc方法申请内存和C语言的差不多,只不过对象的头部位有个地址用于存储引用数。而retain、release就是对引用数加减,dealloc则是free掉对象。 
4.2 Autorelease
- autorelease这个玩意本质上是将对象加入到最近的一个NSAutoreleasePool中,当NSAutoreleasePool销毁时会将对象release。因此这里就有个坑了,如果这个pool很久都不销毁,里面的对象就始终存在,有可能会造成内存不足。
| 1 | NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; | 
- 注意到main.m中有这样的代码,在最外层就有个autoreleasepool了。
| 1 | int main(int argc, char * argv[]) { | 
4.3 ARC
- 我的理解 - 所谓ARC是通过编译器和运行时的协作来实现自动管理引用计数,编译器在ARC有效的代码中加入额外的代码来加减引用计数。 
- 标识 - __strong:默认就是
- __weak:需显式使用
- __autoreleasing:这个标识一些情况下是不需要显式使用,一些情况下是不需要的,最为致命。
- __unsafe_unretained:个人感觉已经被弃用了,在没有weak的时代使用的东西。
 
- __strong - 这个修饰符具有持有对象的功能,与retain类似。它的使用分为如下几种情况: 
 1、- id __strong obj = [[NSObject alloc] init];这行代码可以理解为如下代码,可见所谓自动管理,就是在编译出来的代码中对强引用变量调用release方法。- 1 
 2
 3
 4- > id obj = objc_msgSend(NSObject, @selector(alloc)); 
 objc_msgSend(obj, @selector(init));
 objc_release(obj);
 >
2、
id __strong obj = [NSMutableArray array];这行代码可以理解为以下代码,很有意思的代码。objc_retainAutoreleasedReturnValue是持有(retain)了一个在autoreleasepool中的对象,而这个对象就是array方法的返回值。
2
3
4
objc_retainAutoreleasedReturnValue(obj);
objc_release(obj); // 离开作用域后自动释放
>
与
objc_retainAutoreleasedReturnValue成对出现的是这个方法objc_autoreleasedReturnValue方法,对于NSMutableArray类的array方法可能是这样实现的
2
3
4
5
{
return [[NSMutableArray alloc] init];
}
>
它可以理解为如下代码
2
3
4
5
6
7
{
id obj = objc_msgSend(NSMutableArray, @selector(alloc));
objc_msgSend(obj, @selector(init));
return objc_autoreleasedReturnValue(obj);
}
>
对于外界调用这个方法赋值的变量来说,只是在使用一个autoreleasepool中的对象。在结合
objc_retainAutoreleasedReturnValue使用时,其实生成的对象并没有进入autoreleasepool,而是直接传递给了使用objc_retainAutoreleasedReturnValue方法赋值的变量。
- __weak - 1、被__weak修饰的变量的地址会被放入到weak表中,这个表是个k-v形式的,key是对象的地址,value是所有引用了这个对象的变量的地址。 - 2、一个对象被释放的过程是个复杂的过程, - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11- > objc_release -> dealloc(如果引用计数为0) -> _objc_rootDealloc 
 > -> object_dispose -> objc_destructInstance
 > -> objc_clear_deallocating
 > {
 > 1、用对象地址找到weak表中的value
 > 2、所有变量(weak表中记录了地址)赋值nil
 > 3、从weak表删除记录
 > 4、从引用计数表删除废弃对象的地址为键值的记录
 > }
 
 >- 从以上的过程可以看出当对象被销毁后所有引用它的__weak变量都会被赋值为nil,这个过程是比较消耗CPU的,少用。 - 3、使用被__weak修饰的变量就是使用注册到autoreleasepool中的对象,从以下代码来进行理解这句话, - 1 
 2
 3- id __weak obj1 = obj; 
 NSLog(@"%@", obj1);
 >- 这句话会大致被编译器翻译成这样 - 1 
 2
 3
 4
 5
 6
 7- id obj; 
 objc_initWeak(&obj1, obj);
 id tmp = objc_loadWeakRetaind(&obj1);
 objc_autorelease(tmp);
 NSLog(@"%@", tmp);
 objc_destroyWeak(&obj1);
 >
>可以看到为了能够NSLog执行时obj1引用的对象不被销毁,需要将它赋值给一个strong(默认)修饰的临时变量,而这个临时变量需要放到autoreleasepool中,因此存在一个问题,当你多次在一个作用域中多次使用weak修饰的变量,会导致很多临时变量产生而且会放到autoreleasepool中,作用域结束后autoreleasepool有很多工作要做。所以少用weak,一般就是避免循环引用。- __autoreleasing,核心就是把修饰的变量放入到autoreleasepool中,没啥多说的。 
- 注意事项: - 不能使用retain、release、retainCount、autorelease这样方法
- 不能使用NSAllocateObject和NSDeallocateObject方法,实际上我根本没用过。
- 需要在函数命名时遵守规则,比如alloc\new\copy\mutableCopy必须给与调用者对象持有权限。
- 不能使用NSAutoreleasePool,可以用@autoreleasepool替换。
- dealloc方法不能显示调用,很明显的例子就是在MRC中写dealloc方法时一定要调用super的dealloc方法,但是在ARC中不行了,不过notificationCenter的删除等处理还是要写在dealloc方法中的,会自动调用。