+ alloc + allocWithZone: class_creatInstance calloc
调用alloc方法首先调用allocWithZone:类方法,而后调用class_creatInstance函数,最后调用calloc来分配内存块。安全
- retainCount __CFDoExternRefOperation CFBasicHashGetCountOfKey
- retain __CFDoExternRefOperation CFBasicHashAddValue
- retainCount __CFDoExternRefOperation CFBasicHashRemoveValue //CFBasicHashRemoveValue 为0时,-release调用dealloc
各个方法都经过同一个__CFDoExternRefOperation函数,调用一系列名称类似的函数。而且从函数名看出苹果采用散列表(引用计数表)来管理引用计数,表键值为内存块地址的散列值。然而GNUStep将引用计数保存在对象占用内存块头部的变量中(objc_layout这个结构体中)。多线程
内存块头部管理引用计数的好处:框架
引用技术表管理引用计数的好处:
1. 对象内存快的分配无需考虑内存块头部函数
第二条特征在调试时很重要,即便出现故障致使对象占用的内存块损坏,但只要引用计数表没有被损坏,就可以确认各个内存块的地址
NSAutoreleasePool是经过以AutoreleasePoolPage为结点的双向链表来实现的。AutoreleasePoolPage是一个C++实现的类,类结构如图:oop
在Cocoa框架中,NSRunloop每次循环过程当中NSAutoreleasePool对象被生成或废弃。在大量产生autorelease对象时,只要不废弃NSAutoreleasePool那么生成的对象就不能被释放,在此状况下有时会产生内存不足的现象,所以有必要适当的生成,持有和废弃NSAutoreleasePool。一般在使用Objective-C,不管调用哪个对象的autorelease/retain方法,实现上都是调用NSObject类的autorelease/retain实例方法,可是对于NSAutoreleasePool类,autorelease/retain实例方法已被重写,所以运行时会出错(exception)。autorelease实际上把对象的释放时机交给NSAutoreleasePool管理,使用方法以下:spa
生成并持有NSAutoreleasePool对象。线程
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // 等同于 objc_autoreleasePoolPush()
调用已分配对象的autorelease实例方法。指针
id obj = [[NSObject alloc] init]; [obj autorelease]; // 等同于 objc_autorelease()obj
废弃NSAutoreleasPool对象(自动调用分配对象的release)。调试
[pool drain]; // 等同于 objc_autoreleasePoolPop(pool)
ARC(Automatic Reference Counting)是编译阶段自动作了retain/release,原先须要手动添加处理引用计数的代码能够自动地由编译器完成,但实际上只有编译器是没法彻底胜任的,在此基础上还须要Objective-C运行时库协助。同一程序中按文件单位能够选择ARC有效和无效。Core Foundation中的malloc()或者free()等,仍是须要本身手动进行内存管理。ARC规则以下code
对象型变量不能做为C语言结构体的成员。
struct Data { NSMutableArray *array; /* error: ARC forbids Objective-C objs in structs or unions NSMutableArray *array */ } /* 要把对象型白能量加入到结构体成员中时,可强制转换为void *(见下一条规则)或是附加“__unsafe_unreatained”修饰符。可是附有“__unsafe_unreatained”修饰的变量不属于编译器的内存管理对象,可能形成内存泄露或者崩溃。*/ struct Data { NSMutableArray __unsafe_unreatained *array; / }
显式转换“id” 和 “void”。
在MRC下"id"和"void *"能够强制转换,但在ARC下编译器报错,代码以下:
id obj = [[NSObject alloc] init]; void *p = obj; // ARC编译报错 id o = p; // ARC编译报错 [o release];
ARC状况下要用 "bridge转换",可是ARC不推荐此用法,这种转换常常用在Objective-C和Core Foundation对象之间的相互变换。
__bridge_retained转换:可以使目标变量也持有赋值对象。对象引用计数加1。如下是ARC和MRC等价的代码
// ARC id obj = [[NSObject alloc] init]; void *p = (__bridge_retained void *)obj; // 等效 MRC 代码 id obj = [NSObject alloc] init]; void *p = (void *)obj; [(id)p retain];
__bridge_transfer转换:被转换的变量所持有的对象在赋值给目标变量后随之释放。
// ARC ① (void)(__bridge_transfer id)p; //指定返回结果为void ,不利用返回值 对象引用计数减1 ② id obj = (__bridge_transfer id)p; // 若是返回值赋值给目标变量,转换后对象引用计数不变 // 等效 MRC 代码 ① [p release]; ② id obj = (id)p; [obj retain];[(id)p release];
__strong修饰符:对对象实例的强引用,是id类型或对象类型的默认全部权修饰符。持有强引用的变量在超出其做用域时被废弃,随着强引用的失效,引用的对象
会随之释放。
// 如下两行代码是等效的 id obj = [[NSObject alloc] init]; id __strong obj = [[NSObject alloc] init];
__autoreleasing修饰符:对象变量要为自动变量(包含局部变量、函数以及方法参数)。利用“__objc_autoreleasePoolPrint()”调试autoreleasepool上的对象
等价于MRC时调用对象的autorelease方法。如下两段代码等效:
// MRC NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; id obj = [[NSObject alloc] init]; [obj autorelease]; [pool drain]; // ARC @autoreleasepool { id __autoreleasing obj = [[NSObject alloc] init]; }
显式的附加__autoreleasing修饰符和显式度附加__strong修饰符同样“罕见”,有些状况下非
显式的声明__autoreleasing修饰符也是能够的,这些状况会自动将对象注册到自动释放池。
1.非本身生成并持有的对象
@autoreleasepool { id obj = [NSMutableArray array]; // 变量obj为对象的强引用(默认__strong),编译器判断方法名后(非alloc/new等)自动注册大autoreleasepool }
2.id的指针和对象的指针(二级指针)在没有显示的指定时会被附加上__autoreleasing修饰符。
NSError *error = nil; //全部权为__strong类型 NSError **pError = &error; //编译出错, NSError * (对象)默认为_autoreleasing类型,赋值先后全部权不匹配。 NSError * __strong *pError = &error; //编译正确
3.本身生成并持有的对象做为返回值
+ (id)array { id obj = [[NSMutableArray alloc] init]; return obj; }
4.虽然__weak修饰符是为了不循环引用而使用的,但访问附有__weak修饰符的变量时实j 际上一定要访问注册到autoreleasepool的对象为了不该对象被废弃,注册到autoreleasepool以后能够保证在autoreleasepool块结束以前对象存在。
id __weak obj1 = obj0; NSLog(@"class = %@",[obj1 class]); // 如下代码和以上代码相同 id __weak obj1 = obj0; id __autoreleasing tmp = obj1; NSLog(@"class = %@",[tmp class]);
Apple 提供一些方法查看对象的引用计数,可是并不能彻底信任这些函数提供的引用计数值。对于已释放的对象一级不正确的对象地址,有时 也返回”1“,在多线程中,由于存在竞态条件的问题,因此取得的的数值不必定可信。
[object retainCount]; //获得object的引用计数,此方法仅仅适用于MRC _objc_rootRetainCount(object); // ARC和MRC适用,头文件声明:OBJC_EXTERN int _objc_rootRetainCount(id);