iOS中的限定符
一、变量限定符
ARC为变量提供了四种生命周期限定符
-
__strong
这是默认的限定符,无需显示引入。只要有强引用指向,对象就会长时间驻留在内存中。
-
__weak
这表明引用不会保持被引用对象的存活。当没有强引用指向对象时,弱引用会被置为nil。对象被回收的时候,__weak具有安全性,即指针将自动设置为nil。
-
__unsafe_unretained
与__weak类似,只是当没有强引用指向对象时,不会被置为nil。
-
__autoreleasing
用于由引用使用
id *
传递的消息参数,它预期了autorelease
方法会在传递参数的方法中被调用。
使用这些限定符的基本格式如下:
变量类型 * 限定符类型 变量名
举例说明如下:
/**
创建对象后,引用计数为1,并且对象p1在引用期间不会被回收
*/
Person * __strong p1 = [[Person alloc] init];
/**
创建对象后,引用计数为0,对象会被立即释放,且对象p2被置为nil
*/
Person * __weak p2 = [[Person alloc] init];
/**
创建对象后,引用计数为1,对象会被立即释放,但p3不会被置为nil
*/
Person * __unsafe_unretained p3 = [[Person alloc] init];
/**
创建对象后,引用计数为1,当方法返回时,对象会被立即释放
*/
Person * __autoreleasing p4 = [[Person alloc] init];
二、属性限定符
属性限定符一共有六个:
-
strong
默认限定符,指定了
__strong
关系 -
weak
指定了
__weak
关系 -
assign
这不是新的限定符,但是其含义发生了改变。在ARC之前,
assign
是默认的的持有关系限定符,在启动了ARC之后,assign
表示了__unsafe_unretained
关系 -
copy
暗指了
__strong
关系。此外,它还暗指了setter
中的复制语义的常规行为 -
retain
指定了
__strong
关系 -
unsafe_unretained
指定了
__unsafe_unretained
关系
【注意】 因为assign
和unsafe_unretained
只进行值复制并没有任何实质性的检查,所以他们只应该用于值类型(BOOL
、NSInteger
等),应避免将他们用于引用类型,尤其是指针类型,如(NSString *
、UIView *
等)
限定符在开发中的使用
一、照片模型
创建一个名为YSTPhoto
类,里面title
、url
、comments
等等属性,同时在类.m
文件里实现它的dealloc
方法,来观察对象从创建到最终释放的生命周期过程。
新建
YSTPhoto
类,提供3个属性值
@interface YSTPhoto : NSObject
/**
照片的url地址
*/
@property (nonatomic, strong) NSURL *url;
/**
照片的名字
*/
@property (nonatomic, copy) NSString *title;
/**
照片的评论数
*/
@property (nonatomic, strong) NSArray *comments;
@end
@implementation YSTPhoto
- (void)dealloc{
NSLog(@"YSTPhoto is dealloced!");
}
@end
二、绑定插座变量
在Storyboard
中添加一个标签resultLabel
和四个按钮,然后在ViewController
中对按钮进行事件的绑定,每次触发按钮点击事件后,将相应的结果显示在resultLabel
上。
三、方法实现
在每一个方法中做如下事情:
- 创建一个
YSTPhoto
对象,并打印对象; - 设置对象图片的
title
; - 在
resultLabel
中显示该对象的引用是否为nil。如果不是nil,则显示title
。
实现createStrongPhoto方法
- (IBAction)createStrongPhoto:(id)sender {
//方法一进入的时候打印当前方法名
NSLog(@"%s enter", __PRETTY_FUNCTION__);
//__strong 修饰
YSTPhoto *__strong photo = [[YSTPhoto alloc] init];
NSLog(@"strong photo: %@", photo);
photo.title = @"Strong Photo";
NSMutableString *ms = [[NSMutableString alloc] init];
[ms appendString:(photo == nil? @"Photo is nil" : @"Photo is not nil")];
[ms appendString:@"\n"];
if (photo != nil) {
[ms appendString:photo.title];
}
self.resultLabel.text = ms;
//方法执行结束前打印当前方法名
NSLog(@"%s exit", __PRETTY_FUNCTION__);
}
/**
打印结果如下:
2017-07-04 17:28:13.370 变量限定符的使用[8550:287995] -[ViewController createStrongPhoto:] enter
2017-07-04 17:28:13.370 变量限定符的使用[8550:287995] strong photo: <YSTPhoto: 0x608000220540>
2017-07-04 17:28:13.371 变量限定符的使用[8550:287995] -[ViewController createStrongPhoto:] exit
2017-07-04 17:28:13.371 变量限定符的使用[8550:287995] YSTPhoto is dealloced!
*/
实现createWeakPhoto方法
- (IBAction)createWeakPhoto:(id)sender {
NSLog(@"%s enter", __PRETTY_FUNCTION__);
YSTPhoto *__weak photo = [[YSTPhoto alloc] init];
NSLog(@"weak photo: %@", photo);
photo.title = @"Weak Photo";
NSMutableString *ms = [[NSMutableString alloc] init];
[ms appendString:(photo == nil? @"Photo is nil" : @"Photo is not nil")];
[ms appendString:@"\n"];
if (photo != nil) {
[ms appendString:photo.title];
}
self.resultLabel.text = ms;
NSLog(@"%s exit", __PRETTY_FUNCTION__);
}
/**
打印结果如下:
2017-07-04 17:36:20.692 变量限定符的使用[8690:293630] -[ViewController createWeakPhoto:] enter
2017-07-04 17:36:20.693 变量限定符的使用[8690:293630] YSTPhoto is dealloced!
2017-07-04 17:36:20.693 变量限定符的使用[8690:293630] weak photo: (null)
2017-07-04 17:36:20.694 变量限定符的使用[8690:293630] -[ViewController createWeakPhoto:] exit
*/
实现createStrongToWeakPhoto方法
- (IBAction)createStrongToWeakPhoto:(id)sender {
NSLog(@"%s enter", __PRETTY_FUNCTION__);
YSTPhoto *__strong sphoto = [[YSTPhoto alloc] init];
NSLog(@"Strong Photo: %@", sphoto);
sphoto.title = @"Strong Photo, Assigned to weak";
YSTPhoto *__weak wphoto = sphoto;
NSLog(@"Weak Photo: %@", wphoto);
NSMutableString *ms = [[NSMutableString alloc] init];
[ms appendString:(wphoto == nil? @"Photo is nil" : @"Photo is not nil")];
[ms appendString:@"\n"];
if (wphoto != nil) {
[ms appendString:wphoto.title];
}
self.resultLabel.text = ms;
NSLog(@"%s exit", __PRETTY_FUNCTION__);
}
/**
打印结果如下:
2017-07-04 17:43:49.639 变量限定符的使用[8810:298477] -[ViewController createStrongToWeakPhoto:] enter
2017-07-04 17:43:49.640 变量限定符的使用[8810:298477] Strong Photo: <YSTPhoto: 0x608000031920>
2017-07-04 17:43:49.641 变量限定符的使用[8810:298477] Weak Photo: <YSTPhoto: 0x608000031920>
2017-07-04 17:43:49.641 变量限定符的使用[8810:298477] -[ViewController createStrongToWeakPhoto:] exit
2017-07-04 17:43:49.642 变量限定符的使用[8810:298477] YSTPhoto is dealloced!
*/
实现createUnsafeUnretainedPhoto方法
- (IBAction)createUnsafeUnretainedPhoto:(id)sender {
NSLog(@"%s enter", __PRETTY_FUNCTION__);
YSTPhoto *__unsafe_unretained photo = [[YSTPhoto alloc] init];
NSLog(@"__unsafe_unretained photo: %@", photo);
photo.title = @"__unsafe_unretained Photo";
NSMutableString *ms = [[NSMutableString alloc] init];
[ms appendString:(photo == nil? @"Photo is nil" : @"Photo is not nil")];
[ms appendString:@"\n"];
if (photo != nil) {
[ms appendString:photo.title];
}
self.resultLabel.text = ms;
NSLog(@"%s exit", __PRETTY_FUNCTION__);
}
/**
打印结果如下:
2017-07-04 18:12:05.914 变量限定符的使用[9330:318662] -[ViewController createUnsafeUnretainedPhoto:] enter
2017-07-04 18:12:05.915 变量限定符的使用[9330:318662] YSTPhoto is dealloced!
2017-07-04 18:12:05.915 变量限定符的使用[9330:318662] *** -[YSTPhoto setTitle:]: message sent to deallocated instance 0x60000002bba0
(lldb)
*/
对以上的测试结果进行分析如下:
-
__strong
引用确保了对象在其作用域内不被销毁,对象只会在方法调用完成后被回收; -
__weak
引用对引用计数没有贡献,对象会被立即回收,甚至在其被用于紧邻的下一个语句前; - 在createStrongToWeak方法中,虽然
__weak
引用不会增加引用计数,但是之前创建的__strong
引用确保了对象不会再方法结束前释放; - 在createUnsafeUnretainedPhoto中,对象会被立即释放,但是由于内存还没有被回收,这个引用还可以使用,不会报错;但是,当再次调用这个对象的时候,内存已经重新分配了,这样就导致了非法访问,程序直接崩溃。在打印结果中,
-[YSTPhoto setTitle:]: message sent to deallocated instance 0x60000002bba0
这正是因为内存已经被回收了,所以在调用setter方法的时候,就提示找不到。