一、通过引用计数管理内存
1、引用计数。通过引用计数管理内存。对象被创建出来时,引用计数至少为1,通过retain使引用计数递增,通过release、autorelease使引用计数递减,引用计数为0时,对象所在的内存为可重用,所有指向该对象的引用都无效。
2、dangling pointer。指向对象的引用计数为0的引用。使用dangling pointer不一定使程序崩溃,崩溃是否,看对象内存是否被覆写。
3、引用树。ios应用程序中的root object为UIApplication对象。
4、属性存取。strong relationship修饰的属性,在setValue时,先保留新值,再释放旧值。这个顺序不能颠倒,一旦颠倒,假如新值和旧值为同一个对象,释放后有可能对象已被回收,再保留就会出现问题。
5、autorease。在return方法内部创建的对象时,由于不能在方法内部释放对象(对象引用计数值有可能为0,对象就被回收),就得通过autoreleasepool使引用计数值在下一次event loop时递减。通过类工厂方法创建的对象放置在autoreleasepool中。
6、retain cycle。导致内存泄漏,解决问题有两种办法,一是weak reference,一是代码控制其中的某个对象不再保留另外一个对象。
7、Clang编译器中的静态分析器(static analyzer)。可以查明内存管理问题,也可以根据需要,预先加入保留与释放操作。
注意:retaincount方法获取引用计数值是不太有用的。对引用计数器做递增或递减操作时,只能说做了递增或递减操作,而绝不能说保留计数一定是某个值。
二、ARC自动引用计数(编译器与运行期组件共同管理内存)
1、实现思路是static analyzer。
2、代码中存在retain、release、autorelease、dealloc时,编译器无法通过。在调用这些方法时,不通过oc的消息派发机制,直接调用底层的c语言函数。
3、ARC命名规则。以alloc、new、copy、mutableCopy开头的方法名,对象归方法调用者持有。详解如下:
/* newPerson方法中,方法调用者持有返回对象。因此在方法内部,除了allo使引用计数+1外,没有其余的保留与释放操作,最后在调用方法后,对象需要增加一次释放操作。*/ + (Person *)newPerson{ Person *person = [[person alloc] init]; return person; } /*createPerson方法中,方法调用者不持有返回对象,因此在return操作时,ARC会增加 [person autorelease] 方法。最后计数器平衡。*/ + (Person *)createPerson{ Person *person = [[person alloc] init]; return person; }
4、ARC有时会成对移除保留、释放操作。
5、ARC包含运行期组件。
如果方法名没遵守命名规则,但返回值赋值给强引用修饰的变量,无疑需要增加retain操作。考虑到backward compatibility性能,createPerson方法的return语句不用[person autorelease]替换,而改用objc_autoreleaseReturnValue(person)替换。
objc_autoreleaseReturnValue方法底层实现。此方法会检视方法返回后,是否要执行retain操作。如果要执行retain操作,则会设置全局数据结构中的一个标志位,并且方法中的return对象不会自动释放。而且在执行retain操作时,[person retain]也会用 objc_retainAutoreleaseReturnValue(person)方法代替,检查到标志位已经置位,则不执行保留操作。
设置标志位和检查标志位是否已置位(编译器执行),比保留和释放快。
6、ARC中局部变量与实例变量修饰符。
strong:保留此值,默认语义;
weak:不保留此值,并且值被回收后,指向nil;
7、ARC中的dealloc方法。
1)、dealloc方法不允许发送release消息,不允许给父类发送dealloc消息。ARC借助Objective-C++的一项特性生成清理例程(Objective-C++特性:回收Objective-C++对象时,待回收对象会调用所有C++对象的析构函数,编译器发现对象中存在C++对象,就会生成.cxx_destruct方法),在.cxx_destruct方法中自动生成代码,包括给父类发送dealloc消息。单例对象不会自动析构。
2)、必要时添加CFRealease(Core Foundation对象)、free(通过malloc()分配的内存)方法、注销通知等等。ARC中的dealloc方法仅释放引用并解除监听。
3)、释放引用不包括file descriptor、socket、大块内存等,通常创建另一个方法来释放这些引用,且另一个方法的调用必须在dealloc执行之前。不在dealloc中释放这些资源的原因:一是通过dealloc释放,资源保留时间过长;二是dealloc方法不一定执行。
4)、执行异步任务的方法不应在dealloc中调用,只能在正常状态下执行的方法也不应在dealloc中调用。
8、ARC中可能出现内存泄露的地方:
1)、出现相互强引用,导致双方都无法释放(retain cycle);
2)、c代码的对象,是不会自己释放的,比如cg(CoreGraphics.frameworks)开头的对象。
3)、GCD对象。