今天又开始看《Effective Objective-C 2.0》,这已经是第三遍了,每一遍都有不同的收获,半夜睡不着觉,爬起来把读书笔记记录下来。
copy和strong
书中 第6条:理解“属性”这一概念 中写道,
- strong:此特质表明该属性定义了一种“拥有关系”(owning relationship)。为这种属性设置新值时,设置方法会先保留新值,并释放旧值,然后再将新值设置上去。
- copy:此特质所表达的所属关系与strong类似。然而设置方法并不保留新值,而是将其“拷贝”(copy)。当属性类型为NSString *时,经常用此特质来保护其封装特性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例。这个类是NSString的子类,表示一种可以修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以这时候就要拷贝一份“不可变”(immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的”(mutable),就应该在设置新属性值时拷贝一份。
接下来验证一下:
定义copy和strong分别修饰的NSString,
@property (copy, nonatomic) NSString *duplicateString;
@property (strong, nonatomic) NSString *strongString;
然后开始测试
- (void)test {
NSMutableString *originalStr = @"originalStr".mutableCopy;
self.duplicateString = originalStr;
self.strongString = originalStr;
NSLog(@"originalStr地址%p",originalStr);
NSLog(@"self.duplicateString = %@,地址%p",self.duplicateString,self.duplicateString);
NSLog(@"self.strongString = %@,地址%p",self.strongString,self.strongString);
[originalStr appendString:@"appendString"];
NSLog(@"originalStr地址%p",originalStr);
NSLog(@"self.duplicateString = %@,地址%p",self.duplicateString,self.duplicateString);
NSLog(@"self.strongString = %@,地址%p",self.strongString,self.strongString);
}
对应的打印值如下
originalStr地址0x60000024a3e0
self.duplicateString = originalStr,地址0xa0ca1d056815085b
self.strongString = originalStr,地址0x60000024a3e0
originalStr地址0x60000024a3e0
self.duplicateString = originalStr,地址0xa0ca1d056815085b
self.strongString = originalStrappendString,地址0x60000024a3e0
确实是用copy修饰的字符串的内存地址与原字符串不同,并且不随原字符串修改而改变,strong修饰的string内存地址与原字符串相同且随原字符串修改而发生了改变。
点语法 和 _ 访问
来来来,再换一种写法
- (void)test {
NSMutableString *originalStr = @"originalStr".mutableCopy;
_duplicateString = originalStr;
_strongString = originalStr;
NSLog(@"originalStr地址%p",originalStr);
NSLog(@"self.duplicateString = %@,地址%p",_duplicateString,_duplicateString);
NSLog(@"self.strongString = %@,地址%p",_strongString,_strongString);
[originalStr appendString:@"appendString"];
NSLog(@"originalStr地址%p",originalStr);
NSLog(@"self.duplicateString = %@,地址%p",_duplicateString,_duplicateString);
NSLog(@"self.strongString = %@,地址%p",_strongString,_strongString);
}
这次对应的打印值为
originalStr地址0x60000024db00
self.duplicateString = originalStr,地址0x60000024db00
self.strongString = originalStr,地址0x60000024db00
originalStr地址0x60000024db00
self.duplicateString = originalStrappendString,地址0x60000024db00
self.strongString = originalStrappendString,地址0x60000024db00
用copy和strong修饰的string内存地址与原字符串都相同且随原字符串修改而发生了改变,这又是为什么呢?
点语法是“通过属性访问”,而下划线是“直接访问”实例变量,造成上面的结果是:
直接访问实例变量时,不会调用其“设置方法”,这就绕过了为相关属性所定义的“内存管理语义”此时的copy并不会拷贝该属性,只会保留新值并释放旧值。
assign 和 weak
-
assign “设置方法”只会执行针对“纯量类型”(scalar type,例如CGFloat或NSInteger等)的简单赋值操作。
-
weak 此特质表明该属性定义了一种“非拥有关系”(nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质与assign类似,然而在属性所指的对象遭到摧毁时,属性值也会请空(nil out)。
结合上面的strong和copy,直接上代码,在MRC下重写他们的set方法以帮助理解
@property (nonatomic,strong) NSString * name;
- (void)setName:(NSString*)name {
[name retain]; // 保留新值,把传进来的对象引用计数加一
[_name release]; // 释放旧值,把_name以前的对象release一次
_name = name; // 将新值设置上去,把name的对象地址给_name 这时name 和_name共同对象的引用为2
}
@property (nonatomic , copy) NSString * name;
- (void)setName: (NSString*)name {
[_name release]; //释放旧值,把_name以前的对象release一次
_name = [name copy]; // 把name 的对象拷贝一份给_name 这时_name 的引用计数为1 而name的引用计数不变
}
@property (nonatomic , weak) NSString *name;
- (void)setName:(NSString*)name {
_name = name; //不保留新值,也不释放旧值,name和_name 引用计数为1;
}
@property (nonatomic , assign) NSInteger age;
- (void)setAge:(NSInteger)age {
_age = age; //不保留新值,也不释放旧值,age和_age 引用计数为1;
}
以上总结都是特别基础的知识点,但也特别容易被忽略。以上总结如果有错误,希望各位大佬指出来,共同探讨。
担心拖延症发作,之前计划好的文章又不发了,在这儿立个flag,周末介绍一下weak的底层实现原理:(啪啪打脸了)