談談你對屬性的掌握程度,比如 _ 與 self. 、copy與strong、“=”賦值與setArray的區別

本篇文章的完善版本:對屬性變量賦新值時可能引發的血案

今天review同事的代碼,發現一個bug,如下代碼:
_dataArr = newArr;
其中_dataArr是屬性變量,newArr是局部變量,于是問了如下的幾個問題:
1.掉用屬性使用 __ 與 self. 的區別(不是會調用get方法這么簡單)。
2.修飾數據對象屬性時使用copy與strong的區別。(不是深拷貝與淺拷貝這么簡單)
3.給屬性(比如可變數組)賦值時采用“=”賦值與setArray的區別。

提出后發現對該部分基礎掌握不是很熟悉,所以決定分享下對于初級開發時常常疑惑而出錯的這些地方,于是寫了如下這個測試demo分別演示以上問題所導致的結果,注意觀察局部變量的變化給屬性變量的數據與地址在各種情況下的影響。


#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, strong) NSMutableArray *mArrStrong;
@property (nonatomic, copy)   NSMutableArray *mArrCopy;

@property (nonatomic, strong) NSMutableArray *mArrStrong2;
@property (nonatomic, copy)   NSMutableArray *mArrCopy2;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //_mArrStrong = [NSMutableArray array];
    //_mArrCopy = [NSMutableArray array];
    _mArrStrong2 = [NSMutableArray array];
    _mArrCopy2 = [NSMutableArray array];
    [self test1];
    [self test2];
}

- (void)test1
{
    NSLog(@"%s", __func__);
    
    NSMutableArray *arr1 = [NSMutableArray arrayWithObjects:@"1", nil];
    _mArrStrong = arr1;
    _mArrCopy = arr1;
    [arr1 addObject:@"2"];
    NSLog(@"%p", arr1);
    NSLog(@"a:%p %@", _mArrStrong, _mArrStrong);
    NSLog(@"b:%p %@", _mArrCopy, _mArrCopy);
    arr1 = (NSMutableArray *)@[@"000"];
    NSLog(@"額外測試:arr1:%p _mArrStrong:%p _mArrCopy:%p %@ \n%@", arr1, _mArrStrong, _mArrCopy, _mArrStrong, _mArrCopy);
    
    NSMutableArray *arr2 = [NSMutableArray arrayWithObjects:@"1", nil];
    self.mArrStrong = arr2;
    self.mArrCopy = arr2;
    [arr2 addObject:@"2"];
    NSLog(@"%p", arr2);
    NSLog(@"c:%p %@", self.mArrStrong, self.mArrStrong);
    NSLog(@"d:%p %@", self.mArrCopy, self.mArrCopy);
    [arr2 setArray:@[@"000"]];
    NSLog(@"額外測試2:arr2:%p self.mArrStrong:%p self.mArrCopy:%p %@ \n%@", arr2, self.mArrStrong, self.mArrCopy, self.mArrStrong, self.mArrCopy);
}

- (void)test2
{
    NSLog(@"%s", __func__);

    NSMutableArray *arr1 = [NSMutableArray arrayWithObjects:@"1", nil];
    [_mArrStrong2 setArray:arr1];
    [_mArrCopy2 setArray:arr1];
    [arr1 addObject:@"2"];
    NSLog(@"%p", arr1);
    NSLog(@"e:%p %@", _mArrStrong2, _mArrStrong2);
    NSLog(@"f:%p %@", _mArrCopy2, _mArrCopy2);
    
    NSMutableArray *arr2 = [NSMutableArray arrayWithObjects:@"1", nil];
    [self.mArrStrong2 setArray:arr2];
    [self.mArrCopy2 setArray:arr2];
    [arr2 addObject:@"2"];
    NSLog(@"%p", arr2);
    NSLog(@"g:%p %@", self.mArrStrong2, self.mArrStrong2);
    NSLog(@"h:%p %@", self.mArrCopy2, self.mArrCopy2);
}

@end

輸出如下:


2016-10-13 19:29:29.018 testArr[40794:1137604] -[ViewController test1]
2016-10-13 19:29:29.018 testArr[40794:1137604] 0x7f89cac0f3b0
2016-10-13 19:29:29.018 testArr[40794:1137604] a:0x7f89cac0f3b0 (1, 2)
2016-10-13 19:29:29.019 testArr[40794:1137604] b:0x7f89cac0f3b0 (1, 2)
2016-10-13 19:29:29.019 testArr[40794:1137604] 額外測試:arr1:0x7f89cd000f00 _mArrStrong:0x7f89cac0f3b0 _mArrCopy:0x7f89cac0f3b0 (1, 2) (1, 2)
2016-10-13 19:29:29.019 testArr[40794:1137604] 0x7f89cd00c5d0
2016-10-13 19:29:29.019 testArr[40794:1137604] c:0x7f89cd00c5d0 (1, 2)
2016-10-13 19:29:29.019 testArr[40794:1137604] d:0x7f89cd000f20 (1)
2016-10-13 19:29:29.019 testArr[40794:1137604] 額外測試2:arr2: 0x7f89cd00c5d0 self.mArrStrong: 0x7f89cd00c5d0 self.mArrCopy: 0x7f89cd000f20 (000) (1)
2016-10-13 19:29:29.019 testArr[40794:1137604] -[ViewController test2]
2016-10-13 19:29:29.019 testArr[40794:1137604] 0x7f89cd014250
2016-10-13 19:29:29.019 testArr[40794:1137604] e:0x7f89cae19c10 (1)
2016-10-13 19:29:29.019 testArr[40794:1137604] f:0x7f89cae19b40 (1)
2016-10-13 19:29:29.020 testArr[40794:1137604] 0x7f89cac16e40
2016-10-13 19:29:29.020 testArr[40794:1137604] g:0x7f89cae19c10 (1)
2016-10-13 19:29:29.034 testArr[40794:1137604] h:0x7f89cae19b40 (1)

這樣的輸出如果你不覺得意外,那么不用繼續瀏覽下文了。下面仔細談下我的理解。

代碼解析:

  • 輸出的a與b:

是通過 _ 獲取的屬性變量,他等同于一個全局變量,所以對于他的修飾copy或strong都不管作用,然后是通過指針賦值引用arr1的地址,從而_mArrStrong和_mArrCopy與arr1指向同一個內存地址,擁有一樣的值。所以_mArrStrong和_mArrCopy會由于arr1的變化而變化。

  • 輸出的額外測試1:

arr1指向了一個新的內存地址,數據是@[@"000"],而他之前指向的內存還有_mArrStrong和_mArrCopy引用故不會被釋放,所以arr1改變了,但是不會影響_mArrStrong與_mArrCopy的地址和數據還是保持之前的。

  • 輸出的c與d:

是通過self. 獲取的屬性變量,他會在返回 _ 變量前掉用get方法,從而與修飾的copy或strong構成了關聯。若是strong修飾的,則是淺拷貝,地址與值都保持一致,只是多了一個引用;反之若是copy修飾的,則是深拷貝,獲得了一份新的值,但是數據一樣。所以arr2的變化會引起self.mArrStrong的變化,而不會改變self.mArrCopy。

  • 輸出的額外測試2:

arr2指向的地址的值改變了,引起指向同一地址的self.mArrStrong的值也改變了,與self.mArrCopy無關。

  • 輸出的e與f:

是通過setArray方法賦值,_mArrStrong2與_mArrCopy2的地址不會變化,還是指向初始化時指向的地址,但是擁有了新的值為arr1,之后arr1指向的地址的值發生變化也不會聯系到_mArrStrong2與_mArrCopy2。

  • 輸出的g與h:

雖然self.mArrStrong2是淺拷貝,但是通過setArray賦了新值,地址還是初始化的地址,因此不會隨著arr2的變化而變化,self.mArrCopy2同理。

  • 特別提醒:

輸出的d,實現的是深拷貝功能,我并沒有給self.mArrCopy申請新的內存,但是系統會自動給它申請。那么是何時申請的呢?就在調用self.mArrCopy時系統會判斷給它new內存,因此我們在初始化時別用 self.mArrCopy = [NSMutableArray array];這樣的方式初始化,因為執行self.mArrCopy時 mArrCopy 為nil,系統會自動new內存, 但是 = 后面又開辟了內存給self.mArrCopy引用,從而導致內存浪費。因此在初始化強引用的屬性對象時用 _ 引用進行初始化,如:_mArrStrong = [NSMutableArray array];

總結如下:

只有采用self.的方式獲取copy修飾的屬性時才會是深拷貝,若是用setArray或是初始化方法賦值時則與修飾無關。無論面向可變的還是不可變的數據對象,包括數組、字符串、字典等都具有一樣的性質。若有不恰當之處請指出。

因此我的慣用寫法是使用copy修飾數據對象,更新數據時用setArray或是初始化方法。這樣做更保險,通過自己管理自己的生命周期,不依賴其它變量。

本篇文章的完善版本:對屬性變量賦新值時可能引發的血案

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,197評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,415評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,104評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,884評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,647評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,130評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,208評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,366評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,887評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,737評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,478評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,174評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,586評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,827評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,608評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,914評論 2 372

推薦閱讀更多精彩內容