你真的理解屬性修飾詞嗎?

今天又開(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)原理:(啪啪打臉了)

1. 實(shí)現(xiàn)weak后,為什么對(duì)象釋放后會(huì)自動(dòng)為nil?
2. 當(dāng)weak引用指向的對(duì)象被釋放時(shí),又是如何去處理weak指針的呢?
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容