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