ARC在編譯期間,根據(jù)Objective-C對象的存活周期绳匀,在適當(dāng)?shù)奈恢锰砑觬etain和release代碼芋忿。從概念上講,ARC與MRC內(nèi)存管理遵循同樣的內(nèi)存管理規(guī)則疾棵,區(qū)別MRC, ARC工作是編譯器完成的, 但是ARC也無法防止循環(huán)強引用戈钢。
ARC還引入了新的修飾符來修飾變量和聲明屬性。
聲明變量的修飾符:__strong, __weak, __unsafe_unretained, __autoreleasing;
聲明屬性的修飾符:strong, weak, unsafe_unretained是尔。
對于線程的安全殉了,有nonatomic,這樣效率就更高了拟枚,但是不是線程的薪铜。如果要線程安全,可以使用atomic恩溅,這樣在訪問是就會有線程鎖痕囱。
記住內(nèi)存管理法則(最重要的一點):誰使對象的引用計數(shù)+1,不再引用時暴匠,誰就負責(zé)將該對象的引用計數(shù)-1(誰引用誰釋放)鞍恢。
下面我們來聲明一個Person類來學(xué)習(xí):
@interface Person : NSObject
// 注意:蘋果有命名規(guī)范的,命名屬性時,不能以copy開頭帮掉。
// 如果下面的屬性聲明為copyString弦悉,會編譯不通過。
@property (nonatomic, copy) NSString *copiedString;
// 默認會是什么呢蟆炊?
@property (nonatomic) NSString *name;
// 默認是strong類型
@property (nonatomic) NSArray *array;
@end
如果屬性沒有指定類型稽莉,默認是strong。如果證明呢涩搓?驗證方法:分別將array屬性的類型分別設(shè)置為weak, assign,strong,不設(shè)置污秆,這四種情況的結(jié)果分別是:第一種打印為空,第二種直接直接崩潰昧甘,第三種和最后一種是可以正常使用良拼。如下面的驗證代碼:
Person *lili = [[Person alloc] init];
lili.name = @"LiLi";
lili.copiedString = @"LiLi\' father is LLL";
lili.array = @[@"謝謝", @"感謝"];
NSArray *otherArray = lili.array;
lili = nil;
NSLog(@"%@", otherArray);
再繼續(xù)添加下面的代碼。默認聲明變量的類型為__strong類型充边,因此上面的NSArray *otherArray = lili.array;與__strong NSArray *otherArray = lili.array;是一樣的庸推。如果我們要使用弱引用,特別是在解決循環(huán)強引用時就特別重要了浇冰。我們可以使用__weak聲明變量為弱引用贬媒,這樣就不會增加引用計數(shù)值。
__strong NSArray *strongArray = otherArray;
otherArray = nil;
// 打印出來正常的結(jié)果肘习。
NSLog(@"strongArray = %@", strongArray);
__weak NSArray * weakArray = strongArray;
strongArray = nil;
// 打印出來:null
NSLog(@"weakArray: %@", weakArray);
xib/storybard連接的對象為什么可以使用weak
@property (nonatomic, weak) IBOutlet UIButton *button;
像上面這行代碼一樣际乘,在連接時自動生成為weak。因為這個button已經(jīng)放到view上了漂佩,因此只要這個View不被釋放脖含,這個button的引用計數(shù)都不會為0,因此這里可以使用weak引用仅仆。
如果我們不使用xib/storyboard器赞,而是使用純代碼創(chuàng)建呢?
@property (nonatomic, weak) UIButton *button;
使用weak時墓拜,由于button在創(chuàng)建時港柜,沒有任何強引用,因此就有可能提前釋放咳榜。Xcode編譯器會告訴我們夏醉,這里不能使用weak。因此我們需要記住涌韩,只要我們在創(chuàng)建以后需要使用它畔柔,我們必須保證至少有一個強引用,否則引用計數(shù)為0臣樱,就會被釋放掉靶擦。對于上面的代碼腮考,就是由于在創(chuàng)建時使用了weak引用,因此button的引用計數(shù)仍然為0玄捕,也就是會被釋放踩蔚,編譯器在編譯時會檢測出來的。
這樣寫枚粘,在創(chuàng)建時通過self.button = ...就是出現(xiàn)錯誤馅闽,因為這是弱引用。所以我們需要聲明為強引用馍迄,也就是這樣:
@property (nonatomic, strong) UIButton *button;
block聲明使用copy
在使用block時福也,盡量使用typedef來起一個別名,這樣更容易閱讀攀圈。使block作為屬性時暴凑,使用copy。
typedef void (^HYBTestBlock)(NSString *name);
@property (nonatomic, copy) HYBTestBlock testBlock;
字符串
對于字符串量承,通常都是使用copy的方式搬设。雖然使用strong似乎也沒有沒有問題穴店,但是事實上在開發(fā)中都會使用copy撕捍。為什么這么做?因為對于字符串泣洞,我們希望是一次內(nèi)容的拷貝忧风,外部修改也不會影響我們的原來的值,而且NSString類遵守了NSCopying, NSMutableCopying, NSSecureCoding協(xié)議球凰。
下面時使用copy的方式狮腿,驗證如下:
NSString *hahaString = @"哈哈";
NSString *heheString = [hahaString copy];
// 哈哈, 哈哈
NSLog(@"%@, %@", hahaString, heheString);
heheString = @"呵呵";
// 哈哈, 呵呵
NSLog(@"%@, %@", hahaString, heheString);
我們修改了heheString,并不會影響到原來的hahaString呕诉。copy一個對象變成新的對象(新內(nèi)存地址) 引用計數(shù)為1 原來對象計數(shù)不變缘厢。
屬性聲明修飾符
屬性聲明修飾符有:strong, weak, unsafe_unretained, readWrite,默認strong, readWrite的甩挫。
strong:strong和retain相似,只要有一個strong指針指向?qū)ο筇颍搶ο缶筒粫讳N毀
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);
深拷貝與淺拷貝
關(guān)于淺拷貝痴突,簡單來說,就像是人與人的影子一樣狼荞。而深拷貝就像是夢幻西游中的龍宮有很多個長得一樣的龍宮苞也,但是他們都是不同的精靈,因此他們各自都是獨立的粘秆。
我相信還有不少朋友有這樣一種誤解:淺拷貝就是用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]);
// 打庸淇恪: 謝謝 修改了謝謝
// 說明數(shù)組里面是字符串時瘩绒,直接使用copy是相當(dāng)于深拷貝的。
NSLog(@"%@ %@", otherArray[0], lili.array[0]);
這里就是淺拷貝带族,但是由于數(shù)組中的元素都是字符串锁荔,因此不會影響原來的值。
數(shù)組中是對象時:
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的名字被修改了";
// 打印結(jié)果:lili的名字被修改了
// 說明這邊修改了蝙砌,原來的數(shù)組對象的值也被修改了阳堕。雖然newArray和personArray不是同一個數(shù)組,不是同一塊內(nèi)存择克,
// 但是實際上兩個數(shù)組的元素都是指向同一塊內(nèi)存恬总。
NSLog(@"%@", ((Person *)(personArray[0])).name);
深拷貝,其實就是對數(shù)組中的所有對象都創(chuàng)建一個新的對象:
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的名字被修改了";
// 打印結(jié)果:lili
NSLog(@"%@", ((Person *)(personArray[0])).name);
Getter/Setter
在ARC下肚邢,getter/setter的寫法與MRC的不同了壹堰。我面試過一些朋友,筆試這關(guān)就寫得很糟(不包括算法)道偷。通常在筆試時都會讓重寫一個屬性的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;
}
總結(jié)
關(guān)于屬性的這些選項的學(xué)習(xí)换途,做一下總結(jié):
所有的屬性,都盡可能使用nonatomic,以提高效率录语,除非真的有必要考慮線程安全蜓萄。
NSString:通常都使用copy牙咏,以得到新的內(nèi)存分配,而不只是原來的引用。
strong:對于繼承于NSObject類型的對象,若要聲明為強使用摹恰,使用strong,若要使用弱引用怒见,使用__weak來引用俗慈,用于解決循環(huán)強引用的問題。
(對于數(shù)組, 字典, 集合, 字符串來說, 可變用strong修飾, 不可變用copy修飾)
weak:對于xib上的控件引用遣耍,可以使用weak闺阱,也可以使用strong。
__weak:對于變量的聲明舵变,如果要使用弱引用酣溃,可以使用__weak,如:__weak typeof(Model) weakModel = model;就可以直接使用weakModel了纪隙。
__strong:對于變量的聲明赊豌,如果要使用強引用,可以使用__strong瘫拣,默認就是__strong亿絮,因此不寫與寫__strong聲明都是一樣的告喊。
unsafe_unretained:這個是比較少用的麸拄,幾乎沒有使用到。在所引用的對象被釋放后黔姜,該指針就成了野指針拢切,不好控制。
__unsafe_unretained:也是很少使用秆吵。同上淮椰。
__autoreleasing:如果要在循環(huán)過程中就釋放,可以手動使用__autoreleasing來聲明將之放到自動釋放池纳寂。
參考資料
http://www.reibang.com/p/c16467bbedc1