内存管理
引用计数式管理的思考方式
1、自己生成的对象,自己持有。
2、非自己生成的对象,自己也能持有。
3、不再需要自己持有的对象时释放。
4、非自己持有的对象无法释放。
苹果的实现大概就是采用散列表来管理引用计数。
《!》CNUstep将引用计数保存在对象占用内存块头部的变量中。
采用内存头部管理引用计数的好处:
1、少量代码即可完成。
2、能够统一管理引用计数用内存块与对象用内存块。
采用计数表管理引用计数的好处:
1、对象用内存块的分配无需考虑内存块头部。
2、引用计数表各记录中存有内存块地址,可以从各个记录追溯到各个对象的内存块。
在使用自动释放池中,尽管大量的数据中都采用了autorelease,只要不能废弃NSAutoreleasePool对象,生成的对象仍得不到释放。从而产生内存不足。典型的例子就是读入大量图像的同时改变其尺寸。
eg:
for(int i = 0 ;i < 图像数 ;i++){
//读入图像
//大量产生autorelease的对象
//由于没有废弃NSAutoreleasePool对象
//导致内存不足
}
解决的办法
for(int i = 0 ;i < 图像数 ;i++){
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//读入图像
//大量产生autorelease的对象
[pool drain];
}
引起循环引用的原因:对象之间的强指针相互指着对象,使得对象之间得不到释放。从而导致内存泄露。
内存泄露产生的原因:应当废弃的对象在其超出其生命周期后还继续存在。(循环引用,自身的强引用)。
’常在使用Foundation框架的时候,无论调用哪一个对象的autorelease实例方法,实际上都是调用NSObject类的autorelease的实例方法。
ARC中并没有改变引用计数式内存管理,ARC的任务就是自动帮我们处理“引用计数”的相关部分。
一个应用程序可以混合ARC有效或无效的二进制形式。
ARC中的修饰符
__strong 强指针修饰符,是id类型和对象类型默认的所有权修饰符。id和对象类型在没有明确指定所有权修饰符的时候默认为它。它修饰的变量,不仅只在变量作用域中,在赋值上也能够正确的管理其对象的所有者。只有强指针指向的对象才不会被回收。
__weak 弱指针修饰符,它可以避免循环引用。弱引用不能持有对象实例。因此弱引用只能指向强指针对象。在持有某对象的弱引用的时候,若该对象被废弃,则此弱引用将自动失效且处于nil被赋值的状态。
__unsafe_unretained 不安全的所有修饰符,它修饰的变量不属于编译器的内存管理对象。它与__weak一样。
id __unsafe_unretained obj1 =nil;
{
id __strong obj0 = [[NSObject alloc]init];
obj1 = obj0
NSLog (@“A:%@”,obj1);
}//废弃对象的同时,持有该对象弱引用的obj1变量的弱引用将会失效,nil会赋值给它
NSLog(@“B:%@”,obj1);
这段代码的最后一行只是碰巧正常运行而已,虽然访问了已经被废弃了得对象,但是应用程序在个别运行状况下才会奔溃。
__autoreleasing 在ARC有效的时候,通过该修饰符修饰变量来代替调用autorelease方法。对象赋值给__autoreleasing修饰符修饰的变量等价于在ARC无效的时候调用autorelease方法,即对象被注册到autoreleasepool中。
装入自动释放池的情况:
编译器会检查方法名是否以alloc\new\copy\mutablecopy开头如果不是则自动将返回值注册到autoreleasepool中。因为在使用alloc\new\copy\mutablecopy以外的方法来获取对象,该对象已经被注册到autoreleasepool中。当对象变量超出其作用域时(return的对象时候),强引用对应的自己持有的对象会被自动释放。如果对象作为函数的返回值时,编译器也会将该对象注册到autoreleasepool中。
在使用__weak修饰符的变量的时候,需要使用到autoreleasepool中的对象。因为__weak修饰符只持有对象的弱引用,在访问弱引用的过程中,该对象有可能被废弃。如果把该对象注册到autoreleasepool中,在autoreleasepool结束之前都能保证该对象的存在。
ARC的规则
1、不能使用retain/release/retainCount/autorelease
2、不能使用NSAllocateObject/NSDeallocateObject
3、需遵守内存管理的方法命名规则,用于对象生成/持有的方法,需遵守以alloc\new\copy\mutablecopy开头。
4、不要显示的调用dealloc方法。无论ARC是否有效,对象被放弃的时候,都会自动调用对象的dealloc方法。
5、使用@autoreleasepool块代替NSAutoreleasePool
6、不能使用区域(不管ARC是否有效,如苹果官方文档所说,现在的运行时系统只是简单地忽略了区域的概念。运行时系统中的内存管理本身已极具效率,使用区域来管理内存反而会引起内存使用率低下以及源代码复杂化等问题。)
7、对象型变量不能作为C语言结构体的成员。(因为C语言的规约上没有方法来管理结构体成员的生命周期)如果必须把对象性变量加入到C语言的结构体中,可以把其强制转换为unsafe_unretained类型|void *类型。
8、显示转换id和void *类型(其二则都一样,只是一个是在oc中,一个是在c中)
在进行转换的时候需要使用到__bridge,只要通过__bridge转换,id和void*就能够相互转换了。如果是单纯的转换的话:
id obj = [[NSObject alloc]init];
void *p = (__bridge void*)obj;
id o = (__bridge id)p;
__bridge转换的两种转换方式:
__bridge_retained转换:(可以使要转换赋值的变量也持有所赋值的对象)
id obj = [[NSObject alloc]init];
void *p = (__bridge_retained void *)obj;
这段代码的效果:obj 和p同时持有该对象。
__bridge_transfer转换:(与__bridge_retained相反,被转换的变量所持有的对象在该变量被赋值给转换目标变量后随之释放。)
void *p = (__bridge_retained void*)[[NSObject alloc]init];
id obj = (__bridge_transfer );
这段代码的效果是p不在持有该对象。
Object-C对象与Core Foundation对象之间的转换
Core Foundation对象主要是使用在用C语言编写的Core Foundation框架中,并使用引用计数的对象。
Object-C对象与Core Foundation对象的区别很小,仅仅只是生成于哪一个框架而已。二者的对象可以在不同的框架中使用。因此只用简单的C语言的转换也能实现转换,并且这种转换不需要额外的CPU资源。
二者之间转换的时候用到得方法
OC —>Core Foundation
CFTypeRef CFBridgingRetain(id x){
return (__bridge_retained CFTypeRef)x;
}
Core Fundation ——>OC
id CFBridgingRelease(CFTypeRef x){
return (__bridge_transfer id)x;
}
eg:将NSMutableArray对象作为Core Foundation对象来处理
CFMutableArrayRef cfObject = NULL;
{
id obj = [[NSMutableArray alloc]init];
cfObject = CFBridgingRetain(obj);
CFShow(cfObject);//显示出里面的内容
printf(“retainCount =%d”,CFGetRetainCount(cfObject));
}
printf(“retainCount =%d”,CFGetRetainCount(cfObject));
CFRelease(cfObject);
}
将Core Foundation对象作为NSMutableArray对象来处理
CFMutableArrayRef cfObject = CFArrayCreateMutable(kCFAllocatorDefault, 0 ,NULL);
printf(“retainCount=%d”,CFGetRetainCount(cfObject));
NSMutableArray obj = CFBridgingRelease(cfObject);
printf(“retainCount = %d”,CFGetRetainCount(obj));
当ARC有效的时候,Object-C类的属性会发生变化
除__unsafe_unretained修饰符以外的__strong/__weak/__autoreleasing修饰符保证其指定的变量初始化为nil,同样__strong/__weak/__autoreleasing修饰的数组也保证其初始化值为nil。
C语言中
声明动态数组的指针
id __strong *array = nil;
因为“id *类型“会被默认为”id __autoreleasing *类型“所以在声明动态数组的指针的时候需要显式的指定为__strong类型。虽然被__strong修饰符修饰的id型变量会被初始化为nil,但是不能保证__strong修饰符修饰的id指针型变量被初始化为nil