自動引用計數(shù)和@autoreleasepool

1、[NSObject alloc]在創(chuàng)建完對象后,會讓該對象的retainCount+1,后續(xù)的init為初始化該對象屬性的函數(shù)宠页。
若alloc成功后,不把該地址引用賦值給某個指針變量NSObject *obj寇仓,則該新創(chuàng)建的對象的引用計數(shù)始終為1举户。
則整個程序運(yùn)行的聲明周期內(nèi),該內(nèi)存都不會被回收遍烦,malloc俭嘁、new也是同樣的道理,所以會引起內(nèi)存泄露服猪。

2供填、ARC下雖然不能直接調(diào)用retainCount查看引用計數(shù),但也可以通過以下幾種方式work round罢猪。

方法一:[obj valueForKey:@"retainCount"];
這個方法測了一下經(jīng)常不準(zhǔn)近她,但好處是在引用計數(shù)==0,obj被回收以后不會報錯膳帕,另外兩種會報錯泄私。

方法二:OBJC_EXTERN int _objc_rootRetainCount(id);
_objc_rootRetainCount(obj);

方法三:CFGetRetainCount((__bridge CFTypeRef)obj);

============================================

關(guān)于autoreleasepool

自動釋放池是NSAutoreleasePool的實(shí)例,其中包含了收到autorelease消息的對象备闲。當(dāng)一個自動釋放池自身被銷毀(dealloc)時,它會給池中每一個對象發(fā)送一個release消息捅暴。
如果你給一個對象多次發(fā)送autorelease消息恬砂,那么當(dāng)自動釋放池銷毀時,這個對象也會收到同樣數(shù)目的release消息蓬痒。
需要注意的是:如果在delloc里面沒有調(diào)用[super delloc]泻骤,這樣做沒有問題,只是delloc被多次調(diào)用。但如果有[super delloc]狱掂,則第二次重復(fù)調(diào)用會產(chǎn)生exc_bad_access或者signal sigabrt演痒。所以還是不要在一個autoreleasepool里對同一個對象做一次以上的autorelease函數(shù)調(diào)用,何況這么做沒有任何意義趋惨。
可以看出鸟顺,一個自動釋放的對象,它至少能夠存活到自動釋放池銷毀的時候器虾。這樣看來它是一種延遲釋放機(jī)制讯嫂,這樣保證局部堆上的變量能夠被外部正常使用。

以下寫法也是一樣生效的:

NSObject *obj = [NSObject new];
@autoreleasepool{
    [obj autorelease];
// 1. [obj release];
// 2. [obj retain];
}
// 到這里的時候obj就被釋放了

可見并不是必須在alloc的時候調(diào)用autorelease兆沙,也不是必須在@autoreleasepool內(nèi)部調(diào)用alloc創(chuàng)建對象欧芽,總之只要是在@autoreleasepool內(nèi)部調(diào)用過autorelease的對象,都會在@autoreleasepool作用域結(jié)束的時候調(diào)用release葛圃,僅此而已千扔。
如果在注釋1的位置直接調(diào)用release,那么obj會因?yàn)閞etainCount==0立刻被回收库正,而不會等到autoreleasepool作用域結(jié)束曲楚,雖然在那是又執(zhí)行一遍release,但是沒有意義了诀诊。
如果在注釋2的位置再次調(diào)用retain洞渤,那么就是到autoreleasepool作用域結(jié)束調(diào)用一次release,那么因?yàn)閞etainCount==1, obj對象引用的內(nèi)存還是不會回收属瓣,要么在調(diào)用一次release载迄,要么再調(diào)用一次autorelease。
也就是說抡蛙,alloc/new/copy/retain是一組增加retainCount的护昧,release和autoRelease是一組減少retainCount的,要釋放內(nèi)存第一組和第二組的調(diào)用次數(shù)要完全一致粗截。

小結(jié):

  • 只有在自動釋放池中調(diào)用了對象的autorelease方法,這個對象才會被存儲到這個自動釋放池之中.

  • 如果只是將對象的創(chuàng)建代碼寫在自動釋放之中,而沒有調(diào)用對象的autorelease方法.是不會將這個對象存儲到這個自動釋放池之中的.
    對象的創(chuàng)建可以在自動釋放池的外面,在自動釋放池之中,調(diào)用對象的autorelease方法,就可以將這個對象存儲到這個自動釋放池之中.

  • 如果對象的autorelease方法的調(diào)用放在自動釋放池的外面,是無法將其存儲的這個自動釋放池之中的.

  • autorelease 的調(diào)用只有放在自動釋放池之中 才可以講其存儲到自動釋放池之中, 對象的創(chuàng)建可以在外面

  • 當(dāng)自動釋放池結(jié)束的時候.僅僅是對存儲在自動釋放池中的對象發(fā)送1條release消息 而不是銷毀對象.

  • 如果在自動釋放池中,調(diào)用同1個對象的autorelease方法多次.就會將對象存儲多次到自動釋放池之中.
    在自動釋放池結(jié)束的時候.會為對象發(fā)送多條release消息.
    所以,1個自動釋放池之中,只autorelease1次,只將這個對象放1次, 否則就會出現(xiàn)野指針錯誤.

  • 如果在自動釋放池中,調(diào)用了存儲到自動釋放中的對象的release方法.
    在自動釋放池結(jié)束的時候,還會再調(diào)用對象的release方法.
    這個時候就有有可能會造成野指針操作.

  • 將對象存儲到自動釋放池,并不會使對象的引用計數(shù)器+1 所以其好處就是:創(chuàng)建對象將對象存儲在自動釋放池,就不需要在寫個release了.

  • 自動釋放池可以嵌套.

  • 調(diào)用對象的autorelease方法,會講對象加入到當(dāng)前自動釋放池之中
    只有在當(dāng)前自動釋放池結(jié)束的時候才會像對象發(fā)送release消息.

============================================

在創(chuàng)建對象的時候惋耙,調(diào)用autorelease,就能將該對象放到autoreleasepool中熊昌。利用autoreleasepool的延遲釋放來管理內(nèi)存绽榛。autoreleasepool這么重要,可是我們在實(shí)際開發(fā)中并沒有手動創(chuàng)建autoreleasepool婿屹,卻沒有內(nèi)存泄露灭美。這是為什么呢?其實(shí)沒有手動創(chuàng)建并不代表它不會被創(chuàng)建昂利,那么它是什么時候創(chuàng)建的呢届腐?

iOS程序默認(rèn)在main函數(shù)中會加入@autoreleasepool铁坎,但這個基本沒什么用,因?yàn)樗淖饔糜蚴钦麄€main函數(shù)結(jié)束犁苏,也就是說延遲release的時間點(diǎn)是程序退出之前硬萍,那么既然程序都退出了,所有資源和內(nèi)存都已經(jīng)被操作系統(tǒng)回收了围详,還釋放個什么勁捌庸浴?

所以實(shí)際上iOS程序在運(yùn)行過程中短曾,遠(yuǎn)不止我們自己實(shí)現(xiàn)的代碼里會添加@autoreleasepool寒砖,autoreleasepool的數(shù)據(jù)存儲結(jié)構(gòu)是一個嵌套結(jié)構(gòu),也就是說可以在一個autoreleasepool內(nèi)部嵌套另一個autoreleasepool并且可以無限嵌套下去嫉拐。這有點(diǎn)類似函數(shù)的棧變量壓棧和出棧的邏輯哩都。所以runtime自動調(diào)用和創(chuàng)建的autoreleasepool對程序開發(fā)者來說基本是透明的。

App啟動后婉徘,系統(tǒng)在主線程runLoop里注冊兩個Observser,其回調(diào)都是_wrapRunLoopWithAutoreleasePoolHandler()漠嵌。第一個 Observer 監(jiān)視的事件是 Entry(即將進(jìn)入Loop),其回調(diào)內(nèi)會調(diào)用 _objc_autoreleasePoolPush() 創(chuàng)建自動釋放池盖呼。其優(yōu)先級最高儒鹿,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前。第二個 Observer 監(jiān)視了兩個事件: BeforeWaiting(準(zhǔn)備進(jìn)入休眠) 時調(diào)用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池并創(chuàng)建新池几晤;Exit(即將退出Loop) 時調(diào)用 _objc_autoreleasePoolPop() 來釋放自動釋放池约炎。這個 Observer 優(yōu)先級最低,保證其釋放池子發(fā)生在其他所有回調(diào)之后蟹瘾。在主線程執(zhí)行的代碼圾浅,通常是寫在諸如事件回調(diào)、Timer回調(diào)內(nèi)的憾朴。這些回調(diào)會被runLoop創(chuàng)建好的AutoreleasePool環(huán)繞著狸捕。這就解釋了,為什么即使程序中只有main函數(shù)的最外層設(shè)置autoreleasepool众雷,所有的內(nèi)存也不是在main運(yùn)行完后一股腦一起釋放的原因灸拍,可以說最外層的autoreleasepool只是為了保護(hù)所有沒有被內(nèi)建的autoreleasepool包裹住的部分內(nèi)存自動回收用的。

通過下面的例子砾省,我們來看一下鸡岗,runLoop創(chuàng)建的autoreleasepool是不是真的幫我們管理了內(nèi)存。

__weak id reference = nil;  
- (void)viewDidLoad {  
    [super viewDidLoad];  
    NSString *str = [NSString stringWithFormat:@"autoreleasePool"];  
    // str是一個autorelease對象编兄,設(shè)置一個weak的引用來觀察它  
    reference = str;  
    NSLog(@"%@", reference); // Console: autoreleasePool  
}  
- (void)viewWillAppear:(BOOL)animated {  
    [super viewWillAppear:animated];  
    NSLog(@"%@", reference); // Console: autoreleasePool  
}  
- (void)viewDidAppear:(BOOL)animated {  
    [super viewDidAppear:animated];  
    NSLog(@"%@", reference); // Console: (null)  
}  

這個實(shí)驗(yàn)同時也證明了viewDidLoad和viewWillAppear是在同一個runloop調(diào)用的轩性,而viewDidAppear是在之后的某個runloop調(diào)用的。由于這個vc在loadView之后便add到了window層級上翻诉,所以viewDidLoad和viewWillAppear是在同一個runloop調(diào)用的炮姨,因此在viewWillAppear中,這個autorelease的變量依然有值碰煌。
當(dāng)然舒岸,我們也可以不用等到當(dāng)前runLoop結(jié)束,選擇手動干預(yù)Autorelease對象的釋放時機(jī):

- (void)viewDidLoad {  
    [super viewDidLoad];  
    @autoreleasepool {  
        NSString *str = [NSString stringWithFormat:@"autoreleasePool"];  
    }  
    NSLog(@"%@", str); // Console: (null)  
}  

通過上面的例子芦圾,可以看出蛾派,沒有調(diào)用release也做到了內(nèi)存管理「錾伲可是大家注意到了洪乍,str對象沒有調(diào)用autorelease方法啊,怎么被放到autoreleasepool進(jìn)行管理的呢夜焦?其實(shí)靜態(tài)方法已經(jīng)在內(nèi)部自動調(diào)用了autorelease方法壳澳,所有這里不需要再調(diào)用。
更進(jìn)一步講茫经,ARC在進(jìn)行內(nèi)存釋放管理的時候巷波,如果某個對象是在獨(dú)立作用域中創(chuàng)建并且使用,沒有外層作用域?qū)λ囊玫脑?沒有外層作用域指針指向卸伞,也沒有從函數(shù)返回值中返回)抹镊,則很可能編譯的時候使用release立刻釋放。但若不是這樣荤傲,則一定使用autorelease來減少復(fù)雜度垮耳,比如上面的stringWithFormat靜態(tài)方法,就是在其函數(shù)內(nèi)執(zhí)行了[NSString alloc]遂黍,但其創(chuàng)建對象的引用作為返回值進(jìn)行了返回终佛,所以必然會添加autorelease,因?yàn)槿绻鞘褂胷elease的話妓湘,由于編譯器必須在return函數(shù)前面來調(diào)用release函數(shù)查蓉,而調(diào)用release函數(shù)后引用計數(shù)==0立刻釋放了該對象,則無論如何都會返回一個已經(jīng)被釋放的野指針作為函數(shù)返回值榜贴,唯一的辦法就只能是把release延遲調(diào)用豌研,來起碼等到調(diào)用者拿到對象的引用并決定是weak還是strong之后再延遲釋放。

=========================================
繼續(xù)我們的主題唬党。我們知道autoreleasepool是一個自動釋放池鹃共,那么它到底是一個什么樣的數(shù)據(jù)結(jié)構(gòu)呢?我們在命令行中使用 clang -rewrite-objc main.m 讓編譯器重新改寫這個文件驶拱,編譯完后霜浴,會在該文件目錄下多一個.cpp文件。打開這個文件蓝纲。滾到最底部阴孟∩稳遥可以看到如下代碼:(刪除掉多余的代碼)
很遺憾的是這里只能看到autoreleasepool被編譯的部分,如果還有NSObject *obj = [[NSObject alloc] init];這種定義永丝,是看不到編譯器自動在最后加上的[obj release]調(diào)用的锹漱,可能是這部分屬于llvm的特性,用clang的重寫是看不到的慕嚷。

int main(int argc, const charchar * argv[]) {  
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;  
    }  
    return 0;  
}  

在這個文件中哥牍,有一個非常奇怪的 __AtAutoreleasePool 的結(jié)構(gòu)體,前面的注釋寫到/* @autoreleasepopl */ 喝检。也就是說@autoreleasepool {} 被轉(zhuǎn)換為:__AtAutoreleasePool __autoreleasepool嗅辣。那么__AtAutoreleasePool又是什么?在文件中可以找到__AtAutoreleasePool數(shù)據(jù)結(jié)構(gòu)如下:

struct __AtAutoreleasePool {  
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}  
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}  
  voidvoid * atautoreleasepoolobj;  
};  

它是一個結(jié)構(gòu)體挠说,該結(jié)構(gòu)體會在初始化時調(diào)用 objc_autoreleasePoolPush() 方法澡谭,會在析構(gòu)時調(diào)用objc_autoreleasePoolPop 方法。所以我們可以進(jìn)一步將main函數(shù)中的代碼改寫為如下:

int main(int argc, const charchar * argv[]) {    
    {  
        voidvoid * atautoreleasepoolobj = objc_autoreleasePoolPush();  
  
        // do whatever you want  
  
        objc_autoreleasePoolPop(atautoreleasepoolobj);  
    }  
    return 0;  
}  

@autoreleasepool 只是幫助我們少寫了這兩行代碼而已纺涤,讓代碼看起來更美觀译暂,然后要根據(jù)上述兩個方法來分析自動釋放池的實(shí)現(xiàn)。objc_autoreleasePoolPush 和 objc_autoreleasePoolPop 的實(shí)現(xiàn):

voidvoid *objc_autoreleasePoolPush(void) {    
    return AutoreleasePoolPage::push();  
}  
  
void objc_autoreleasePoolPop(voidvoid *ctxt) {    
    AutoreleasePoolPage::pop(ctxt);  
}  

__AtAutoreleasePool的Push和Pop方法看上去方法看上去是對 AutoreleasePoolPage對應(yīng)靜態(tài)方法push和pop的封裝撩炊。
那么AutoreleasePoolPage又是一個什么東東呢外永?,它的定義可以在NSObject.mm文件中看到拧咳,定義如下:

class AutoreleasePoolPage {    
    magic_t const magic;  
    idid *next;  
    pthread_t const thread;  
    AutoreleasePoolPage * const parent;  
    AutoreleasePoolPage *child;  
    uint32_t const depth;  
    uint32_t hiwat;  
};  

=========================================

以上的過程說明了autoreleasepool可以由系統(tǒng)調(diào)用自動多次嵌套伯顶,而且只對調(diào)用了autorelease的對象起作用。
ARC是在編譯階段由LLVM自動加入reatin骆膝,release祭衩,autorelease等操作函數(shù),那么對于棧作用域的函數(shù)內(nèi)部變量來說阅签,會在函數(shù)退出之前對局部變量引用的對象執(zhí)行release操作掐暮,當(dāng)retainCount==0的時候立刻執(zhí)行回收內(nèi)存操作。而@autoreleasepool可以讓這個操作提前政钟,例如:

- (void) doSomething {

__weak NSMutableArray *wcollection;
@autoreleasepool{

     NSMutableArray *collection = @[].mutableCopy;
     wcollection = collection;

     for (int i = 0; i < 10e6; ++i) {

        NSString *str = [NSString stringWithFormat:@"hi + %d", i];

        [collection addObject:str];

    }
    NSLog(@"%d",_objc_rootRetainCount(collection)); //打印1
    NSLog(@"finished!");
}
NSLog(@"%d",[wcollection valueForKey:@"retainCount"]); //打印0
     // 其他的代碼
    NSLog(@"completed!");
}

以上的例子中路克,如果不使用局部的@autoreleasepool(但是還是要保留{}以減小collection的作用域),則collection引用的對象雖然在{}作用域的最后會被release养交,并且將其中add過的str對象都做release操作精算,但由于str在通過靜態(tài)方法創(chuàng)建的時候進(jìn)行了一次持有并且用autorelease釋放,所以str還占用的內(nèi)存要在autoreleasepool drain的時候才能釋放碎连。另外還能確定在超過函數(shù)作用域的對象多次引用(比如doSomething中return collection)灰羽,或者多線程并發(fā)引用同一個外部對象的情況下,想通過添加release第一時間釋放的難度太大,所以ARC會選擇使用autorelease廉嚼,在更外層更好控制的地方使用autoreleasepool進(jìn)行延遲釋放玫镐。

而如果在collection使用的外部套上@autoreleasepool,則執(zhí)行完成后會立刻釋放str所占用的內(nèi)存怠噪,也就是說會降低整個函數(shù)執(zhí)行過程中摘悴,內(nèi)存占用的峰值。另外大量的釋放內(nèi)存也會占用不少cpu時間舰绘,所以在打印完finished!以后,到//其他代碼執(zhí)行之前葱椭,會卡住一段時間捂寿,從執(zhí)行監(jiān)視器視圖看,這段時間內(nèi)程序占用的內(nèi)存會持續(xù)下降孵运。

for(int i=0;i<10e6;i++){
[[NSString alloc] initWithFormat:@"hi:%d",i];
__weak NSString *str = [[NSString alloc] initWithFormat:@"hi:%d",i];
NSLog(@"%@",str); // 打印nil
NSString *str2 = [[NSString alloc] initWithFormat:@"hi:%d",i];
NSLog(@"%@",str2);// 打印字符串
}

ARC的情況下秦陋,第一種寫法編譯器會立刻優(yōu)化為[[[NSString alloc] initWithFormat:@"hi:%d",i] release],所以雖然cpu在不斷的執(zhí)行和創(chuàng)建銷毀對象治笨,但內(nèi)存不變化驳概。
第二種寫法用弱引用接收跟不接收一下不能增加引用計數(shù),則同理旷赖。打印為nil
第三種寫法有區(qū)別顺又,可以打印出字符串,編譯器會在{}作用域的最后一行加上[str2 release]等孵,而不是alloc之后立刻回收稚照。
可以想象為一個虛擬的強(qiáng)引用指針
NSString *temp = [alloc init];
第一種寫法什么都不做
第二種寫法_weak NSString *str = temp;
第三種寫法NSString *str2 = temp;
[temp release];
// alloc之后的代碼

MRC的情況下就比較悲劇了,__weak在MRC下不可用編譯不通過,另外兩種寫法都會因?yàn)闆]有release讓內(nèi)存持續(xù)增長俯萌,尤其是第一種寫法果录,想release都沒有辦法。

=========================================

__autoreleasing修飾符的作用

切換到ARC之后咐熙,每個指向OC對象的指針弱恒,都被賦上了所有權(quán)修飾符。一共有__strong棋恼、__weak返弹、__unsafe_unretained和__autoreleasing這樣四種所有權(quán)修飾符。

當(dāng)一個對象被賦值給一個使用__autoreleasing修飾符修飾的指針時蘸泻,相當(dāng)于這個對象在MRC下被發(fā)送了autorelease消息琉苇,也就是說它被注冊到了autorelease pool中。

全局變量和實(shí)例變量是無法用__autoreleasing來修飾的悦施,不然編譯器會報錯并扇。
而局部變量用__autoreleasing修飾后,其指向的對象抡诞,在當(dāng)前autorelease pool結(jié)束之前不會被回收穷蛹,即使其生命的作用域結(jié)束土陪。也就是說__autoreleasing讓編譯器改變了默認(rèn)的處理原則。

__weak NSObject *weakObj1;
__weak NSObject *weakObj2;

{
    __autoreleasing NSObject *obj1 = [[NSObject alloc] init];
    weakObj1 = obj1; //weakObj1指向的對象已被注冊到autorelease pool

    __strong NSObject *obj2 = [[NSObject alloc] init];
    weakObj2 = obj2;//weakObj2指向的對象沒有被注冊到autorelease pool
}
//局部變量obj1和obj2的作用域結(jié)束肴熏,
//此時weakObj2指向的對象不再被強(qiáng)引用鬼雀,因此被回收;
//而obj1指向的對象仍然在autorelease pool中

NSLog(@"%@", weakObj1);//輸出<NSObject: 0x100206030>蛙吏,因?yàn)榇丝蘷eakObj1在autorelease pool中源哩,不會因?yàn)閛bj1作用域的結(jié)束而被回收
NSLog(@"%@", weakObj2);//輸出null

=========================================

方法名檢查

@interface XSQObject : NSObject

+ (NSString *)newHelloWorldString;
+ (NSString *)allocHelloWorldString;
+ (NSString *)copyHelloWorldString;

+ (NSString *)helloWorldString;
+ (NSString *)initHelloWorldString;
+ (NSString *)shitHelloWorldString;

@end

@implementation XSQObject

+ (NSString *)newHelloWorldString {
    return [[NSString alloc] initWithCString:"HelloWorld" encoding:NSUTF8StringEncoding];
}
+ (NSString *)allocHelloWorldString {
    return [[NSString alloc] initWithCString:"HelloWorld" encoding:NSUTF8StringEncoding];
}
+ (NSString *)copyHelloWorldString {
    return [[NSString alloc] initWithCString:"HelloWorld" encoding:NSUTF8StringEncoding];
}

+ (NSString *)helloWorldString {
    return [[NSString alloc] initWithCString:"HelloWorld" encoding:NSUTF8StringEncoding];
}
+ (NSString *)initHelloWorldString {
    return [[NSString alloc] initWithCString:"HelloWorld" encoding:NSUTF8StringEncoding];
}
+ (NSString *)shitHelloWorldString {
    return [[NSString alloc] initWithCString:"HelloWorld" encoding:NSUTF8StringEncoding];
}

@end

int main(int argc, char * argv[]) {
    @autoreleasepool {
        
        __weak NSString *newHelloWorldString = [XSQObject newHelloWorldString];
        __weak NSString *allocHelloWorldString = [XSQObject allocHelloWorldString];
        __weak NSString *copyHelloWorldString = [XSQObject copyHelloWorldString];
        //上面三個都有warning:
        //Assigning retained object to weak variable; object will be released after assignment
        NSLog(@"%@", newHelloWorldString);//輸出null
        NSLog(@"%@", allocHelloWorldString);//輸出null
        NSLog(@"%@", copyHelloWorldString);//輸出null
        
        __weak NSString *helloWorldString = [XSQObject helloWorldString];
        __weak NSString *initHelloWorldString = [XSQObject initHelloWorldString];
        __weak NSString *shitHelloWorldString = [XSQObject shitHelloWorldString];
        //上面三個沒有warning
        
        NSLog(@"%@", helloWorldString);//輸出HelloWorld
        NSLog(@"%@", initHelloWorldString);//輸出HelloWorld
        NSLog(@"%@", shitHelloWorldString);//輸出HelloWorld
        
    }
    return 0;
}

雖然[XSQObject helloWorldString]和[XSQObject newHelloWorldString]兩個方法的實(shí)現(xiàn)完全一樣,但是它們返回的對象被賦值給__weak指針后鸦做,前者仍然存在励烦,而后者則被銷毀了。如果再加入@autorelease塊做點(diǎn)實(shí)驗(yàn)泼诱,可以發(fā)現(xiàn)helloWorldString指向的對象其實(shí)已被注冊到autorelease pool中坛掠。

對比內(nèi)存管理原則,這就像是在MRC下治筒,不以alloc/new/copy/mutableCopy開頭的方法屉栓,會對返回的對象發(fā)送autorelease消息一樣。而事實(shí)上耸袜,在ARC下友多,編譯器會檢查方法名是否以alloc/new/copy/mutableCopy開頭,如果不是堤框,則自動將返回的對象注冊到autorelease pool中夷陋。

在一些特殊的情況下,程序員也可以手動給某些方法加上其他標(biāo)記胰锌,來覆蓋被編譯器隱式加上的標(biāo)記骗绕。

需要注意的一點(diǎn)是,clang的ARC文檔描述如下:

Methods in the alloc, copy, init, mutableCopy, and new families are implicitly marked attribute((ns_returns_retained)). This may be suppressed by explicitly marking the method attribute((ns_returns_not_retained)).

但其實(shí)還有一個更詳細(xì)的默認(rèn)規(guī)則限定资昧,那就是init用作實(shí)例方法的開頭酬土,其他的用作靜態(tài)方法的開頭。這就是為什么上面的例子中用init開頭的靜態(tài)方法卻沒有警告和預(yù)期結(jié)果的原因格带。

=========================================

更詳細(xì)的內(nèi)容可以參考
http://blog.csdn.net/shxwork/article/details/51437003
http://blog.csdn.net/shxwork/article/details/51363306
http://www.reibang.com/p/1b66c4d47cd7 (強(qiáng)烈推薦)
http://www.reibang.com/p/32265cbb2a26(強(qiáng)烈推薦)
http://www.reibang.com/p/ec2c854b2efd

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末撤缴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子叽唱,更是在濱河造成了極大的恐慌屈呕,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棺亭,死亡現(xiàn)場離奇詭異虎眨,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門嗽桩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來岳守,“玉大人,你說我怎么就攤上這事碌冶∈。” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵扑庞,是天一觀的道長譬重。 經(jīng)常有香客問我,道長罐氨,這世上最難降的妖魔是什么害幅? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮岂昭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘狠怨。我一直安慰自己约啊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布佣赖。 她就那樣靜靜地躺著恰矩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪憎蛤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機(jī)與錄音泰佳,去河邊找鬼纲熏。 笑死,一個胖子當(dāng)著我的面吹牛棚辽,可吹牛的內(nèi)容都是我干的技竟。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼屈藐,長吁一口氣:“原來是場噩夢啊……” “哼榔组!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起联逻,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤搓扯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后包归,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锨推,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了谭贪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡锦担,死狀恐怖俭识,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情洞渔,我是刑警寧澤套媚,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站磁椒,受9級特大地震影響堤瘤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜浆熔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一本辐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧医增,春花似錦慎皱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至忽刽,卻和暖如春天揖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背跪帝。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工今膊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人伞剑。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓万细,卻偏偏與公主長得像,于是被迫代替她去往敵國和親纸泄。 傳聞我的和親對象是個殘疾皇子赖钞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

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