iOS开发ARC下内存管理
2015-06-23 17:25
477 查看
ARC的修饰符
ARC主要提供了4种修饰符,他们分别是:__strong,__weak,__autoreleasing,__unsafe_unretained。1、__strong
表示引用为强引用。对应在定义property时的"strong"。所有对象只有当没有任何一个强引用指向时,才会被释放。注意:如果在声明引用时不加修饰符,那么引用将默认是强引用。当需要释放强引用指向的对象时,需要将强引用置nil。
2、__weak
表示引用为弱引用。对应在定义property时用的"weak"。弱引用不会影响对象的释放,即只要对象没有任何强引用指向,不管有多少个弱引用对象指向也没用,该对象依然会被释放。对象在被释放的同时,指向它的弱引用会自动被置nil。这样有效得防止无效指针、野指针的产生。__weak一般用在delegate关系中防止循环引用或者用来修饰指向由InterfaceBuilder编辑与生成的UI控件。
3、__autoreleasing
表示在autorelease pool中自动释放对象的引用。定义property时不能使用这个修饰符,任何一个对象的property都不应该是autorelease型的。一个常见的误解是,在ARC中没有autorelease,因为这样一个“自动释放”看起来好像有点多余。这个误解可能源自于将ARC的“自动”和autorelease“自动”的混淆。_autoreleasing在ARC中主要用在参数传递返回值和引用传递参数的情况下。比如常用的NSError的使用:
NSError *__autoreleasing error; if (![data writeToFile:filename options:NSDataWritingAtomic error:&error]) { NSLog(@"Error: %@", error); }
注意,如果你的error定义为了strong型,那么,编译器会帮你隐式地做如下事情,保证最终传入函数的参数依然是个__autoreleasing类型的引用。
NSError *error; NSError *__autoreleasing tempError = error; // 编译器添加 if (![data writeToFile:filename options:NSDataWritingAtomic error:&tempError]) { error = tempError; // 编译器添加 NSLog(@"Error: %@", error); }
所以为了提高效率,避免这种情况,我们一般在定义error的时候将其声明为__autoreleasing类型的:
NSError *__autoreleasing error;等待 if() 函数结束后,error会自动释放,函数外error的使用者并不需要关心*error指向对象的释放。
注意,在Block 中,error会被释放掉。
为了能够正常的使用*error,我们需要一个strong型的临时引用,在dict的枚举Block中是用这个临时引用,保证引用指向的对象不会在出了dict的枚举Block后被释放,正确的方式如下:
- (void)loopThroughDictionary:(NSDictionary *)dict error:(NSError **)error { __block NSError* tempError; // 加__block保证可以在Block内被修改 [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { if (there is some error) { *tempError = [NSError errorWithDomain:@"MyError" code:1 userInfo:nil]; }  }] if (error != nil) { *error = tempError; }  }
4、__unsafe_unretained
ARC是在iOS 5引入的,而这个修饰符主要是为了在ARC刚发布时兼容iOS 4以及版本更低的设备,因为这些版本的设备没有weak pointer system,简单的理解这个系统就是我们上面讲weak时提到的,能够在weak引用指向对象被释放后,把引用值自动设为nil的系统。这个修饰符在定义property时对应的是"unsafe_unretained",实际可以将它理解为MRC时代的assign:纯粹只是将引用指向对象,没有任何额外的操作,在指向对象被释放时依然原原本本地指向原来被释放的对象(所在的内存区域),所以非常不安全。现在可以完全忽略掉这个修饰符了,因为iOS 4早已退出历史舞台很多年。
5、正确的使用修饰符
要定义一个weak型的NSString引用,它的写法应该是:NSString * __weak str = @"hehe"; // 正确!
而不应该是:
__weak NSString *str = @"hehe"; // 错误!
这是因为考虑到很多人会用错,所以在编译器这边贴心地帮我们忽略并处理掉了这个错误:)虽然不报错,但是我们还是应该按照正确的方式去使用这些修饰符。
6、栈中指针默认值为nil
无论是被strong,weak还是autoreleasing修饰,声明在栈中的指针默认值都会是nil。所有这类型的指针不用再初始化的时候置nil了。虽然好习惯是最重要的,但是这个特性更加降低了“野指针”出现的可能性。在ARC中,以下代码会输出null而不是crash:
- (void)myMethod { NSString *name; NSLog(@"name: %@", name); }
ARC与Block
在MRC时代,Block会隐式地对进入其作用域内的对象(或者说被Block捕获的指针指向的对象)加retain,来确保Block使用到该对象时,能够正确的访问。这件事情在下面代码展示的情况中要更加额外小心。
MyViewController *myController = [[MyViewController alloc] init]; // 隐式地调用[myController retain];造成循环引用 myController.completionHandler = ^(NSInteger result) { [myController dismissViewControllerAnimated:YES completion:nil]; }; [self presentViewController:myController animated:YES completion:^{ [myController release]; // 注意,这里调用[myController release];是在MRC中的一个常规写法,并不能解决上面循环引用的问题 }];
这时,编译器会及时地给我们一个警告,提醒我们可能会发生这类型的问题:
对这种情况,我们一般用如下方法解决:给要进入Block的指针加一个__block修饰符。
这个__block在MRC时代有两个作用:
说明变量可改
说明指针指向的对象不做这个隐式的retain操作
一个变量如果不加__block,是不能在Block里面修改的,不过这里有一个例外:static的变量和全局变量不需要加__block就可以在Block中修改,局部变量需要加__block。
使用这种方法,我们对代码做出修改,解决了循环引用的问题:
__block MyViewController * myController = [[MyViewController alloc] init]; myController.completionHandler = ^(NSInteger result) { [myController dismissViewControllerAnimated:YES completion:nil]; myController = nil; // 注意这里,保证了block结束myController强引用的解除 };
参考文献:http://www.cnblogs.com/flyFreeZn/p/4264220.html
相关文章推荐
- iOS之NSJSONSerialization对json解析
- ios view截图
- iOS view模糊背景
- iOS CAGradientLayer 颜色渐变
- 消消乐无限道具BUG
- ios修改textField的placeholder的字体颜色、大小
- iOS 8 毛玻璃效果
- iOS TableView实现QQ好友列表(三)
- 支付宝 iOS SDK 官方下载页面
- iOS开发-编译出错 duplicate symbols for architecture x86_64
- IOS开发——获取本地音频文件(属性/信息)
- iOS音乐播放器小技巧
- nios ii 中双向pio的设置与读写函数
- iOS多线程GCD
- iOS 判断文件或文件夹大小(MB)
- iOS开发:创建真机调试证书
- iOS实现多行输入
- iOS开发篇——应用生命周期
- iOS 简单代理(delegate)实现
- 在真机调试 iOS 应用:理解 Certificates, Identifiers & Profiles