高性能iOS翻譯-第2章:性能測(cè)量

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
} }

  1. AClass類有兩個(gè)屬性
  2. anInteger是一個(gè)NSInteger類型郁油,值傳遞
  3. aString是一個(gè)NSString類型本股,引用傳遞
  4. createAClassWithInteger: string:方法用于實(shí)例化AClass,這個(gè)方法提供創(chuàng)建對(duì)象的必要值桐腌。
  5. 值i是在在棧上面的拄显,當(dāng)賦值給屬性的時(shí)候他必須從棧上復(fù)制到堆,因?yàn)檫@是結(jié)果存儲(chǔ)的位置案站。
  6. 盡管NSString *是引用傳遞躬审,這個(gè)屬性是被標(biāo)記為copy,意味著這個(gè)值必須被復(fù)制或者克隆蟆盐,依賴于-NSCopying copyWithZone:方法是怎么實(shí)現(xiàn)的承边。
  7. somethod:方法用于處理AClass對(duì)象中的一個(gè)數(shù)組。
  8. 當(dāng)anInteger被調(diào)用的時(shí)候石挂,他的值必須在使用之前復(fù)制到棧中博助,這個(gè)值被加到total中。
  9. 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
  1. 對(duì)象創(chuàng)建筒主,message擁有對(duì)象座柱,引用計(jì)數(shù)為1
  2. messageRetained擁有對(duì)像,引用計(jì)數(shù)為2
  3. messageRetained釋放擁有權(quán)物舒,引用計(jì)數(shù)為1
  4. message釋放擁有權(quán),引用計(jì)數(shù)減到0
  5. 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
}
  1. 對(duì)象被創(chuàng)建锦针,result的引用計(jì)數(shù)為1
  2. 被paddress引用之后result的引用計(jì)數(shù)還是1荠察,因?yàn)閟howPerson:是對(duì)象所有者創(chuàng)建使用地址按鈕,它不應(yīng)該擁有result奈搜;
  3. 釋放所有權(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
}
  1. 沒有使用alloc方法
  2. 在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)行分析:

  1. 你擁有通過alloc創(chuàng)建的返回對(duì)象
  2. 為了確保沒有內(nèi)存泄露,必須在失去引用前釋放擁有權(quán)
  3. 因此如果使用了release 對(duì)象將會(huì)在方法返回前被釋放嵌牺,結(jié)果就是返回一個(gè)不可用的引用
  4. 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];
            }
} }
}
  1. 這個(gè)代碼是不好的因?yàn)樗挥幸粋€(gè)autoreleasepool 內(nèi)存清理在所有的迭代完成之后右莱。
  2. 在這個(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
}
  1. 不需要autorelease顽冶,因?yàn)椴荒苁褂胊utorelease或者retain
  2. 不能對(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
  1. 對(duì)象創(chuàng)建的時(shí)候引用計(jì)數(shù)為1韧骗,對(duì)象直到p1指正釋放引用才被回收驱敲。
  2. 對(duì)象創(chuàng)建的時(shí)候引用計(jì)數(shù)為0,將會(huì)立刻被回收并且p2被設(shè)置nil
  3. 對(duì)象創(chuàng)建的時(shí)候引用計(jì)數(shù)為1宽闲,將會(huì)立刻被回收但是p2不會(huì)被設(shè)置為nil
  4. 對(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;
  1. assign錯(cuò)誤的指向一個(gè)指針或听。
  2. assign正確的用于值類型
  3. retain是ARC前期的產(chǎn)物探孝,現(xiàn)在很少使用了,這里只是為了例子的完整性誉裆。

未完 待續(xù)顿颅。。足丢。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末粱腻,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子霎桅,更是在濱河造成了極大的恐慌栖疑,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件滔驶,死亡現(xiàn)場(chǎng)離奇詭異遇革,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)揭糕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門萝快,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人著角,你說我怎么就攤上這事揪漩。” “怎么了吏口?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵奄容,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我产徊,道長(zhǎng)昂勒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任舟铜,我火速辦了婚禮戈盈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谆刨。我一直安慰自己塘娶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布痊夭。 她就那樣靜靜地躺著刁岸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪她我。 梳的紋絲不亂的頭發(fā)上难捌,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天膝宁,我揣著相機(jī)與錄音,去河邊找鬼根吁。 笑死员淫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的击敌。 我是一名探鬼主播介返,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼沃斤!你這毒婦竟也來了圣蝎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤衡瓶,失蹤者是張志新(化名)和其女友劉穎徘公,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體哮针,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡关面,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了十厢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片等太。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蛮放,靈堂內(nèi)的尸體忽然破棺而出搞隐,到底是詐尸還是另有隱情弄诲,我是刑警寧澤辙芍,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布胞枕,位于F島的核電站,受9級(jí)特大地震影響娩嚼,放射性物質(zhì)發(fā)生泄漏蘑险。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一待锈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嘴高,春花似錦竿音、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至套啤,卻和暖如春宽气,著一層夾襖步出監(jiān)牢的瞬間随常,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工萄涯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绪氛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓涝影,卻偏偏與公主長(zhǎng)得像枣察,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子燃逻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 29.理解引用計(jì)數(shù) Objective-C語言使用引用計(jì)數(shù)來管理內(nèi)存序目,也就是說,每個(gè)對(duì)象都有個(gè)可以遞增或遞減的計(jì)數(shù)...
    Code_Ninja閱讀 1,487評(píng)論 1 3
  • 內(nèi)存管理 簡(jiǎn)述OC中內(nèi)存管理機(jī)制伯襟。與retain配對(duì)使用的方法是dealloc還是release猿涨,為什么?需要與a...
    丶逐漸閱讀 1,963評(píng)論 1 16
  • iOS內(nèi)存管理 概述 什么是內(nèi)存管理 應(yīng)用程序內(nèi)存管理是在程序運(yùn)行時(shí)分配內(nèi)存(比如創(chuàng)建一個(gè)對(duì)象,會(huì)增加內(nèi)存占用)與...
    蚊香醬閱讀 5,721評(píng)論 8 119
  • 內(nèi)存管理是程序在運(yùn)行時(shí)分配內(nèi)存姆怪、使用內(nèi)存叛赚,并在程序完成時(shí)釋放內(nèi)存的過程。在Objective-C中片效,也被看作是在眾...
    蹲瓜閱讀 3,070評(píng)論 1 8
  • 1.1 什么是自動(dòng)引用計(jì)數(shù) 概念:在 LLVM 編譯器中設(shè)置 ARC(Automaitc Reference Co...
    __silhouette閱讀 5,151評(píng)論 1 17