iPhone和iPad在內(nèi)存方面是資源緊缺的設(shè)備,一個(gè)app如果超過了最大使用限制它可能被系統(tǒng)干掉的片排,因此實(shí)現(xiàn)一個(gè)iOS app管理好內(nèi)存是非常重要的。
在WWDC 2011大會(huì)上面,蘋果公司透露岸售,大約90%的設(shè)備崩潰都與內(nèi)存管理有關(guān)错敢,輕重最大的原因是內(nèi)存訪問錯(cuò)誤或者內(nèi)存泄露導(dǎo)致的循環(huán)引用翰灾。
不像java運(yùn)行時(shí)使用的shi是垃圾回收機(jī)制,iOS使用的是引用計(jì)數(shù),引用計(jì)數(shù)的缺點(diǎn)就是如果開發(fā)者不注意可能重復(fù)釋放內(nèi)存和循環(huán)引用纸淮。
因此在iOS中理解內(nèi)存管理是非常重要的平斩。
在這一章中我們將學(xué)習(xí)一下知識(shí):
- 內(nèi)存消耗(例如:一個(gè)app怎么消耗內(nèi)存的)
- 內(nèi)存管理模型(例如:iOS運(yùn)行時(shí)怎么管理內(nèi)存)
- 語法:我們將看看一些我們能看到的OC語法特性。
- 最小內(nèi)存實(shí)踐而不影響用戶體驗(yàn)咽块。
內(nèi)存消耗
內(nèi)存消耗是指一個(gè)app消耗的RAM(隨機(jī)存取存儲(chǔ)器(英語:Random Access Memory绘面,RAM),是與CPU直接交換數(shù)據(jù)的內(nèi)部存儲(chǔ)器侈沪,也叫主存).
iOS虛擬內(nèi)存模型不包括交換內(nèi)存揭璃,這就意味著,不想桌面應(yīng)用磁盤不能用于內(nèi)存分頁峭竣,這直接導(dǎo)致的是app受到可用RAM的限制塘辅,RAM不僅用于前臺(tái)應(yīng)用同時(shí)也被系統(tǒng)服務(wù)或者其他應(yīng)用的后臺(tái)任務(wù)占用。
在一個(gè)app中有兩部分的內(nèi)存消耗:棧大小和堆大小皆撩。接下來我們將深入討論這兩點(diǎn)扣墩。
棧大小
在應(yīng)用中的每個(gè)新線程棧空間的構(gòu)成包括預(yù)留和初始化兩部分扛吞,當(dāng)線程退出的時(shí)候椛胩瑁空間就會(huì)被釋放,一個(gè)線程的最大棧尺寸是很小的滥比,除此之外亚脆,他還限制了以下東西:
最大數(shù)量方法被遞歸訪問
每個(gè)方法都有他自己的棧框架盲泛,來消耗整個(gè)棻舫郑空間大小。例如例子2-1顯示寺滚,如果我們?cè)L問main方法它接著又訪問了method1 又訪問了method2柑营,所有有三個(gè)棧框架來消耗一些字節(jié)空間村视,圖2-1顯示了一個(gè)線程堆棧的樣子官套。
例 2-1
main() {
method1();
}
method1() {
method2();
}
你可以在一個(gè)方法中使用的最大變量個(gè)數(shù)
棧框架中的變量都會(huì)被加載蚁孔,所以會(huì)消耗椖膛猓空間。
你可以在嵌套視圖結(jié)構(gòu)中使用的最大數(shù)量視圖
渲染一個(gè)復(fù)雜視圖將會(huì)調(diào)用layoutSubViews和drawRect方法來遞歸訪問整個(gè)層次結(jié)構(gòu)樹杠氢,如果層次結(jié)構(gòu)太深站刑,那么它可能導(dǎo)致棧溢出
圖 2-1
堆大小
一個(gè)進(jìn)程中所有的線程共享同一個(gè)堆棧空間鼻百〉讯郏可用的堆空間一般都比設(shè)備的RAM小质况,例如iPhone5s可能有1G的RAM,但是app能使用的堆空間可能只有512M或者更少。app還不能控制堆的分配玻靡,他被系統(tǒng)管理。
例如使用NSString中贝、加載圖片囤捻、創(chuàng)建或使用JSON/XML數(shù)據(jù)和使用視圖都將消耗許多堆內(nèi)存。如果你的app需要使用到大量的圖片(像Flickr和Instagram)邻寿,你需要特別注意內(nèi)存的平均值和峰值使用蝎土。
圖2-2顯示了一個(gè)在應(yīng)用中可能出現(xiàn)的一個(gè)典型的堆。
在圖2-2中主線程在main方法中創(chuàng)建了一個(gè)UIApplication绣否,我們假設(shè)窗口在某個(gè)時(shí)間點(diǎn)使用了UITableViewDataSourcelai來展示UITableView誊涯,當(dāng)需要顯示一行的時(shí)候需要調(diào)用tableView:cellForRowAtIndex:.
數(shù)據(jù)源是引用到一個(gè)所有需要顯示圖片的photos數(shù)組。 如果實(shí)現(xiàn)不合理蒜撮,這個(gè)數(shù)組可能會(huì)很大暴构,結(jié)果導(dǎo)致高的內(nèi)存消耗。一個(gè)解決方案是存儲(chǔ)固定數(shù)量的那些顯示在用戶滾動(dòng)區(qū)域的段磨。這固定的數(shù)量將決定你的app的平均內(nèi)存使用取逾。
圖2-2
在數(shù)組中的每一個(gè)對(duì)象都是HPPhoto對(duì)象,HPPhoto存儲(chǔ)和對(duì)象關(guān)聯(lián)的數(shù)據(jù)苹支,例如圖片大小砾隅、創(chuàng)建日期、作者债蜜、標(biāo)簽晴埂、圖片URL、本地緩存等等寻定。
從類中創(chuàng)建與對(duì)象關(guān)聯(lián)的所有數(shù)據(jù)都存儲(chǔ)在堆中儒洛。
這個(gè)類中有些值類型實(shí)例變量例如int、char或者struct特姐,但是因?yàn)閷?duì)象是創(chuàng)建在堆中晶丘,所有他們將消耗堆內(nèi)存。
當(dāng)對(duì)象創(chuàng)建后賦值唐含,他們可能從棧復(fù)制到堆中浅浮,同樣當(dāng)一個(gè)值是被用在一個(gè)方法里面的時(shí)候,他們可能從堆復(fù)制到棧當(dāng)中捷枯,這是一個(gè)非常昂貴的開銷滚秩,示例2-2中是從棧復(fù)制到堆中,反之亦然淮捆。
示例2-2 堆和棧
@interface AClass 1
@property (nonatomic, assign) NSInteger anInteger; 2
@property (nonatomic, copy) NSString *aString; 3
@end
//some other class
-(AClass *) createAClassWithInteger:(NSInteger)i
string:(NSString *)s {4
AClass *result = [AClass new];
result.anInteger = i;5
result.aString = s;6
}
-(void) someMethod:(NSArray *)items {7
NSInteger total = 0;
NSMutableString *finalValue = [NSMutableString string];
for(AClass *obj in items) {
total += obj.anInteger;8
[finalValue appendString:obj.aString];9
} }
- AClass類有兩個(gè)屬性
- anInteger是一個(gè)NSInteger類型郁油,值傳遞
- aString是一個(gè)NSString類型本股,引用傳遞
- createAClassWithInteger: string:方法用于實(shí)例化AClass,這個(gè)方法提供創(chuàng)建對(duì)象的必要值桐腌。
- 值i是在在棧上面的拄显,當(dāng)賦值給屬性的時(shí)候他必須從棧上復(fù)制到堆,因?yàn)檫@是結(jié)果存儲(chǔ)的位置案站。
- 盡管NSString *是引用傳遞躬审,這個(gè)屬性是被標(biāo)記為copy,意味著這個(gè)值必須被復(fù)制或者克隆蟆盐,依賴于-NSCopying copyWithZone:方法是怎么實(shí)現(xiàn)的承边。
- somethod:方法用于處理AClass對(duì)象中的一個(gè)數(shù)組。
- 當(dāng)anInteger被調(diào)用的時(shí)候石挂,他的值必須在使用之前復(fù)制到棧中博助,這個(gè)值被加到total中。
- aString調(diào)用的時(shí)候痹愚,他是引用傳遞富岳,appendString:方法是用的aString對(duì)象的引用。
建議:維持內(nèi)存不超過可用的RAM是一個(gè)好辦法里伯,雖然沒有硬性規(guī)定城瞎,但是建議不要超過80%-85%,其余的用于核心操作系統(tǒng)服務(wù)疾瓮,不要忽視didReceiveMemoryWarning信號(hào)脖镀。
內(nèi)存管理模型
在本節(jié)中我們將學(xué)習(xí)iOS運(yùn)行時(shí)如何管理內(nèi)存和影響代碼的。
內(nèi)存管理模型是基于一種擁有關(guān)系概念的狼电,只有被一個(gè)對(duì)象擁有蜒灰,那么它使用的內(nèi)存就不能被回收。
當(dāng)一個(gè)對(duì)象在方法中被創(chuàng)建的時(shí)候肩碟,那么這個(gè)方法擁有這個(gè)對(duì)象强窖,如果這個(gè)對(duì)象從方法中返回,然后被調(diào)用削祈,那么調(diào)用者也擁有這個(gè)對(duì)象翅溺,這個(gè)值被分配到另外一個(gè)變量,對(duì)應(yīng)的變量也同樣擁有這個(gè)對(duì)象髓抑。
一旦對(duì)象的任務(wù)完成咙崎,你將釋放擁有權(quán),這個(gè)過程不會(huì)轉(zhuǎn)移擁有權(quán)吨拍,但是會(huì)增加或減少對(duì)象的擁有計(jì)數(shù)褪猛,當(dāng)對(duì)象的擁有者數(shù)量降為0的時(shí)候,這個(gè)對(duì)象將會(huì)被回收并釋放內(nèi)存羹饰。
擁有關(guān)系計(jì)數(shù)通常被說成ARC伊滋,當(dāng)你自己管理他的時(shí)候交MRC碳却,盡管MRC很少用,但是,MRC對(duì)于理解內(nèi)存管理模型是很有用的笑旺,Modern-apps是用ARC的我們將討論ARC在39頁昼浦。
示例2-3 演示了用引用計(jì)數(shù)管理內(nèi)存的基本結(jié)構(gòu)。
示例 2-3 引用計(jì)數(shù)手動(dòng)管理內(nèi)存
NSString *message = @"Objective-C is a verbose yet awesome language";1
NSString *messageRetained = [message retain];2
[messageRetained release];3
[message release];4
NSLog(@"Value of message: %@", message);5
- 對(duì)象創(chuàng)建筒主,message擁有對(duì)象座柱,引用計(jì)數(shù)為1
- messageRetained擁有對(duì)像,引用計(jì)數(shù)為2
- messageRetained釋放擁有權(quán)物舒,引用計(jì)數(shù)為1
- message釋放擁有權(quán),引用計(jì)數(shù)減到0
- message的值嚴(yán)格的說是不確定的戏锹,可能得到之前一樣的值冠胯,因?yàn)樗赡苓€沒有被重用或者重置。
示例 2-4 演示了方法如何影響引用計(jì)數(shù)
示例 2-4 方法中的引用計(jì)數(shù)
//part of a class Person
-(NSString *) address {
NSString *result = [[NSString alloc]
initWithFormat:@"%@\n%@\n%@, %@",
self.line1, self.line2, self.city, self.state]; 1
return result;
}
-(void) showPerson:(Person *) p {
NSString *paddress = [p address]; 2
NSLog(@"Person's Address: %@", paddress);
[paddress release]; 3
}
- 對(duì)象被創(chuàng)建锦针,result的引用計(jì)數(shù)為1
- 被paddress引用之后result的引用計(jì)數(shù)還是1荠察,因?yàn)閟howPerson:是對(duì)象所有者創(chuàng)建使用地址按鈕,它不應(yīng)該擁有result奈搜;
- 釋放所有權(quán)悉盆,引用計(jì)數(shù)為0;
看示例2-4 showPerson:方法不知道address是應(yīng)該新建還是復(fù)用馋吗,但是他確實(shí)知道這個(gè)對(duì)象是引用計(jì)數(shù)加1之后被返回的焕盟,因此他不能擁有address,一旦工作完成宏粤,它將釋放address脚翘,如果這個(gè)對(duì)象引用計(jì)數(shù)為1那么他將變?yōu)?然后被釋放。
蘋果的LLVN文檔比較喜歡用擁有關(guān)系绍哎,在本書中擁有關(guān)系和引用計(jì)數(shù)是可以互換的来农。
自動(dòng)釋放對(duì)象
自動(dòng)釋放對(duì)象允許延遲釋放對(duì)象的擁有關(guān)系,他有用的一個(gè)場(chǎng)景是當(dāng)我們?cè)谝粋€(gè)方法中創(chuàng)建一個(gè)對(duì)象想要返回他的時(shí)候崇堰,他能幫助MRC管理對(duì)象的生命周期沃于。
嚴(yán)格意義上從OC的語法上講(誰創(chuàng)建誰釋放),在示例2-4中海诲,沒有展示address方法擁有返回值繁莹,因此調(diào)用者showPerson:沒有理由來釋放這個(gè)返回值,因此就存在一個(gè)潛在的內(nèi)存泄露[paddress release]這段代碼是為了演示的目的饿肺。
那么什么才是address方法的正確寫法
有兩種方法:
- 不要用alloc相關(guān)聯(lián)的方法
- 使用延遲消息返回一個(gè)對(duì)象
因?yàn)槭褂玫氖荖SString所以修改很容易實(shí)現(xiàn)蒋困,更新代碼像示例2-5
示例 2-5
-(NSString *) address {
NSString *result = [NSString
stringWithFormat:@"%@\n%@\n%@, %@",
self.line1, self.line2, self.city, self.state]; 1
return result;
}
-(void) showPerson:(Person *) p {
NSString *paddress = [p address];
NSLog(@"Person's Address: %@", paddress);
2
}
- 沒有使用alloc方法
- 在showPerson:方法中不要使用relead因?yàn)樗麤]有創(chuàng)建實(shí)例
但是我們?nèi)绻皇鞘褂玫腘SString我們將不好修復(fù)的,因?yàn)橥ǔ:茈y找到合適的方法來為我們所需要敬辣。例如我們使用一個(gè)第三方的類庫或者一個(gè)類有多種方法來創(chuàng)建對(duì)象雪标,它通常不容易發(fā)現(xiàn)哪個(gè)方法是維持擁有關(guān)系零院。
因此還有什么辦法來處理呢。
NSObject協(xié)議定義了一個(gè)消息autorelease能用于延遲釋放村刨,當(dāng)我們從一個(gè)方法返回對(duì)象的時(shí)候使用它告抄。
更新代碼2-6
示例2-6 使用autorelease引用計(jì)數(shù)
-(NSString *) address
{
NSString *result = [[[NSString alloc] initWithFormat:@"%@\n%@\n%@, %@",
self.line1, self.line2, self.city, self.state]
autorelease];
return result;
}
這段代碼能按下面步驟進(jìn)行分析:
- 你擁有通過alloc創(chuàng)建的返回對(duì)象
- 為了確保沒有內(nèi)存泄露,必須在失去引用前釋放擁有權(quán)
- 因此如果使用了release 對(duì)象將會(huì)在方法返回前被釋放嵌牺,結(jié)果就是返回一個(gè)不可用的引用
- autorelease的作用就是你想釋放擁有權(quán)但是同時(shí)又想讓調(diào)用者在這個(gè)返回對(duì)象釋放是之前使用打洼。
使用autorelease當(dāng)我們創(chuàng)建一個(gè)對(duì)象從不是alloc方法放回的時(shí)候,他能確保調(diào)用者一方完成工作逆粹,對(duì)對(duì)象的釋放募疮。
Autorelease Pool Blocks 自動(dòng)釋放池塊
自動(dòng)釋放池塊允許釋放對(duì)象的擁有權(quán)但是避免他不會(huì)被立刻被回收。這是非常有用的對(duì)于從一個(gè)方法中返回一個(gè)對(duì)象僻弹。
他也能確保一個(gè)在block中創(chuàng)建的對(duì)象在block執(zhí)行完成的釋放阿浓,因此放我們需要?jiǎng)?chuàng)建多個(gè)對(duì)象的時(shí)候是非常有用的(for循環(huán)里面使用autorelease)。本地塊的創(chuàng)建能讓對(duì)象竟可能早的釋放所以能保持內(nèi)存在一個(gè)比較低點(diǎn)蹋绽。
一個(gè)自動(dòng)釋放池kuai塊是用@autorelease 標(biāo)記
如果你打開項(xiàng)目的main.m文件,你會(huì)看到示例2-7代碼芭毙。
示例 2-7 @autoreleasepool block in main.m
int main(int argc, char * argv[]) { @autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([HPAppDelegate class]));
} }
所有包含在block里面的對(duì)象都會(huì)發(fā)送一個(gè)autorelease消息同時(shí)在autautorelease block的結(jié)尾會(huì)發(fā)送一個(gè)release消息。也就是說一個(gè)對(duì)象被發(fā)送autorelease消息那么同時(shí)也會(huì)發(fā)送一個(gè)release消息卸耘,這是好的退敦,因?yàn)樗麑⒕S持對(duì)象的引用計(jì)數(shù)和加入autorelease塊之前一樣,如果引用計(jì)數(shù)為0蚣抗,對(duì)象將會(huì)被回收侈百,保持較低的內(nèi)存占用。
如果你看在main中的方法忠聚,你將看到app的入口都包含在autorelease塊中设哗,這意味著任何對(duì)象在最后都會(huì)被釋放,不會(huì)有內(nèi)存泄露两蟀。
另外的一些block代碼网梢,autorelease塊是可以被嵌套的,像示例2-8
示例 2-8. Nested autoreleasepool blocks
@autoreleasepool { // some code
@autoreleasepool {
// some more code
} }
因?yàn)榭刂茝囊粋€(gè)方法傳遞到另外一個(gè)方法赂毯,autorelease嵌套是常見的战虏,,然而被調(diào)用的方法可能有使用autorelease塊來回收要提前釋放的對(duì)象党涕。
自動(dòng)引用塊是無處不在的:Cocoa礦建希望代碼都在autorelease塊中執(zhí)行的烦感,否則autorelease的對(duì)象是不會(huì)被釋放的導(dǎo)致app內(nèi)存泄露,AppKitheUIKit框架都使用autorelease 塊來處理事件的迭代循環(huán)膛堤,因此一般都不要自己創(chuàng)建手趣。
因此有些場(chǎng)合,你可能需要?jiǎng)?chuàng)建autorelease塊,例如:
當(dāng)你有一個(gè)循環(huán)需要?jiǎng)?chuàng)建許多零時(shí)對(duì)象
用autorelease pool block在循環(huán)代碼中來釋放每次迭代的內(nèi)存绿渣。 雖然最終的內(nèi)存使用在迭代前和迭代后釋放都是一樣的朝群,但是你的app能減少瞬間最大內(nèi)存。
當(dāng)你創(chuàng)建一個(gè)線程的時(shí)候
每個(gè)線程都有他自己的autoreleasepool block棧中符,主線程有姜胖,因?yàn)樗詣?dòng)生成,但是對(duì)于自定義線程淀散,你必須創(chuàng)建自己的autoreleasepool 示例2-10
示例 2-9 Autorelease pool block in a loop
//Bad code 1
{
@autoreleasepool {
NSUInteger *userCount = userDatabase.userCount; for(NSUInteger *i = 0; i < userCount; i++) {
Person *p = [userDatabase userAtIndex:i];
NSString *fname = p.fname; if(fname == nil) {
fname = [self askUserForFirstName];
}
NSString *lname = p.lname; if(lname == nil) {
lname = [self askUserForLastName];
}
//...
[userDatabase updateUser:p];
}
} }
//Good code 2
{
@autoreleasepool {
NSUInteger *userCount = userDatabase.userCount; for(NSUInteger *i = 0; i < userCount; i++) {
@autoreleasepool {
Person *p = [userDatabase userAtIndex:i];
NSString *fname = p.fname; if(fname == nil) {
fname = [self askUserForFirstName];
}
NSString *lname = p.lname; if(lname == nil) {
lname = [self askUserForLastName];
}
//...
[userDatabase updateUser:p];
}
} }
}
- 這個(gè)代碼是不好的因?yàn)樗挥幸粋€(gè)autoreleasepool 內(nèi)存清理在所有的迭代完成之后右莱。
- 在這個(gè)例子中,有兩個(gè)autoreleasepool档插,內(nèi)部的autoreleasepool確保每次迭代后清理內(nèi)存慢蜓,所以需要更少的內(nèi)存。
示例2-10 Autorelease pool block in custom thread
-(void)myThreadStart:(id)obj { @autoreleasepool {
//New thread's code
} }
//Somewhere else
{
NSThread *myThread = [[NSThread alloc] initWithTarget:self
selector:@selector(myThreadStart:) object:nil];
[myThread start];
}
ARC
持續(xù)跟蹤retain郭膛、release和autorelease是不容易的胀瞪,更讓人困惑的時(shí)候不知道什么時(shí)候什么地點(diǎn)該發(fā)消息給誰。
蘋果是介紹了ARC在WWDC 2011來解決這個(gè)問題饲鄙,Swift是開發(fā)iOS app的新語言,也是使用ARC和OC不同的是Swift不支持MRC
ARC是一個(gè)編譯功能圆雁,他評(píng)估對(duì)象的整個(gè)生命周期代碼忍级,在適當(dāng)?shù)奈蛔幼詣?dòng)注入內(nèi)存管理代碼,編譯器會(huì)自動(dòng)生成dealloc方法伪朽,這就意味著跟蹤內(nèi)存使用的困難都解決了轴咱。
圖2-3展示了MRC和ARC在開發(fā)時(shí)候的關(guān)系,使用ARC開發(fā)是更快的烈涮,因?yàn)樗a更少朴肺,
圖2-3
你需要確定ARC是在Xcode設(shè)置是允許的,從Xcode5開始是默認(rèn)的 (看圖2=4)
圖 2-4
不使用ARC依賴:可能很難找到?jīng)]有使用ARC依賴或者替代方案坚洽。但是如果不希望用ARC,那么你需要對(duì)一個(gè)或多個(gè)文件限制ARC功能戈稿。
為了禁用ARC,去設(shè)置Targets → Build Phases → Compile Sources 選擇不需要ARC的文件讶舰,添加編譯標(biāo)記-fno-objc-arc鞍盗,如圖2-5
圖2-5
這同樣的操作能創(chuàng)建一個(gè)復(fù)合類,可以在分類中使用MRC代碼跳昼,代碼卸載分類中般甲,ARC的代碼可以通過這個(gè)文件禁用。
ARC規(guī)則
ARC是編寫代碼時(shí)候需要遵循的規(guī)則鹅颊,這些規(guī)則的目的是提供一個(gè)可靠的內(nèi)存管理模型敷存,在某些情況下,他們只執(zhí)行最佳實(shí)踐堪伍,在他們簡(jiǎn)化代碼你不必直接參與內(nèi)存管理锚烦,這些規(guī)則都有編譯器執(zhí)行觅闽,如果有錯(cuò)他們將導(dǎo)致編譯錯(cuò)誤而不會(huì)崩潰,ARC有下面一些編譯規(guī)則:
- 你不能實(shí)現(xiàn)或調(diào)用reatin挽牢、release谱煤、autorelease或者reatincount方法,這些在對(duì)象中和方法都是被限制的禽拔,因此[obj release]或者@selector(reatin)會(huì)造成編譯錯(cuò)誤刘离。
- 你能實(shí)現(xiàn)dealloc方法但是不能調(diào)用它,不僅對(duì)于另外的對(duì)象禁用而且父類也是睹栖,所以[super dealloc]是會(huì)編譯錯(cuò)誤的硫惕。你能用CFReatain、CFRelease野来,其他Core Foundation風(fēng)格的方法恼除。
- 你能使用NSAllocateObject或者NSDeallocateObject,使用alloc創(chuàng)建對(duì)象曼氛,runtime負(fù)責(zé)deallocation
- 不能使用C結(jié)構(gòu)指針
- 不能再id和void *類型之間隨便轉(zhuǎn)換豁辉,如果需要,必須強(qiáng)制轉(zhuǎn)換舀患。
- 不能使用NSAutoreleasePool徽级,用autoreleasepool block替代。
- 不能使用NSZone內(nèi)存區(qū)域
- 屬性訪問名(getter)不能以new開頭聊浅,為了確保MRC的互操作性餐抢,示例2-11是演示的。
- 盡管通常有些事情要避免的低匙,但是我們還是可以混合使用ARC和MRC旷痕,我們已近在40頁討論過了
示例2-11 Accessor name with ARC enabled
//Not allowed
@property NSString * newTitle; //Allowed
@property (getter=getNewTitle) NSString * newTitle;
記住這些規(guī)則,我們更新2-5的代碼2-12
示例2-12 Updated code with ARC enabled
-(NSString *) address
{
NSString *result = [[NSString alloc] initWithFormat:@"%@\n%@\n%@, %@", self.line1, self.line2, self.city, self.state]; 1
return result;
}
-(void) showPerson:(Person *) p {
NSString *paddress = [p address];
NSLog(@"Person's Address: %@", paddress);
2
}
- 不需要autorelease顽冶,因?yàn)椴荒苁褂胊utorelease或者retain
- 不能對(duì)paddress調(diào)用release方法
引用類型
ARC介紹了一種新的引用類型:弱引用欺抗,理解可以使用的引用類型對(duì)于內(nèi)存管理是非常重要的,支持的類型是:
強(qiáng)引用:
強(qiáng)引用是默認(rèn)創(chuàng)建的强重,內(nèi)存被一個(gè)強(qiáng)引用引用是不能被釋放的佩迟,強(qiáng)引用讓引用計(jì)數(shù)加1,延長(zhǎng)對(duì)象的聲明周期
弱引用
弱引用是一個(gè)特殊的引用竿屹,他不會(huì)增加引用計(jì)數(shù)报强,(因此不會(huì)延長(zhǎng)對(duì)象的生命周期)弱引用在OC的ARC編程中是非常重要的,接下來我們將討論拱燃。
其他引用類型
OC目前不支持其他類型秉溉,但是你可能對(duì)下面類型感興趣:
軟引用
軟引用是有點(diǎn)像弱引用的,除了他不急于釋放對(duì)象,一個(gè)弱引用對(duì)象在下一次垃圾回收周期中會(huì)被釋放召嘶,但是軟引用對(duì)象通常還會(huì)逗留一段時(shí)間父晶。
虛引用
他是最弱的引用,他是優(yōu)先被清理的弄跌,一個(gè)虛引用對(duì)象和一個(gè)回收對(duì)象是相似的甲喝,但是實(shí)際上內(nèi)存沒有被回收
這些引用類型對(duì)于計(jì)數(shù)系統(tǒng)是不重要的,他們更適用于垃圾回收器
變量修飾符
ARC是介紹了4種生命周期修飾符:
__strong
它是默認(rèn)的修飾符铛只,不要明確提及埠胖,一個(gè)對(duì)象只要有強(qiáng)指針指向他,它就一直保存在內(nèi)存中類似ARC中的retain
__weak
這表明這個(gè)引用不會(huì)一直保持對(duì)象的引用淳玩,當(dāng)沒有強(qiáng)引用指向?qū)ο蟮臅r(shí)候直撤,弱引用將設(shè)置成nil,類似ARC中的assignment(不設(shè)置為nil)操作符蜕着,除了添加一個(gè)安全的指針谋竖,因?yàn)閷?duì)象回收的時(shí)候會(huì)設(shè)置成nil。
__unsafe_unretained
他是和__weak相似的除了當(dāng)沒有強(qiáng)引用指向的時(shí)候不設(shè)置為nil承匣,它類似ARC中的assignment操作符
__autoreleasing
用于id *類型的參數(shù)傳遞蓖乘,當(dāng)參數(shù)在方法中傳遞的時(shí)候希望調(diào)用autorelease。
像下面用修飾符的語法
TypeName * qualifier variable;
示例 2-13 Using variable qualifiers
Person * __strong p1 = [[Person alloc] init]; 1
Person * __weak p2 = [[Person alloc] init]; 2
Person * __unsafe_unretained p3 = [[Person alloc] init]; 3
Person * __autoreleasing p4 = [[Person alloc] init]; 4
- 對(duì)象創(chuàng)建的時(shí)候引用計(jì)數(shù)為1韧骗,對(duì)象直到p1指正釋放引用才被回收驱敲。
- 對(duì)象創(chuàng)建的時(shí)候引用計(jì)數(shù)為0,將會(huì)立刻被回收并且p2被設(shè)置nil
- 對(duì)象創(chuàng)建的時(shí)候引用計(jì)數(shù)為1宽闲,將會(huì)立刻被回收但是p2不會(huì)被設(shè)置為nil
- 對(duì)象創(chuàng)建的時(shí)候引用計(jì)數(shù)為1,當(dāng)方法返回的時(shí)候會(huì)自動(dòng)釋放一次握牧。
屬性修飾符
引入了兩個(gè)新的所有權(quán)修飾符屬性聲明容诬,strong和weak,assign的語義也更新了沿腰,總之現(xiàn)在有6個(gè)修飾符:
strong:默認(rèn)的览徒,表示一個(gè)__strong 關(guān)系
weak:便是一個(gè)__weak關(guān)系
assign:它不是一個(gè)新的修飾符,但是語義已經(jīng)改變了颂龙,在ARC之前assign是默認(rèn)的擁有關(guān)系修飾符习蓬,在ARC之后assign是__unsafe_untetained
copy:意味著__strong關(guān)系,此外措嵌,他意味著setter中的復(fù)制語義躲叼。
retain:表示__strong關(guān)系
unsafe_unretained:表示__unsafe_unretained關(guān)系
示例2-14展示了這些修飾符的例子,因?yàn)閍ssign和unsafe_untetained只復(fù)制了值沒有進(jìn)行完整性檢查企巢,它應(yīng)該只能用于值類型(BOOL,NSInteger枫慷、NSUInteger等)避免用于引用類型,特別是NSString*和UIView *指針。
示例 2-14 Using property qualifiers
@property (nonatomic, strong) IBOutlet UILabel *titleView;
@property (nonatomic, weak) id<UIApplicationDelegate> appDelegate;
@property (nonatomic, assign) UIView *danglingReference; 1
@property (nonatomic, assign) BOOL selected; 2
@property (nonatomic, copy) NSString *name;
@property (nonatomic, retain) HPPhoto *photo; 3
@property (nonatomic, unsafe_unretained) UIView *danglingReference;
- assign錯(cuò)誤的指向一個(gè)指針或听。
- assign正確的用于值類型
- retain是ARC前期的產(chǎn)物探孝,現(xiàn)在很少使用了,這里只是為了例子的完整性誉裆。
未完 待續(xù)顿颅。。足丢。