关于属性这些关键字的文章,相信大家已经看过不少了,不过都是千篇一律的东西,并且其中有很多的错误。那么今天我们就来讨论讨论。
首先我们还是来介绍一下这些关键字的作用吧。
我认为:weak,strong,copy,assign,unsafe_unretained这些关键字其实就是这个属性的set语义。
weak:既不释放旧值,也不保留新值。当它指向的内容被释放的时候,它本身也会被置空。好处就是可以防止野指针访问。
weak的实现原理:系统通过一个CFMutableDictionary的hash表来管理weak指针。一个对象可能有多个weak指针指向,当这个对象被释放的时候,就会从hash表中查找所有指向这个对象的weak指针,并把它们都置为nil。
strong:释放旧值,并保留新值。它会对指向的内容进行引用计数+1的操作。
copy:释放旧值,并拷贝新值。它也会对指向的内容进行引用计数+1的操作。
assign:就是一个简单的赋值操作。它和weak很像,不会增加引用计数,但是当它指向的内容被释放的时候,它不会被置空,所以它还指向那个内容,所以容易造成野指针访问。一般用来修饰基本数据类型。当然指向对象也可以的。
unsafe_unretained:可以理解为MRC下的weak。和assign类似,同样不会自动置空,只能用来修饰对象类型。容易造成野指针访问。
首先声明一下,他对于这些关键字的解释是没有问题的,但是举的例子有问题。
po一下网址:http://www.cnblogs.com/langtianya/p/3691035.html
这是我随便百度出来的。
PS:想要有深入的理解,必须掌握好内存的相关知识。
首先,我们按照他说的,写个代码,为了方便,我把地址也打印出来:
@interface ViewController ()
@property (nonatomic, strong) NSString *string1;
@property (nonatomic, weak) NSString *string2;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.string1 = @"hahaha";
self.string2 = self.string1;
NSLog(@"Str1 point to Address: %p", self.string1);
NSLog(@"Str2 point to Address: %p", self.string2);
self.string1 = nil;
NSLog(@"String 1 = %@", self.string1);
NSLog(@"String 2 = %@", self.string2);
NSLog(@"Str1 point to Address: %p", self.string1);
NSLog(@"Str2 point to Address: %p", self.string2);
}
@end

这是网站上说的,结果是空???你信么?是不是大家都觉得结果肯定是空?我运行一下。。。
嘿~有意思了吧~并不是空!!!而且这里你改成什么都是一样的,比如,改成如下的:
self.string1 = @"hahaha";
self.string2 = self.string1;
NSLog(@"Str1 point to Address: %p", self.string1);
NSLog(@"Str2 point to Address: %p", self.string2);
self.string1 = @"hehehe";
NSLog(@"String 1 = %@", self.string1);
NSLog(@"String 2 = %@", self.string2);
NSLog(@"Str1 point to Address: %p", self.string1);
NSLog(@"Str2 point to Address: %p", self.string2);
结果毫无疑问,还是String 2 = hahaha
为什么?学过MRC的同学肯定知道这是怎么回事了。其实很好解释,一开始,string1和string2都指向同一个地址,但之后,string1已经不再指向这个地址了,而是指向了其他地址。也就是说,一开始string1指向的那个地址对应的值并没有改变,而string2正是指向的这个地址。
举个例子,string1牵着一只狗(self.string1 = @”hahaha”),此时string2也牵着这只狗(self.string2 = self.string1),现在这只狗有两个绳子牵着。
情景1:突然,string1的绳子断了(self.string1 = nil),但是string2的绳子并没有断啊。
情景2:突然string1不喜欢这只狗了,去牵了另一只狗(self.string1 = @”hehehe”),但是此时string2还是牵着以前的那只。
这么说就很好理解了吧。关键就是这一句:self.string2 = self.string1
如果换成C语言的基本数据类型,可能会更好理解一些:
int a = ;
int b = a;
a = ;
printf("%d", b);
你们说,输出的b的结果是多少?毫无疑问是10啊!
所以说,上面的self.string2 = self.string1做的就是这样的事情。
到此,可能大家已经明白了。在这个属性中,无论你string2用的是weak/strong/copy/assign,结果都是一模一样的。因为被改变的不是string1指向的内容,而是string1本身的指向。
所以想要真正去测试那些属性的语义,必须有一个正确的方法:要被改变的是内容,而不是指向。
定义一个Person类:
@interface Person : NSObject <NSCopying>
//这里用strong或copy都可以的,原因同上
@property (nonatomic, copy) NSString *name;
@end
实现Copy方法。重写description方法,让它输出name的值
- (NSString *)description {
return self.name;
}
- (id)copyWithZone:(NSZone *)zone {
Person *newPer = [[self class] allocWithZone:zone];
newPer.name = self.name;
return newPer;
}
接下来我们开始测试了~
@interface ViewController ()
@property (nonatomic, copy/strong/weak/assign/unsafe_unretained) Person *per2;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Person *per1 = [[Person alloc] init];
per1.name = @"Tom";
self.per2 = per1;
//情景1
per1.name = @"Jerry";
//情景2
per1.name = nil;
//情景3
per1 = nil;
//情景4
per1 = [[Person alloc] init];
per1.name = @"NewName";
NSLog(@"per1 - %@", per1);
//PS:专门把per2的log写在外面,就是考虑到作用域的问题。也就是说,出了下面这个大括号,per1有可能会被释放。就看用什么关键字来修饰per2了
[self logResult];
}
- (void)logResult {
NSLog(@"per2 - %@", self.per2);
}
@end
上面的情景4在上文已经介绍过了,情景1和情景2都属于改变了指向的内容,情景3和情景4都是改变了自己本身的指向。
对于weak
情景1:两个都是 Jerry
情景2:两个都是 空
情景3:两个都是 空
情景4:per1为 NewName,per2为 空
对于strong
情景1:两个都是 Jerry
情景2:两个值都是 空
情景3:per1为 空,per2为 Tom
情景4:per1为 NewName,per2为 Tom
对于copy
为了实现copy,需要遵守NSCopying协议,实现-copyWithZone方法。
情景1:per1为 Jerry,per2为 Tom
情景2:per1为 空,per2为 Tom
情景3:per1为 空,per2为 Tom
情景4:per1为 NewName,per2为 Tom
对于assign
情景1:两个都是 Jerry
情景2:两个都是 空
情景3:per1为 空,per2无法输出,出现野指针错误。
情景4:per1为 NewName,per2无法输出,出现野指针错误。
对于unsafe_unretained
情景1:两个都是 Jerry
情景2:两个都是 空
情景3:per1为 空,per2无法输出,出现野指针错误。
情景4:per1为 NewName,per2无法输出,出现野指针错误。