OC内存管理有两种模式:
ARC automatic reference counting 自引用用计数
MRC manual reference counting 手动引用计数
在2011年、IOS5之前,OC的开发只支持MRC模式。
也就是只支持手动引用计数。
当时每当一个新的指针引用了一块堆空间(也就是对象)
就必须手动的把此块堆空间内的 retainCount + 1。
具体操作:
Person* p = [Person new];//默认就是1 ,所以这里的p不需要手动操作。
Person* p2 = p;
[p2 retain];//将retainCount的值+1;
当p2指针不使用此堆空间了。要手动把 retainCount 值 - 1
[p2 release];
p不用了,也需要release
[p release];
手动计数器使用规则:
谁用谁retain,谁不用谁release。
关于内存释放的本质:
当一块内存释放的时候,本质上只是给这部分字节打了标签。并没有把字节里的二进制数据全部清成0或者1.
所以,当一块内存说是释放了,但如果没有其他的二进制数据去填充它,那么它的内部数据是一直存在的。
内存释放,数据仍然存在,就会出现一种僵尸对象的问题。
什么是僵尸对象?堆空间已经被标记清空,能被其他数据使用。但此时此刻,新的二进制数据还没有进来。
我们此时用一个指针指向已经标记释放了的堆空间。这个就叫僵尸对象和野指针。
At mrc model:
Person* p = [Person new];
[p release];//指针指向的堆空间,在代码级别已经被释放。
p.name = @“内存就没有释放的概念,只是打了可以被其他数据填充的标签”;
NSLog(@“%@”,p.name);//但如果没有其他的数据填充这块空间,所以在某种条件下(没被其他数据填充)数据仍然存在。可以继续访问。
关于dealloc函数释放堆空间。
在手动模式下:
1、可以直接调用dealloc 强制标记当前堆空间直接被释放,而不用去管retainCount是否是0.
2、每次调用release 把 retainCount-1 的同时会判断retainCount的最终值是否是0,是0接着调用dealloc函数标记当前堆空间释放。
如何在手动模式下,避免已经被标记的堆空间被指针引用并使用呢?
因为被标记释放的堆空间,会在任意时间内被其他数据使用。所以这对这个指针来说是不安全的。
有两种解决方式:
1、打开僵尸对象检查机制【一但这块堆空间被标记释放了,那么所有的指针都不能使用这块空间】
2、将你认为可能是指向僵尸堆空间的指针设置成nil
OC的实例函数比较过硬,当传入的第一个参数,也就是self对象为nil时,函数也不会报错。
这在.net里会爆出 【未将对象引用设置到对象实例】 的错误的。
ARC:automatic reference counting 自动引用计数
所有堆空间在新指针引用时的 retainCount+1 在指针不引用时 retainCount-1。
自动维护引用计数,并标记释放当前堆空间。不需要程序员去关心。
补充一点:
当一块堆空间被标记可以删除之后,即使暂时没有新的数据填充这块空间。原来空间的数据也不能【复活】过来重新使用。
eg:
//引用计数1
Person* p = [Person new];
//引用计数jian-1,为0 触发dealloc。标记释放
[p release];//
[p retain];//此块堆空间已经被标记释放,所以对堆内的操作都将无效。数据马上就要被释放了,还操作个毛线。