今天又開(kāi)始看《Effective Objective-C 2.0》,這已經(jīng)是第三遍了,每一遍都有不同的收獲,半夜睡不著覺(jué),爬起來(lái)把讀書(shū)筆記記錄下來(lái)。
copy和strong
書(shū)中 第6條:理解“屬性”這一概念 中寫(xiě)道,
- strong:此特質(zhì)表明該屬性定義了一種“擁有關(guān)系”(owning relationship)。為這種屬性設(shè)置新值時(shí),設(shè)置方法會(huì)先保留新值,并釋放舊值,然后再將新值設(shè)置上去。
- copy:此特質(zhì)所表達(dá)的所屬關(guān)系與strong類似。然而設(shè)置方法并不保留新值,而是將其“拷貝”(copy)。當(dāng)屬性類型為NSString *時(shí),經(jīng)常用此特質(zhì)來(lái)保護(hù)其封裝特性,因?yàn)閭鬟f給設(shè)置方法的新值有可能指向一個(gè)NSMutableString類的實(shí)例。這個(gè)類是NSString的子類,表示一種可以修改其值的字符串,此時(shí)若是不拷貝字符串,那么設(shè)置完屬性之后,字符串的值就可能會(huì)在對(duì)象不知情的情況下遭人更改。所以這時(shí)候就要拷貝一份“不可變”(immutable)的字符串,確保對(duì)象中的字符串值不會(huì)無(wú)意間變動(dòng)。只要實(shí)現(xiàn)屬性所用的對(duì)象是“可變的”(mutable),就應(yīng)該在設(shè)置新屬性值時(shí)拷貝一份。
接下來(lái)驗(yàn)證一下:
定義copy和strong分別修飾的NSString,
@property (copy, nonatomic) NSString *duplicateString;
@property (strong, nonatomic) NSString *strongString;
然后開(kāi)始測(cè)試
- (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);
}
對(duì)應(yīng)的打印值如下
originalStr地址0x60000024a3e0
self.duplicateString = originalStr,地址0xa0ca1d056815085b
self.strongString = originalStr,地址0x60000024a3e0
originalStr地址0x60000024a3e0
self.duplicateString = originalStr,地址0xa0ca1d056815085b
self.strongString = originalStrappendString,地址0x60000024a3e0
確實(shí)是用copy修飾的字符串的內(nèi)存地址與原字符串不同,并且不隨原字符串修改而改變,strong修飾的string內(nèi)存地址與原字符串相同且隨原字符串修改而發(fā)生了改變。
點(diǎn)語(yǔ)法 和 _ 訪問(wèn)
來(lái)來(lái)來(lái),再換一種寫(xiě)法
- (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);
}
這次對(duì)應(yīng)的打印值為
originalStr地址0x60000024db00
self.duplicateString = originalStr,地址0x60000024db00
self.strongString = originalStr,地址0x60000024db00
originalStr地址0x60000024db00
self.duplicateString = originalStrappendString,地址0x60000024db00
self.strongString = originalStrappendString,地址0x60000024db00
用copy和strong修飾的string內(nèi)存地址與原字符串都相同且隨原字符串修改而發(fā)生了改變,這又是為什么呢?
點(diǎn)語(yǔ)法是“通過(guò)屬性訪問(wèn)”,而下劃線是“直接訪問(wèn)”實(shí)例變量,造成上面的結(jié)果是:
直接訪問(wèn)實(shí)例變量時(shí),不會(huì)調(diào)用其“設(shè)置方法”,這就繞過(guò)了為相關(guān)屬性所定義的“內(nèi)存管理語(yǔ)義”此時(shí)的copy并不會(huì)拷貝該屬性,只會(huì)保留新值并釋放舊值。
assign 和 weak
-
assign “設(shè)置方法”只會(huì)執(zhí)行針對(duì)“純量類型”(scalar type,例如CGFloat或NSInteger等)的簡(jiǎn)單賦值操作。
-
weak 此特質(zhì)表明該屬性定義了一種“非擁有關(guān)系”(nonowning relationship)。為這種屬性設(shè)置新值時(shí),設(shè)置方法既不保留新值,也不釋放舊值。此特質(zhì)與assign類似,然而在屬性所指的對(duì)象遭到摧毀時(shí),屬性值也會(huì)請(qǐng)空(nil out)。
結(jié)合上面的strong和copy,直接上代碼,在MRC下重寫(xiě)他們的set方法以幫助理解
@property (nonatomic,strong) NSString * name;
- (void)setName:(NSString*)name {
[name retain]; // 保留新值,把傳進(jìn)來(lái)的對(duì)象引用計(jì)數(shù)加一
[_name release]; // 釋放舊值,把_name以前的對(duì)象release一次
_name = name; // 將新值設(shè)置上去,把name的對(duì)象地址給_name 這時(shí)name 和_name共同對(duì)象的引用為2
}
@property (nonatomic , copy) NSString * name;
- (void)setName: (NSString*)name {
[_name release]; //釋放舊值,把_name以前的對(duì)象release一次
_name = [name copy]; // 把name 的對(duì)象拷貝一份給_name 這時(shí)_name 的引用計(jì)數(shù)為1 而name的引用計(jì)數(shù)不變
}
@property (nonatomic , weak) NSString *name;
- (void)setName:(NSString*)name {
_name = name; //不保留新值,也不釋放舊值,name和_name 引用計(jì)數(shù)為1;
}
@property (nonatomic , assign) NSInteger age;
- (void)setAge:(NSInteger)age {
_age = age; //不保留新值,也不釋放舊值,age和_age 引用計(jì)數(shù)為1;
}
以上總結(jié)都是特別基礎(chǔ)的知識(shí)點(diǎn),但也特別容易被忽略。以上總結(jié)如果有錯(cuò)誤,希望各位大佬指出來(lái),共同探討。
擔(dān)心拖延癥發(fā)作,之前計(jì)劃好的文章又不發(fā)了,在這兒立個(gè)flag,周末介紹一下weak的底層實(shí)現(xiàn)原理:(啪啪打臉了)