iOS高级进阶之ARC
2018-10-28 本文已影响0人
平安喜乐698
目录
由编译器自动管理引用计数。通过对代码的静态分析,在合适的位置添加retain、release。
打开/关闭 ARC环境
项目 | Build Phases | Compile Sources 下文件右方添加flag:
关闭ARC: -fno-objc-arc
打开ARC: -fobjc-arc
ARC修饰符
__strong 强引用(默认)
__weak 弱引用(用来避免循环引用)
__autoreleasing
__unsafe_unretained 已废弃
使用格式
ClassName * qualifier variableName;
例
NSString * __weak str = @"hello"; // 正确!
__weak NSString *str = @"hehe"; // 错误!,但编译器不会报错,会做相应处理。
__autoreleasing
在ARC中主要用在参数传递返回值和引用传递参数。
为了避免编译器做额外处理,即为了提高效率,+ __autoreleasing
例1
NSError *__autoreleasing error;
if (![data writeToFile:filename options:NSDataWritingAtomic error:&error])
{
NSLog(@"Error: %@", error);
}
如果不加__autoreleasing,则编译器会进行如下处理:
NSError *error;
NSError *__autoreleasing tempError = error; // 编译器添加
if (![data writeToFile:filename options:NSDataWritingAtomic error:&tempError])
{
error = tempError; // 编译器添加
NSLog(@"Error: %@", error);
}
例2
- (NSString *)doSomething:(NSNumber **)value
{
// do something
}
如果不加__autoreleasing,则编译器会进行如下处理:
- (NSString *)doSomething:(NSNumber * __autoreleasing *)value
{
// do something
}
例3
某些类的方法会隐式地创建autorelease pool
NSError *__autoreleasing error;
[self loopDictionary:@{@"s":@"s"} error:&error];
- (void)loopDictionary:(NSDictionary *)dict error:(NSError **)error
{
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){
*error=[NSError errorWithDomain:@"s" code:1 userInfo:nil];
}];
NSLog(@"%@",error); // 崩溃,error已经被释放
}
编译器会自动在enumerateKeysAndObjectsUsingBlock中做处理
NSError *__autoreleasing error;
[self loopDictionary:@{@"s":@"s"} error:&error];
- (void)loopDictionary:(NSDictionary *)dict error:(NSError **)error
{
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){
@autoreleasepool{ // 被隐式创建
*error=[NSError errorWithDomain:@"s" code:1 userInfo:nil];
}
}];
NSLog(@"%@",*error); // 崩溃,error已经被释放
}
修正
NSError *__autoreleasing error;
[self loopDictionary:@{@"s":@"s"} error:&error];
- (void)loopDictionary:(NSDictionary *)dict error:(NSError **)error
{
__block NSError *tempError;
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){
@autoreleasepool{ // 被隐式创建
tempError=[NSError errorWithDomain:@"s" code:1 userInfo:nil];
}
}];
*error=tempError;
NSLog(@"%@",*error);
}
以下代码意义相同
NSString *str = [[[NSString alloc] initWithFormat:@"hehe"] autorelease]; // MRC
NSString *__autoreleasing str = [[NSString alloc] initWithFormat:@"hehe"]; // ARC
__unsafe_unretained
现在可以完全忽略掉该修饰符,因为iOS 4已彻底退出历史舞台。
为了在ARC刚发布时(iOS 5引入)兼容iOS 4以及版本更低的设备,因为这些版本的设备没有weak pointer system(在weak引用指向对象被释放后,把引用值自动设为nil的系统)。
可以理解为MRC时代的assign:只是引用该对象,不做任何额外的操作。
在引用的对象被释放时依然原原本本地指向原来被释放的对象(所在的内存区域),所以非常不安全。
block
block会隐式地对进入其作用域内的对象加retain,来确保block使用到该对象时能够正确的访问。
MRC环境下
+ __block :
1、变量可改
2、不做隐式的retain,这样避免了循环引用
ARC环境下
+ __block :
变量可改
避免循环引用,使用__weak。
这里又引出一个新问题,被引用的对象可能提前释放(多线程时),解决:
typeof(self) __weak weakSelf=self;
self.myBlock = ^{
typeof(self) bSelf=weakSelf; // 强引用,block执行完毕则销毁,不会造成循环引用
NSLog(@"%@",bSelf);
};
Core Foundation不支持ARC
__bridge
只是声明类型转变,但是不做内存管理规则的转变
CFStringRef s1 = (__bridge CFStringRef) [[NSString alloc] initWithFormat:@"HelloWorld!"];
ARC->MRC
NSString *str = [[NSString alloc] initWithFormat:@"HelloWorld!"];
// CFStringRef s2 = (__bridge_retained CFStringRef)str;
CFStringRef s2 = (CFStringRef)CFBridgingRetain(str);
//...
CFRelease(s2);
MRC->ARC
CFStringRef result = CFURLCreateStringByReplacingPercentEscapes(, , );
NSString *s = (__bridge_transfer NSString *)result;