网站 文件夹 上传,公司网站建设找谁做,网站运营心得,梅州网站优化目录 ARC规则
概要
所有权修饰符
__strong修饰符
__weak修饰符
__unsafe_unretained修饰符
__autoreleasing修饰符 ARC规则
概要
“引用计数式内存管理”的本质部分在ARC中并没有改变#xff0c;ARC只是自动地帮助我们处理“引用计数”的相关部分。
在编译单位上可以…目录 ARC规则
概要
所有权修饰符
__strong修饰符
__weak修饰符
__unsafe_unretained修饰符
__autoreleasing修饰符 ARC规则
概要
“引用计数式内存管理”的本质部分在ARC中并没有改变ARC只是自动地帮助我们处理“引用计数”的相关部分。
在编译单位上可以设置ARC有效或无效。
所有权修饰符
ARC有效时非基本类型上必须附加所有权修饰符一共有四种 __strong修饰符 __weak修饰符 __unsafe _unretained修饰符 __autoreleasing修饰符
__strong修饰符
__strong修饰符是非基础类型默认的所有权修饰符也就是说非基础类型在没有明确指定所有权修饰符时默认为__strong修饰符。
{id __strong obj [[NSObject alloc] init];
}
此源代码明确指定了变量的作用域ARC无效时该源代码可记述如下
{id obj [[NSObject alloc] init];[obj release];
}
这里增加了调用release方法的代码同之前ARC有效时的动作一样如代码所示附有__strong修饰符的变量obj在超出其变量作用域时会释放其被赋予的对象。__strong修饰符表示对对象的“强引用”持有强引用的变量在超出其作用域时被废弃随着强引用的失效引用的对象会随之释放。
自己生成并持有对象时
{id __strong obj [[NSObject alloc] init];//变量obj为强引用所以自己持有对象
}
//obj超出其作用域强引用失效所以自动释放自己持有的对象。
//对象的所有者不存在因此废弃该对象
取得非自己生成并持有的对象时:
{id __strong obj [NSMutableArray array];//obj为强引用所以自己持有对象
}//因为变量obj超出其作用域强引用失效所以自动释放自己持有的对象
附有__strong修饰符的变量之间可以相互赋值\
id __strong obj0 [[NSObject alloc] init];
id __strong obj1 [[NSObject alloc] init];
id __strong obj2 nil;
obj0 obj1;
obj2 obj0;
obj1 nil;
obj0 nil;
obj2 nil;
下面来看一下生成并持有对象的强引用
id __strong obj0 [[NSObject alloc] init];
//obj0持有对象A的强引用
id __strong obj1 [[NSObject alloc] init];
//obj1持有对象B的强引用
id __strong obj2 nil;
//obj2不持有任何对象
obj0 obj1;
//obj0持有由obj1赋值的对象B的强引用
//obj0被赋值原先持有的对对象A的强引用失效
//对象A的所有者不存在因此废弃对象A
//此时持有对象B的强引用的变量为obj0和obj1。
obj2 obj0;
//obj2持有由obj0赋值的对象B的强引用
//此时持有对象B的强引用的变量为obj0obj1和obj2。
obj1 nil;
//因为nil被赋予了obj1所以对对象的B的强引用失效
//此时持有对象B的强引用的变量为obj0和obj2。
obj0 nil;
//因为nil被赋予obj0所以对对象B的强引用失效
//此时持有对象B的强引用的变量为obj2;
obj2 nil;
//因为nil被赋予obj2所以对对象B地强引用失效
//对象B的所有者不存在因此废弃对象B
可见__strong修饰符的变量不仅只在变量作用域中在赋值上也能够正确地管理其对象的所有者。并且OC类成员变量也可以在方法参数上使用附有__strong修饰符的变量。
interface Test : NSObject
{id __strong obj_;
}
end
implementation Test
- (id)init {self [super init];return self;
}
- (void)setObject:(id __strong)obj {obj_ obj;
}
end;
使用这个类
{id __strong test [[Test alloc] init];[test setObject:[[NSObject alloc] init]];
}
该例中生成并持有对象的状态记录如下
{id __strong test [[Test alloc] init];//test持有对象Test对象的强引用[test setObject:[[NSObject alloc] init]];//Test对象的obj_成员持有NSObject对象的强引用
}//因为test变量超出其作用域强引用失效所以自动释放Test对象。//Test对象的所有者不存在因此废弃该对象
//废弃Test对象时Test对象obj_成员也被废弃NSObject对象的强引用失效自动释放NSObject对象。//NSObject对象的所有者不存在因此废弃该对象
像这样可以直接使用于类成员变量以及方法参数中。另外__strong、__weak、__autoreleasing这三种修饰符可以保证将附有这些修饰符的自动变量初始化为nil。
因为id类型和对象类型的所有权修饰符默认为__strong修饰符所以不需要写上这个修饰符。
__weak修饰符
仅仅通过__strong修饰符进行内存管理会发生“循环引用”的问题如下图所示 比如前面出现的带有__strong修饰符的成员变量在持有对象时很容易发生循环引用。
interface Test : NSObject {id __strong obj_;
}
- (void)setObject:(id __strong)obj;
end
implementation Test
- (id)init {self [super init];return self;
}
- (void)setObject:(id __strong)obj {obj_ obj;
}
end
以下为循环引用 {id test0 [[Test alloc] init];id test1 [[Test alloc] init];[test0 setObject:test1];[test1 setObject:test0];
}
分析一下这里生成并持有对象的状态
{id test0 [[Test alloc] init];//test0持有Test对象A的强引用id test1 [[Test alloc] init];//test1持有Test对象B的强引用[test0 setObject:test1];//Test对象A的obj_成员变量持有Test对象B的强引用//此时持有Test对象B的强引用的变量为Test对象A的obj_和test1[test1 setObject:test0];//Test对象B的obj_成员变量持有Test对象A的强引用//此时持有Test对象A的强引用的变量为Test对象B的obj_和test0
}
//test0变量超出作用域强引用失效所以自动释放Test对象A
//test1变量超出作用域强引用失效所以自动释放Test对象B
//此时持有Test对象A的强引用的变量为Test对象B的obj_
//此时持有Test对象B的强引用的变量为Test对象A的obj_
//发生内存泄漏
由于这里在test0和test1所持有的对象在生命周期结束后还被分别被test1和test0的属性所持有所以对象在超出生命周期后本应被废弃却没有被废弃。
循环引用容易发生内存泄漏。所谓内存泄漏就是说应当废弃的对象在超出其生存周期后继续存在。
像下面这样虽然只有一个对象但在该对象持有自身时也会发生循环引用自引用
id test [[Test alloc] init];
[test setObject:test]; 这时就需要__weak修饰符来避免循环引用。
__weak修饰符与__strong修饰符相反提供弱引用不持有对象实例。
当变量加上__weak修饰符时如果编译以下代码编译器会发生警告。
id __weak obj [[NSObject alloc] init];
此源代码将自己生成并持有的对象赋值给附有__weak修饰符的变量obj变量obj持有对持有对象的弱引用。因此为了不以自己持有的状态来保存自己生成并持有的对象生成的对象会立即被释放。编译器对此会给出警告。但如果像下面这样将对象赋给附有__strong修饰符的变量之后再赋值给附有__weak修饰符的变量就不会发生警告了。 {id __strong obj0 [[NSObject alloc] init];id __weak obj1 obj0;
}
下面分析对象的持有状况
{id __strong obj0 [[NSObject alloc] init];//因为obj0变量为强引用所以自己持有对象id __weak obj1 obj0;//obj1变量持有生成对象的弱引用
}
//因为obj0变量超出作用域强引用失效所以自动释放自己持有的对象
//因为对象所有者不存在所以废弃该对象
带__weak修饰符的变量不持有对象所以在超出其变量作用域时对象即被释放。将先前可能发生循环引用的类成员变量改为附有__weak修饰符的成员变量的话可以避免循环引用现象。
除此之外__weak修饰符还有另一优点就是在持有某对象的弱引用时如果该对象被废弃则此弱引用将自动失效且处于nil被赋值的状态空弱引用。如以下代码所示
id __weak obj1 nil;{id __strong obj0 [[NSObject alloc] init];obj1 obj0;NSLog(A:%,obj1);
}
NSLog(B:%, obj1);
执行结果如下
下面分析一下对象的持有情况 id __weak obj1 nil{id __strong obj0 [[NSObject alloc] init];//obj0变量为强引用所以自己持有对象obj1 obj0;//obj1变量持有对象的弱引用NSLog(A:%, obj1);//输出obj1变量持有的弱引用的对象
}
//因为obj0变量超出作用域强引用失效所以自动释放自己持有的对象。
//对象无持有者所以废弃该对象
//废弃对象的同时持有该对象弱引用的obj1变量的弱引用失效nil赋值给obj1
像这样使用_weak修饰符可避免循环引用。并且可以通过检查附有__weak修饰符的变量是否为nil来判断被赋值的对象是否已废弃。
但是__weak修饰符只能用于iOS5以上及OS X Lion以上版本的应用程序。在iOS4以及OS X Snow Leopard的应用程序中可使用__unsafe_unretained修饰符来代替。
__unsafe_unretained修饰符
__unsafe_unretained修饰符是一个不安全的所有权修饰符。附有__unsafe_unretained修饰符的变量不属于编译器的内存管理对象。将自己生成并持有的对象赋值给附有__unsafe_unretained修饰符的变量中编译器会给出适当的警告。附有__unsafe_unretained修饰符的变量因为自己生成并持有的对象不能继续为自己所有所以生成的对象会立即被释放。在如下代码中__weak修饰符和__unsafe_unretained修饰符就有一些差异了。
id __unsafe_unretained obj1 nil;
{id __strong obj0 [[NSObject alloc] init];obj1 obj0;NSLog(A:%, obj1);
}
NSLog(B:%, obj1);
代码的执行结果如下
来分析一下对象的持有情况 id __unsafe_unretained obj1 nil;
{id __strong obj0 [[NSObject alloc] init];//因为obj变量为强引用所以自己持有对象obj1 obj0;//虽然obj0变量赋值给obj1但是obj1变量既不持有对象的强引用也不持有弱引用NSLog(A: %, obj1);
}//因为obj0变量超出作用域强引用失效所以自动释放自己持有的对象//因为对象无持有者所以废弃该对象
NSLog(B:%, obj1);
//obj1变量表示的对象已经被废弃悬垂指针
//错误访问
也就是说最后一行的NSLog只是碰巧正常运行虽然访问了已经被废弃的对象但程序在个别运行状况下才会崩溃。
在使用__unsafe_unretained修饰符时赋值给附有__strong修饰符的变量时有必要确定被赋值的对象确实存在。
__autoreleasing修饰符
ARC有效时不能使用autorelease方法也不能使用NSAutoreleasePool类。虽然不能直接使用autorelease但实际上ARC有效时autorelease功能是起作用的。
ARC无效时
NSAutoreleasePool *pool [[NSAutoreleasePoll alloc] init];
id obj [[NSObject alloc] init];
[obj autorelease];
[pool drain];
ARC有效时
autoreleasepool {id __autoreleasing obj [[NSObject alloc] init];
}
指定autoreleasepool块来替代“NSAutoreleasePool类对象生成、持有以及废弃“这一范围。ARC有效时要通过将对象赋值给附加了__autoreleasing修饰符的变量来替代调用autorelease方法。对象赋值给附有__autoreleasing修饰符的变量等价于在ARC无效时调用对象的autorelease方法即对象被注册到autoreleasepool。 一般不显式地附加__autoreleasing修饰符。
在使用alloc/new/copy/mutableCopy以外的方法来取得对象时编译器会检查方法名是否已alloc/new/copy/mutableCopy开始如果不是则自动将返回值的对象注册到autoreleasepool。另外init方法返回值的对象不注册到autoreleasepool。
autoreleasepool {id __strong obj [NSMutableArray array];
}
分析一下对象的所有状况
autoreleasepool {//取得非自己生成并持有的对象id __strong obj [NSMutableArray array];//因为变量obj为强引用所以自己持有对象//该对象由编译器判断其方法名后自动注册到autoreleasepool
}//因为变量obj超出其作用域强引用失效自动释放自己持有的对象//同时随着autoreleasepool块的结束注册到autoreleasepool中的所有对象被自动释放//对象的所有者不存在对象被废弃
分析一下为什么取得非自己生成并持有对象时不使用__autoreleasing修饰符也能使对象注册到autoreleasepool。以下为源代码示例 (id)array {return [[NSMutableArray alloc] init];
}
也可以写成 (id)array {id obj [[NSMutableArray alloc] init];return obj;
}
这里id obj的所有权修饰符默认为__strong当return是的对象变量超出作用域时该强引用对应的自己持有的对象会被自动释放。但该对象作为函数的返回值编译器会自动将其注册到autoreleasepool。
另外在访问附有__weak修饰符的变量时也必定要访问注册到autoreleasepool的对象。
id __weak obj1 obj0;
NSLog(class %, [obj1 class]);
以下源代码与此相同
id __weak obj1 obj0;
id __autoreleasing tmp obj1;
NSLog(class%, [tmp class]);
为什么访问附有__weak修饰符的变量时必须访问注册到autoreleasepool的对象呢因为__weak修饰符只持有对象的弱引用而在访问引用对象的过程中该对象可能被废弃。如果把要访问的对象注册到autoreleasepool中那么在autoreleasepool块结束之前都能确保该对象存在。因此在使用附有__weak修饰符的变量时就必定要使用注册到autoreleasepool中的对象。
最后id的指针例如id *obj也是非显式地使用__autoreleasing修饰符的例子id *obj即id __autoreleasing *obj。同样对象的指针NSObject **obj便成为了NSObject * __autoreleaseing *obj。
像这样id的指针或对象的指针在没有显式指定时会被附加上__autoreleasing修饰符。
比如为了得到详细的错误信息经常会在方法的参数重传递NSError对象的指针而不是函数返回值。Cocoa框架中大多方法也是用这种方式。如NSString的stringWithContentsOfFile:encoding:error类方法等。使用该方法的代码如下所示
NSError *error nil;
BOOL result [obj performOperationWithError:error];
id指针或对象的指针会默认附加上__autoreleasing修饰符。
作为alloc/new/copy/mutableCopy方法返回值取得的对象是自己生成并持有的其他情况下便是取得非自己生成并持有的对象。因此使用附有__autoreleasing修饰符的变量作为对象取得参数与除alloc/new/copy/mutableCopy外其他方法的返回值取得对象完全一样都会注册到autoreleasepool并取得非自己生成并持有的对象。
比如performOperationWithError方法的源代码应该是下面这样
-- (BOOL)performOperationWithError:(NSError* __autoreleasing *)error {//错误发生*error [[NSError alloc] initwithDomain:MyAppDomain code:errorCode userInfo:nil];return NO;}
因为声明为NSError* __autoreleasing *类型的error作为 *error被赋值所以能够返回注册到autoreleasepool中的对象。
然而下面这样的源代码会产生编译器错误
NSError *error nil;
NSError **pError error;
因为赋值给对象指针时所有权修饰符必须一致。
前面的方法参数中使用了附有__autoreleasing修饰符的对象指针类型然而调用方却使用了附有__strong修饰符的对象指针类型。那为什么该源代码没有警告就顺利通过编译了呢实际上编译器自动将该源代码转化成了下面形式
NSError __strong *error nil;
NSError __autoreleasing *tep error;
BOOL result [obj performOperationWithError:tmp];
error tep;
当然也可以显式地指定方法参数中对象指针类型的所有权修饰符。
- (BOOL) performOperationWithError:(NSError* __strong *)error;
像该源代码的声明一样对象不注册到autoreleasepool也能传递但是为了在使用参数取得对象时贯彻内存管理的思考方式我们要将参数声明为附有__autoreleasing修饰符的对象指针类型。
另外虽然可以非显式地指定__autoreleasing修饰符但在显式地指定__autoreleasing修饰符时必须注意对象变量要为自动变量包括局部变量、函数以及方法参数。
另外ARC无效时可将NSAutoreleasePool对象嵌套使用。
NSAutoreleasePool *pool0 [[NSAutoreleasePool alloc] init];NSAutoreleasePool *pool1 [[NSAutoreleasePool alloc] init];NSAutoreleasePool *pool2 [[NSAutoreleasePool alloc] init];id obj [[NSObject alloc] init];[obj autorelease];[pool2 drain];[pool1 drain];
[pool0 drain];
同样autoreleasepool块也能够嵌套使用
autoreleasepool {autoreleasepool {autoreleasepool {id __autoreleasing obj [[NSObject alloc] init];}}
}