
+ alloc+ allocWithZone:class_creatInstancecalloc
调用alloc方法首先调用allocWithZone:类方法,然后调用class_creatInstance函数,最后调用calloc来分配内存块。
3.retainCount/retain/release 方法- retainCount__CFDoExternRefOperationCFBasicHashGetCountOfKey
- retain__CFDoExternRefOperationCFBasicHashAddValue
- retainCount__CFDoExternRefOperationCFBasicHashRemoveValue //CFBasicHashRemoveValue 为0时,-release调用dealloc
各个方法都通过同一个__CFDoExternRefOperation函数,调用一系列名称相似的函数。并且从函数名看出苹果采用散列表(引用计数表)来管理引用计数,表键值为内存块地址的散列值。然而GNUStep将引用计数保存在对象占用内存块头部的变量中(objc_layout这个结构体中)。
内存块头部管理引用计数的好处:
少量代码皆可完成 能够统一管理引用计数内存块与对象内存块。引用技术表管理引用计数的好处:
1. 对象内存快的分配无需考虑内存块头部
第二条特征在调试时很重要,即使出现故障导致对象占用的内存块损坏,但只要引用计数表没有被损坏,就能够确认各个内存块的地址4.autorelease方法
NSautoreleasePool是通过以autoreleasePoolPage为结点的双向链表来实现的。autoreleasePoolPage是一个C++实现的类,类结构如图:
在Cocoa框架中,NSRunloop每次循环过程中NSautoreleasePool对象被生成或废弃。在大量产生autorelease对象时,只要不废弃NSautoreleasePool那么生成的对象就不能被释放,在此情况下有时会产生内存不足的现象,因此有必要适当的生成,持有和废弃NSautoreleasePool。通常在使用Objective-C,无论调用哪一个对象的autorelease/retain方法,实现上都是调用NSObject类的autorelease/retain实例方法,但是对于NSautoreleasePool类,autorelease/retain实例方法已被重写,因此运行时会出错(exception)。autorelease实际上把对象的释放时机交给NSautoreleasePool管理,使用方法如下:
生成并持有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)5.ARC规则
ARC(automatic Reference Counting)是编译阶段自动做了retain/release,原先需要手动添加处理引用计数的代码可以自动地由编译器完成,但实际上只有编译器是无法完全胜任的,在此基础上还需要Objective-C运行时库协助。同一程序中按文件单位可以选择ARC有效和无效。Core Foundation中的malloc()或者free()等,还是需要自己手动进行内存管理。ARC规则如下
不能使用retain/release/retainCount/autorelease。 不能使用NSAllocateObject/NSDeallocateObject。 必须遵守内存管理的方法命名规则。 不要显式调用dealloc。 使用@autoreleasepool块代替NSautoreleasePool。 不能使用区域。虽说ARC有效时,不能使用区域(NSZone)。不管ARC是否有效,区域在现在的运行时系统(编译器宏__OBJC2__)中已单纯的被忽略
对象型变量不能作为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转换:只做类型转换,在不改变对象的引用计数,可能会造成悬垂指针而导致程序崩溃。__brIDge_retained转换:可使目标变量也持有赋值对象。对象引用计数加1。以下是ARC和MRC等价的代码
// ARCID 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];6.所有权修饰符
__strong修饰符:对对象实例的强引用,是ID类型或对象类型的默认所有权修饰符。持有强引用的变量在超出其作用域时被废弃,随着强引用的失效,引用的对象
会随之释放。
// 以下两行代码是等效的ID obj = [[NSObject alloc] init];ID __strong obj = [[NSObject alloc] init];__weak修饰符:提供弱引用,弱引用不能持有对象实例,与__strong修饰符相反。可用来解决循环引用造成的内存泄露问与题。 __unsafe_unretained修饰符:不安全的所有权修饰符,尽管ARC式的内存管理属于编译器的工作,但附有__unsafe_unretained修饰符的变量不属于编译器的内存管理对象,既不持有对象的强引用也不持有对象的弱引用,对象的可能随时被释放,访问释放的对象运行时会崩溃。
__autoreleasing修饰符:对象变量要为自动变量(包含局部变量、函数以及方法参数)。利用“__objc_autoreleasePoolPrint()”调试autoreleasepool上的对象
等价于MRC时调用对象的autorelease方法。以下两段代码等效:
// MRCNSautoreleasePool *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]);7.引用计数查看
Apple 提供一些方法查看对象的引用计数,但是并不能完全信任这些函数提供的引用计数值。对于已释放的对象一级不正确的对象地址,有时 也返回”1“,在多线程中,因为存在竞态条件的问题,所以取得的的数值不一定可信。
[object retainCount]; //得到object的引用计数,此方法仅仅适用于MRC _objc_rootRetainCount(object); // ARC和MRC适用,头文件声明:OBJC_EXTERN int _objc_rootRetainCount(ID);总结
以上是内存溢出为你收集整理的iOS内存管理全部内容,希望文章能够帮你解决iOS内存管理所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)