基于OC中内存管理的一些理解
下面是本人的一些理解
- 自己生成的对象,自己所持有
- 非自己生成的对象,自己也能持有
- 不再需要自己持有的对象时释放
- 非自己持有的对象无法释放
对象操作 | OC方法 |
---|---|
生成并持有对象 | alloc/new/copy/mutableCopy |
持有对象 | retain方法 |
释放对象 | release方法 |
废弃对象 | dealloc方法 |
这里解释一下release方法和dealloc方法的区别
release方法主要是在MRC下释放Foundation框架下面创建的对象,在ARC下面是系统自动的释放
dealloc方法是释放UIKit框架创建的UI界面的内存
(小弟的理解就是这样的,如果有错请大佬指点出来)
自己生成的对象,自己所持有
使用以下名称开头的方法名意味着自己生成的对象只有自己持有
- alloc
- new
- copy
- mutableCopy
/*
*自己生成并持有的对象
*/
id obj = [[NSobject alloc]init];
/*
*这样创建的对象引用计数+1
*/
非自己生成的对象,自己也能持有
用上述方法以外取得的对象,即alloc/new/copy/mutableCopy以外的方法获取的对象,因为非自己生成并持有,所以自己不是该对象的持有者,比如NSMutableArray类的array类方法创建的对象
/*
*取得非自己生成并持有的对象
*/
id obj = [NSMutableArray array];
/*
*源码中,NSMutableArray类对象被赋给变量obj,但变量obj自己并没有持有该对象。
*/
[obj retain];//obj对象的引用计数+1,调用retain方法之后就变为自己所持有
不再需要自己持有的对象时释放
/*
*自己创建并持有的方法
*/
id obj = [NSObject new];
/*
*当自己不需要的时候立即释放
*/
[obj release];
非自己持有的对象无法释放
对于自身创建的对象或者持有的对象,在不使用的时候必须释放掉
倘若在程序中释放了非自己所持有的对象程序就会崩溃
id obj = [NSObject new];
[obj release];
/*
*当自身释放了再次释放之后系统就会崩溃
*/
[obj release];
ARC规则
在便以单位上,可设置ARC有效或者无效
一个应用程序可以混合ARC有效或无效的二进制形势
设置ARC有效的编译方式如下所示
- 使用clang(LLVM编译器)3.0或以上版本
- 指定编译器属性为“-fobjc-arc”
当然在Xcode4.2及以上的版本默认设置的对所有的文件ARC有效
当然MRC的内存管理方式对于ARC一样的适用,即: - 自己生成的对象,自己所持有
- 非自己生成的对象,自己也能持有
- 不再需要自己持有的对象时释放
- 非自己持有的对象无法释放
权限修饰符
- __strong
- __weak
- __unsafc_unretained
- __autoreleasing
__strong
__strong修饰符是id类型和对象类型默认的所有权限修饰符。也就是说,以下源代码中的id变量,实际上被附加了所有权限修饰符
id obj = [[NSObject alloc]init];
id和对象类型在没有明确指定所有权限修饰符时,默认__strong修饰符,上面的源代码与以下相同
id __strong obj = [[NSObject alloc]init];
该源代码在ARC无效时的表述
/*ARC无效*/
id obj = [[NSObject alloc]init];
{
/*
*自己生成并且自己所持有
*/
id __strong obj = [[NSObject alloc]init];
}
//此源码明确的指定了c语言的变量的作用域,ARC无效时,该源代码可描述如下:
/*ARC无效*/
{
id obj = [[NSObject alloc]init];
[obj release];
}
/*
*因为变量默认为__strong修饰,当obj超出了其作用域,强引用就会失效
*并且对象所有者不存在,因此废弃该对象
*/
以上是__strong修饰符修饰的自己创建并且自己持有的对象的源码分析
下面简单的说一下非自己生成并持有对象的源码
{
id __strong obj = [NSMutableArray array];
}
//在NSMutableArray类的array类方法的源码中取得非自己生成并持有的对象
{
/*取得非自己生成并持有*/
id __strong obj = [NSMutableArray array];
/*
*因为变量obj是强引用,所以自己持有
*/
}
/*
*变量obj超出了作用域,强引用失效,自己释放自己持有的对象
*/
其实附有__strong修饰符的变量之间可以相互赋值
id __strong obj0 = [[NSObject alloc]init];
id __strong obj1 = [[NSObject alloc]init];
id __strong obj2 = nill;
obj0 = obj1;
obj2 = obj0;
obj0 = nil;
obj1 = nil;
obj2 = nil;
当然,即便是OC类成员变量,也可以在方法参数上,使用附有__strong修饰符的变量
@interface Test : NSObject
{
id __strong obj_;
}
- (void)setObject:(id __strong)obj;
@end
@implementation Test
- (instancetype)init{
self = [super init];
if (self) {
}
return self;
}
- (void)setObject:(id __strong)obj{
obj_ = obj;
}
@end
正如苹果粑粑说的那样,通过__strong修饰符,不必再次键入retain和release,满足了“引用计数式管理的思考方式”
__weak
看起来好像通过__strong修饰符编译器就能完美的进行内存管理,但是遗憾的是,仅通过__strong修饰符是不能解决有些重大的问题
这里所有的重大的问题就是引用计数式内存管理中必然会发生的“循环引用”的问题
例如:前面我们的带有__strong 的例子就容易发生循环引用
@interface Test : NSObject
{
id __strong obj_;
}
- (void)setObject:(id __strong)obj;
@end
@implementation Test
- (instancetype)init{
self = [super init];
if (self) {
}
return self;
}
- (void)setObject:(id __strong)obj{
obj_ = obj;
}
@end
//以下为循环引用
{
id test0 = [Test new];//对象A
id test1 = [Test new];//对象B
[test0 setObject: test1];//对象A的obj_成员变量持有对象B的强引用
[test1 setObject:test0];//对象B的obj_成员变量持有对象A的强引用
}
循环引用很容易造成内存泄漏
上面这种情况是两个相互强引用发生的内存泄漏,下面这种是自身强引用自身发生的内存泄漏
id test = [Test new];
[test setObject: test];
既然出现了这种强引用的内存泄漏,那么苹果粑粑就会解决这种内存泄漏,和__strong对应的修饰符__weak,如:
id __weak obj = [NSObject new];
变量obj上附加__weak修饰符,但是这样编译之后,编译器会发出警告
Assigning retained object to weak variable; object will be released after assignment
很显然,编译器的意思就是很容易造成提前释放,有问题就要解决
id __strong obj0 = [NSObject new];
id __weak obj1 = obj0;
这样赋值就不会出现警告
当然__weak还有一个优点,在持有某个对象的弱引用时,若该对象被废弃,则此弱引用将自动失效且处于nil被赋值的状态,代码如下:
id __weak obj1 = nil;
{
id __strong obj0 = [NSObject new];
obj1 = obj0;
NSLog(@"A:obj1:%@",obj1);
}
NSLog(@"B:obj1:%@",obj1);
此源码执行的结果:
A:obj1:<NSObject: 0x103000db0>
B:obj1:(null)
但是有个注意事项:__weak修饰符只能用于iOS5以上及OS X Lion以上版本的应用,在iOS4以下及OS X Snow Leopard 的应用程序中可使用__unsafe _unretained 来修饰,但是对于现在来说也没有多少人在使用iOS4以下及OS X Snow Leopard版本了
__unsafe _unretained
__unsafe _unretained 和__weak是相同的,这里就不过多的讲解,但是要注意的是__unsafe _unretained是不安全的权限修饰符
__autoreleasing
ARC有效时不能使用autorelease和NSAutoreleasePool类,虽然autorelease无法直接使用,但是在ARC有效时autorelease功能是起作用的
/*ARC无效*/
NSAutoreleasePool *pool = [NSAutoreleasePool new];
id obj = [NSObject new];
[obj autorelease];
[pool drain];
/*ARC有效*/
@autoreleasepool {
id __autoreleasing obj = [NSObject new];
}
指定“@autoreleasepool代码块”来替代“ NSAutoreleasePool”
另外,在ARC有效时,要通过将对象赋值给附加了__autoreleasing修饰符的变量来替代调用autorelease方法,对象赋值给附有__autoreleasing修饰符的变量等价于ARC无效时调用对象的autorelease方法,即对象被注册到autoreleasepool
换一种说话就是在ARC有效时,用@autoreleasepool块替代NSAutoreleasePool类,用附有__autoreleasing修饰符的变量替代autorelease方法,图解: