copy/strong/weak/__weak/__strong/assign的使用總結

ARC在編譯期間,根據Objective-C對象的存活周期,在適當的位置添加retain和release代碼。從概念上講,ARC與MRC內存管理遵循同樣的內存管理規則,區別MRC, ARC工作是編譯器完成的, 但是ARC也無法防止循環強引用。

ARC還引入了新的修飾符來修飾變量和聲明屬性。

聲明變量的修飾符:__strong, __weak, __unsafe_unretained, __autoreleasing;

聲明屬性的修飾符:strong, weak, unsafe_unretained。

對于線程的安全,有nonatomic,這樣效率就更高了,但是不是線程的。如果要線程安全,可以使用atomic,這樣在訪問是就會有線程鎖。

記住內存管理法則(最重要的一點):誰使對象的引用計數+1,不再引用時,誰就負責將該對象的引用計數-1(誰引用誰釋放)。

下面我們來聲明一個Person類來學習:

@interface Person : NSObject

// 注意:蘋果有命名規范的,命名屬性時,不能以copy開頭。

// 如果下面的屬性聲明為copyString,會編譯不通過。

@property (nonatomic, copy) NSString *copiedString;

// 默認會是什么呢?

@property (nonatomic) NSString *name;

// 默認是strong類型

@property (nonatomic) NSArray *array;

@end

如果屬性沒有指定類型,默認是strong。如果證明呢?驗證方法:分別將array屬性的類型分別設置為weak, assign,strong,不設置,這四種情況的結果分別是:第一種打印為空,第二種直接直接崩潰,第三種和最后一種是可以正常使用。如下面的驗證代碼:

Person *lili = [[Person alloc] init];

lili.name = @"LiLi";

lili.copiedString = @"LiLi\' father is LLL";

lili.array = @[@"謝謝", @"感謝"];

NSArray *otherArray = lili.array;

lili = nil;

NSLog(@"%@", otherArray);

再繼續添加下面的代碼。默認聲明變量的類型為__strong類型,因此上面的NSArray *otherArray = lili.array;與__strong NSArray *otherArray = lili.array;是一樣的。如果我們要使用弱引用,特別是在解決循環強引用時就特別重要了。我們可以使用__weak聲明變量為弱引用,這樣就不會增加引用計數值。

__strong NSArray *strongArray = otherArray;

otherArray = nil;

// 打印出來正常的結果。

NSLog(@"strongArray = %@", strongArray);

__weak NSArray * weakArray = strongArray;

strongArray = nil;

// 打印出來:null

NSLog(@"weakArray: %@", weakArray);

xib/storybard連接的對象為什么可以使用weak

@property (nonatomic, weak) IBOutlet UIButton *button;

像上面這行代碼一樣,在連接時自動生成為weak。因為這個button已經放到view上了,因此只要這個View不被釋放,這個button的引用計數都不會為0,因此這里可以使用weak引用。

如果我們不使用xib/storyboard,而是使用純代碼創建呢?

@property (nonatomic, weak) UIButton *button;

使用weak時,由于button在創建時,沒有任何強引用,因此就有可能提前釋放。Xcode編譯器會告訴我們,這里不能使用weak。因此我們需要記住,只要我們在創建以后需要使用它,我們必須保證至少有一個強引用,否則引用計數為0,就會被釋放掉。對于上面的代碼,就是由于在創建時使用了weak引用,因此button的引用計數仍然為0,也就是會被釋放,編譯器在編譯時會檢測出來的。

這樣寫,在創建時通過self.button = ...就是出現錯誤,因為這是弱引用。所以我們需要聲明為強引用,也就是這樣:

@property (nonatomic, strong) UIButton *button;

block聲明使用copy

在使用block時,盡量使用typedef來起一個別名,這樣更容易閱讀。使block作為屬性時,使用copy。

typedef void (^HYBTestBlock)(NSString *name);

@property (nonatomic, copy) HYBTestBlock testBlock;

字符串

對于字符串,通常都是使用copy的方式。雖然使用strong似乎也沒有沒有問題,但是事實上在開發中都會使用copy。為什么這么做?因為對于字符串,我們希望是一次內容的拷貝,外部修改也不會影響我們的原來的值,而且NSString類遵守了NSCopying, NSMutableCopying, NSSecureCoding協議。

下面時使用copy的方式,驗證如下:

NSString *hahaString = @"哈哈";

NSString *heheString = [hahaString copy];

// 哈哈, 哈哈

NSLog(@"%@, %@", hahaString, heheString);

heheString = @"呵呵";

// 哈哈, 呵呵

NSLog(@"%@, %@", hahaString, heheString);

我們修改了heheString,并不會影響到原來的hahaString。copy一個對象變成新的對象(新內存地址) 引用計數為1 原來對象計數不變。

屬性聲明修飾符

屬性聲明修飾符有:strong, weak, unsafe_unretained, readWrite,默認strong, readWrite的。

strong:strong和retain相似,只要有一個strong指針指向對象,該對象就不會被銷毀

weak:聲明為weak的指針,weak指針指向的對象一旦被釋放,weak的指針都將被賦值為nil;

unsafe_unretained:用unsafe_unretained聲明的指針,指針指向的對象一旦被釋放,這些指針將成為野指針。

@property (nonatomic, copy) NSString *name;

// 一旦所指向的對象被釋放,就會成為野指針

@property (nonatomic, unsafe_unretained) NSString *unsafeName;

lili.name = @"Lili";

lili.unsafeName = lili.name;

lili.name = nil;

// unsafeName就變成了野指針。這里不會崩潰,因為為nil.

NSLog(@"%@", lili.unsafeName);

深拷貝與淺拷貝

關于淺拷貝,簡單來說,就像是人與人的影子一樣。而深拷貝就像是夢幻西游中的龍宮有很多個長得一樣的龍宮,但是他們都是不同的精靈,因此他們各自都是獨立的。

我相信還有不少朋友有這樣一種誤解:淺拷貝就是用copy,深拷貝就是用mutableCopy。如果有這樣的誤解,一定要更正過來。copy只是不可變拷貝,而mutableCopy是可變拷貝。比如,NSArray *arr = [modelsArray copy],那么arr是不可變的。而NSMutableArray *ma = [modelsArray mutableCopy],那么ma是可變的。

lili.array = [@[@"謝謝", @"感謝"] mutableCopy];

NSMutableArray *otherArray = [lili.array copy];

lili.array[0] = @"修改了謝謝";

NSLog(@"%@ %@", otherArray[0], lili.array[0]);

// 打?。?謝謝 修改了謝謝

// 說明數組里面是字符串時,直接使用copy是相當于深拷貝的。

NSLog(@"%@ %@", otherArray[0], lili.array[0]);

這里就是淺拷貝,但是由于數組中的元素都是字符串,因此不會影響原來的值。

數組中是對象時:

NSMutableArray *personArray = [[NSMutableArray alloc] init];

Person *person1 = [[Person alloc] init];

person1.name = @"lili";

[personArray addObject:person1];

Person *person2 = [[Person alloc] init];

person2.name = @"lisa";

[personArray addObject:person2];

// 淺拷貝

NSArray *newArray = [personArray copy];

Person *p = newArray[0];

p.name = @"lili的名字被修改了";

// 打印結果:lili的名字被修改了

// 說明這邊修改了,原來的數組對象的值也被修改了。雖然newArray和personArray不是同一個數組,不是同一塊內存,

// 但是實際上兩個數組的元素都是指向同一塊內存。

NSLog(@"%@", ((Person *)(personArray[0])).name);

深拷貝,其實就是對數組中的所有對象都創建一個新的對象:

NSMutableArray *personArray = [[NSMutableArray alloc] init];

Person *person1 = [[Person alloc] init];

person1.name = @"lili";

[personArray addObject:person1];

Person *person2 = [[Person alloc] init];

person2.name = @"lisa";

[personArray addObject:person2];

// 深拷貝

NSMutableArray *newArray = [[NSMutableArray alloc] init];

for (Person *p in personArray) {

Person *newPerson = [[Person alloc] init];

newPerson.name = p.name;

[newArray addObject:newPerson];

}

Person *p = newArray[0];

p.name = @"lili的名字被修改了";

// 打印結果:lili

NSLog(@"%@", ((Person *)(personArray[0])).name);

Getter/Setter

在ARC下,getter/setter的寫法與MRC的不同了。我面試過一些朋友,筆試這關就寫得很糟(不包括算法)。通常在筆試時都會讓重寫一個屬性的Getter/Setter方法。

@property (nonatomic, strong) NSMutableArray *array;

- (void)setArray:(NSMutableArray *)array {

if (_array != array) {

_array = nil;

_array = array;

}

}

如果是要重寫getter就去呢?就得增加一個變量了,如果同時重寫getter/setter方法,就不會自動生成_array變量,因此我們可以聲明一個變量為_array:

- (void)setArray:(NSMutableArray *)array {

if (_array != array) {

_array = nil;

_array = array;

}

}

- (NSMutableArray *)array {

return _array;

}

總結

關于屬性的這些選項的學習,做一下總結:

所有的屬性,都盡可能使用nonatomic,以提高效率,除非真的有必要考慮線程安全。

NSString:通常都使用copy,以得到新的內存分配,而不只是原來的引用。

strong:對于繼承于NSObject類型的對象,若要聲明為強使用,使用strong,若要使用弱引用,使用__weak來引用,用于解決循環強引用的問題。

(對于數組, 字典, 集合, 字符串來說, 可變用strong修飾, 不可變用copy修飾)

weak:對于xib上的控件引用,可以使用weak,也可以使用strong。

__weak:對于變量的聲明,如果要使用弱引用,可以使用__weak,如:__weak typeof(Model) weakModel = model;就可以直接使用weakModel了。

__strong:對于變量的聲明,如果要使用強引用,可以使用__strong,默認就是__strong,因此不寫與寫__strong聲明都是一樣的。

unsafe_unretained:這個是比較少用的,幾乎沒有使用到。在所引用的對象被釋放后,該指針就成了野指針,不好控制。

__unsafe_unretained:也是很少使用。同上。

__autoreleasing:如果要在循環過程中就釋放,可以手動使用__autoreleasing來聲明將之放到自動釋放池。


參考資料

http://www.lxweimin.com/p/c16467bbedc1

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

推薦閱讀更多精彩內容