[iOS] @autoreleasepool是干神馬的

首先我們先看個好玩的事情~

#import "ViewController2.h"

@interface ViewController2 () {
    __weak id tracePtr;
}

@end

@implementation ViewController2

- (void)viewDidLoad {
    [super viewDidLoad];

    NSString *str = [NSString stringWithFormat:@"%@", @"ssuuuuuuuuuuuuuuuuuuuu"];
    tracePtr = str;
}

- (void)viewWillAppear:(BOOL)animated {
    NSLog(@"viewWillAppear tracePtr: %@", tracePtr);
}

- (void)viewDidAppear:(BOOL)animated {
    NSLog(@"viewDidAppear tracePtr: %@", tracePtr);
}

@end

看到上面的代碼,猜測一下輸出會是什么呢仰挣?我最開始的想法應該都是null豆茫,因為tracePtr是弱指針馋劈,str在viewDidLoad結(jié)束以后就沒有引用計數(shù)了,應該被回收掉偶洋,所以在viewWillAppear和viewDidAppear中再打印的時候應該就空啦熟吏。

但是實際上打印了什么嘞?

Example1[10896:167819] viewWillAppear tracePtr: ssuuuuuuuuuuuuuuuuuuuu
Example1[10896:167819] viewDidAppear tracePtr: (null)

是不是灰常神奇玄窝,在viewWillAppear的時候str的內(nèi)存仍舊沒有被清空牵寺。這是為什么呢?


autorelease

上面的問題一會兒再解決恩脂,我們先了解一下autorelease相關(guān)的方法哈帽氓。在MRC時代我們需要自己手動管理內(nèi)存,當對象不用了以后俩块,需要調(diào)用[obj release]來釋放內(nèi)存黎休,但有的時候我們不希望它馬上釋放,需要它等一會兒在釋放玉凯,例如作為函數(shù)返回值:

- (Person *)createPerson {
  return [[[Person alloc] init] autorelease];
}

如果不加autorelease势腮,直接返回一個新的person,那么由于alloc init會加一次引用計數(shù)漫仆,無論怎么也無法抵消捎拯,除非alloc后調(diào)用release或者讓外部release兩次,但依賴調(diào)用者release是很不容錯的盲厌;而如果馬上release署照,外部調(diào)用這個方法的拿到的就是nil了祸泪,所以這里用autorelease。

autorelease會將對象放到一個自動釋放池中藤树,當自動釋放池被銷毀時浴滴,會對池子里面的所有對象做一次release操作。也就是調(diào)用后不是馬上計數(shù)-1岁钓,而是在自動釋放池銷毀時再-1升略。

這樣的話當外部createPerson以后是可以獲取到一個person的,如果使用了另外的引用指向person屡限,person的引用數(shù)暫時為2品嚣,而自動釋放池銷毀時,會對person執(zhí)行一次release钧大,它的計數(shù)就變?yōu)榱?翰撑,由于仍舊有引用就不會被銷毀;如果外部沒有建新的引用啊央,那么在自動釋放池銷毀時就會銷毀這個對象啦眶诈。

這里的自動釋放池其實是和runloop有關(guān)的,是系統(tǒng)自動創(chuàng)建維護的瓜饥,每次runloop休眠的時候進行清空逝撬,后面的autoreleasepool中會解釋。

我們來看下源碼~

//autorelease方法
- (id)autorelease {
    return ((id)self)->rootAutorelease();
}

//rootAutorelease 方法
inline id objc_object::rootAutorelease()
{
    if (isTaggedPointer()) return (id)this;

    //檢查是否可以優(yōu)化
    if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;
    //放到auto release pool中乓土。
    return rootAutorelease2();
}

// rootAutorelease2
id objc_object::rootAutorelease2()
{
    assert(!isTaggedPointer());
    return AutoreleasePoolPage::autorelease((id)this);
}

再看一下AutoreleasePoolPage的autorelease:

public: static inline id autorelease(id obj)
    {
        assert(obj);
        assert(!obj->isTaggedPointer());
        id *dest __unused = autoreleaseFast(obj);
        assert(!dest  ||  dest == EMPTY_POOL_PLACEHOLDER  ||  *dest == obj);
        return obj;
    }

static inline id *autoreleaseFast(id obj)
    {
        AutoreleasePoolPage *page = hotPage();
        if (page && !page->full()) {
            return page->add(obj);
        } else if (page) {
            return autoreleaseFullPage(obj, page);
        } else {
            return autoreleaseNoPage(obj);
        }
    }
id *add(id obj)
    {
        assert(!full());
        unprotect();
        id *ret = next;  // faster than `return next-1` because of aliasing
        *next++ = obj;
        protect();
        return ret;
    }

autorelease方法會把對象存儲到AutoreleasePoolPage的鏈表里*next++ = obj;宪潮。等到auto release pool被釋放的時候,把鏈表內(nèi)存儲的對象刪除趣苏。所以狡相,AutoreleasePoolPage就是自動釋放池的內(nèi)部實現(xiàn)。


autorelease釋放時機

ARC時代我們是不用自己做對象釋放的處理滴食磕,但ARC其實就是對MRC包了一下尽棕,系統(tǒng)幫我們release和retain,ARC中也是有需要延后銷毀的autorelease對象的彬伦,它們究竟在什么時候銷毀的呢萄金?

其實對象的釋放是由autorelease pool來做的,而這個pool會在RunLoop進入的時候創(chuàng)建媚朦,在它即將進入休眠的時候?qū)ool里面所有的對象做release操作氧敢,最后再創(chuàng)建一個新的pool。(RunLoop可參考:http://www.cocoachina.com/articles/11970

{
    /// 1. 通知Observers询张,即將進入RunLoop
    /// 此處有Observer會創(chuàng)建AutoreleasePool: _objc_autoreleasePoolPush();
    __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopEntry);
    do {
 
        /// 2. 通知 Observers: 即將觸發(fā) Timer 回調(diào)孙乖。
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeTimers);
        /// 3. 通知 Observers: 即將觸發(fā) Source (非基于port的,Source0) 回調(diào)。
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeSources);
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
 
        /// 4. 觸發(fā) Source0 (非基于port的) 回調(diào)。
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0);
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
 
        /// 6. 通知Observers唯袄,即將進入休眠
        /// 此處有Observer釋放并新建AutoreleasePool: _objc_autoreleasePoolPop(); _objc_autoreleasePoolPush();
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeWaiting);
 
        /// 7. sleep to wait msg.
        mach_msg() -> mach_msg_trap();
        
 
        /// 8. 通知Observers弯屈,線程被喚醒
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopAfterWaiting);
 
        /// 9. 如果是被Timer喚醒的,回調(diào)Timer
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(timer);
 
        /// 9. 如果是被dispatch喚醒的恋拷,執(zhí)行所有調(diào)用 dispatch_async 等方法放入main queue 的 block
        __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(dispatched_block);
 
        /// 9. 如果如果Runloop是被 Source1 (基于port的) 的事件喚醒了资厉,處理這個事件
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(source1);
 
 
    } while (...);
 
    /// 10. 通知Observers,即將退出RunLoop
    /// 此處有Observer釋放AutoreleasePool: _objc_autoreleasePoolPop();
    __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopExit);
}

故而蔬顾,在當前RunLoop沒有進入這一輪兒休眠的時候宴偿,對象是暫時不會釋放的,所以如果我們不特殊處理這些autorelease變量诀豁,在他們看起來計數(shù)為0的時候窄刘,可能也不會立刻被釋放,因為其實它的計數(shù)還沒歸零舷胜,當release執(zhí)行后才歸零娩践。

  • 那么autorelease Pool是啥呢?

還記得MRC的[obj autorelease]么烹骨,其實就是將obj放入了自動釋放池的頂部翻伺,這個自動釋放池就是autorelease Pool。

它類似一個棧沮焕,我們可以往里面push一個個新建的變量穆趴,然后在池子銷毀的時候,就會把里面的變量一個個拿出來執(zhí)行release方法遇汞。


@autoreleasepool與AutoreleasePool及原理

我們最經(jīng)常看到的大概就是main()函數(shù)里的autoreleasepool了簿废,如下:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

這個main()函數(shù)里面的池并非必需空入。因為塊的末尾是應用程序的終止處,即便沒有這個自動釋放池族檬,也會由操作系統(tǒng)來釋放歪赢。但是這些由UIApplicationMain函數(shù)所自動釋放的對象就沒有池可以容納了,系統(tǒng)會發(fā)出警告单料。因此埋凯,這里的池可以理解成最外圍捕捉全部自動釋放對象所用的池。

@autoreleasepool{}其實就相當于:

void * atautoreleasepoolobj = objc_autoreleasePoolPush();
// do whatever you want
objc_autoreleasePoolPop(atautoreleasepoolobj);

void *objc_autoreleasePoolPush(void) {
    return AutoreleasePoolPage::push();
}

void objc_autoreleasePoolPop(void *ctxt) {
    AutoreleasePoolPage::pop(ctxt);
}

AutoreleasePoolPage是啥類扫尖?上面也出現(xiàn)過它的身影白对,那么它的定義是:

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

這里的parent和child其實就是鏈表的上一個和下一個,也就是說其實自動釋放池AutoreleasePool里面有很多AutoreleasePoolPage换怖,page形成一個鏈表結(jié)構(gòu)甩恼,就像下圖一樣:


AutoreleasePool

自動釋放池AutoreleasePool是以一個個AutoreleasePoolPage組成,而AutoreleasePoolPage以雙鏈表形成的自動釋放池。

AutoreleasePoolPage中的每個對象都會開辟出虛擬內(nèi)存一頁的大刑趺(也就是4096個字節(jié))悦污,除了實例變量占據(jù)空間,其他的空間都用來存儲autorelease對象的地址钉蒲。

id * next指向的是棧頂對象的下一個位置切端,這樣再放入新的對象的時候就知道放到哪個地址了,放入以后會更新next指向顷啼,讓它指到新的空位踏枣。如果AutoreleasePoolPage空間被占滿時,會創(chuàng)建一個AutoreleasePoolPage連接鏈表线梗,后來的對象也會在新的page加入椰于。

單向鏈表適用于節(jié)點的增加刪除,雙向鏈表適用于需要雙向查找節(jié)點值的情況仪搔。這即是AutoreleasePoolPage以雙鏈表的方式組合的原因瘾婿。缺點就是空間占用較單鏈表大。

  • 假設當前線程只有一個AutoreleasePoolPage對象烤咧,對象的內(nèi)存地址如下圖:


    AutoreleasePoolPage開始
  • 然后當一個對象發(fā)送了autorelease消息偏陪,就是將當前這個對象加入到AutoreleasePoolPage的棧頂next指向的位置。

  • 每進行一次objc_autoreleasePoolPush調(diào)用時煮嫌,runtime就會將當前的AutoreleasePoolPage加入一個哨兵對象笛谦,就會變成下面結(jié)構(gòu):


    objc_autoreleasePoolPush
  • objc_autoreleasePoolPop的時候,根據(jù)傳入的哨兵位置找到哨兵所對應的page
    將晚于哨兵對象插入的autorelease對象都發(fā)送一個release消息昌阿,并移動next指針到正確的位置饥脑。
    objc_autoreleasePoolPush返回值也就是哨兵對象的地址,被objc_autoreleasePoolPop作為參數(shù)懦冰。

@autoreleasepool{} 就是先push一下得到哨兵地址灶轰,然后把包裹的創(chuàng)建的變量一個個放入AutoreleasePoolPage,最后pop將哨兵地址之后的變量都拿出來一個個執(zhí)行release刷钢。所以@autoreleasepool和AutoreleasePool不是一個含義哦笋颤!


ARC與MRC下如何創(chuàng)建自動釋放池

NSAutoreleasePool(只能在MRC下使用)
@autoreleasepool {}代碼塊(ARC和MRC下均可以使用)

// MRC 
NSAutoreleasePool *pool = [NSAutoreleasePool alloc] init]; 
id obj = [NSObject alloc] init]; 
[obj autorelease]; 
[pool drain];

// ARC 
@autoreleasepool { 
id obj = [NSObject alloc] init]; 
} 

@autoreleasepool應用場景

  • 循環(huán)優(yōu)化

如果你嘗試跑下面的代碼,你的內(nèi)存會持續(xù)性的增加内地,幾乎電腦容量就爆了伴澄。。畢竟100000000非常大阱缓,所以如果想嘗試建議改小哈非凌。

for (int i = 0; i < 100000000; i++) {
  UIImage *image = [UIImage imageNamed:@"logo"];
}

這個內(nèi)存爆的原因其實就是image作為局部變量,在不特殊處理的時候會在runLoop休眠時再被銷毀荆针,不會立即銷毀清焕。

所以如果想解決這個問題應該改為:

for (int i = 0; i < 100000000; i++) {
  @autoreleasepool{
    UIImage *image = [UIImage imageNamed:@"logo"];
  }
}
  • 如果你的應用程序或者線程是要長期運行的并蝗,或者長期在后臺中運行的任務,因為任務運行中runloop是不會休眠的秸妥,如果產(chǎn)生大量需要autorelease的對象滚停,需要手動@autoreleasepool,否則不會立刻釋放導致內(nèi)存增加

子線程中Autorelease的釋放

  1. 子線程在使用autorelease對象時粥惧,如果沒有autoreleasepool會在autoreleaseNoPage中懶加載一個出來键畴。

  2. 在runloop的run:beforeDate,以及一些source的callback中突雪,有autoreleasepool的push和pop操作起惕,總結(jié)就是系統(tǒng)在很多地方都有autorelease的管理操作。

  3. 就算插入沒有pop也沒關(guān)系咏删,在線程exit的時候會釋放資源惹想。


最后解答一下最開始的問題:

通常非alloc、new督函、copy嘀粱、mutableCopy出來的對象都是autorelease的,比如[UIImage imageNamed:]辰狡、[NSString stringWithFormat]锋叨、[NSMutableArray array]等。(會加入到最近的autorelease pool哈)

也就是說 [NSString stringWithFormat:@"%@", @"ss"]方法內(nèi)部類似于:

+(NSString *) stringWithFormat {
  NSString *str = [[NSString alloc] initWithXXX];
  return [str autorelease];
}

因為alloc init已經(jīng)對引用+1了宛篇,然后NSString *str = [NSString stringWithFormat:@"%@", @"ssuuuuuuuuuuuuuuuuuuuu"];再次增加了引用娃磺,作用域結(jié)束的時候只是release了一次,這個變量在stringWithFormat內(nèi)部放入了自動釋放池叫倍,于是要在pool pop的時候才會再次release偷卧,真正的進行內(nèi)存釋放。

所以哦吆倦,不是autoreleasepool可以自動監(jiān)測對象的創(chuàng)建听诸,而是你對象創(chuàng)建的時候被ARC默認加了return [obj autorelease],就被放進AutoReleasePage啦

下面測試一下alloc之類的會怎樣:

  1. 如果替換為mutableCopy逼庞,則在離開作用域的時候馬上就銷毀了:
NSMutableString *str = [@"a string object" mutableCopy];

輸出:
Example1[41407:454952] viewWillAppear tracePtr: (null)
Example1[41407:454952] viewDidAppear tracePtr: (null)
  1. 如果替換為NSArray的alloc init方法也是會立刻release:
NSArray *arr = [[NSArray alloc] initWithObjects:@(1), nil];
tracePtr = arr;

輸出:
Example1[41494:457063] viewWillAppear tracePtr: (null)
Example1[41494:457063] viewDidAppear tracePtr: (null)
  1. 如果替換為NSString的alloc init方法比較特殊,是不會release的:
NSString *str = [[NSString alloc] initWithString:@"a string object"];
//等同于NSString *str = @"a string object";
tracePtr = str;

輸出:
Example1[41494:457063] viewWillAppear tracePtr: tracePtr: a string object
Example1[41494:457063] viewDidAppear tracePtr: tracePtr: a string object

這個我猜測大概是類似java里面的常量池瞻赶,由系統(tǒng)來管理字符串字面量的釋放之類的赛糟,和Array不太一樣。


加入@autoreleasepool再測一下~

  1. stringWithFormat返回的autorelease對象會被加入到最近的autorelease pool也就是@autoreleasepool {}所在的page砸逊,在@autoreleasepool {}執(zhí)行到結(jié)束的時候璧南,就會把它包裹的新對象都從page拿出來執(zhí)行一遍release,所以當運行到NSLog(@"viewDidLoad tracePtr: %@", tracePtr);的時候师逸,str對象已經(jīng)release過了司倚。
- (void)viewDidLoad {
    [super viewDidLoad];
    
    @autoreleasepool {
        NSString *str = [NSString stringWithFormat:@"%@", @"ssuuuuuuuuuuuuuuuuuuuu"];
        tracePtr = str;
    }
    NSLog(@"viewDidLoad tracePtr: %@", tracePtr);
}

輸出:
Example1[42032:469774] viewDidLoad tracePtr: (null)
Example1[42032:469774] viewWillAppear tracePtr: (null)
Example1[42032:469774] viewDidAppear tracePtr: (null)
  1. 在@autoreleasepool聲明變量:
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSString *str = nil;
    @autoreleasepool {
        str = [NSString stringWithFormat:@"%@", @"ssuuuuuuuuuuuuuuuuuuuu"];
        tracePtr = str;
    }
    NSLog(@"viewDidLoad tracePtr: %@", tracePtr);
}

輸出:
Example1[42055:470561] viewDidLoad tracePtr: ssuuuuuuuuuuuuuuuuuuuu
Example1[42055:470561] viewWillAppear tracePtr: (null)
Example1[42055:470561] viewDidAppear tracePtr: (null)

雖然str加入了autorelease pool,也就是在運行到@autoreleasepool結(jié)尾的時候會對str做release操作,相當于stringWithFormat的autorelease剛把對象放到自動釋放池动知,自動釋放池就做了pop操作執(zhí)行了release皿伺,相當于抵消了stringWithFormat的autorelease。

但是str即使做了release計數(shù)-1盒粮,外面還有一個引用鸵鸥,所以引用數(shù)仍舊不為0,故而不會立刻釋放丹皱,當運行完viewDidLoad的時候它的計數(shù)-1妒穴,會立刻進行釋放。

6.我們再來最后試一下字面量的@autoreleasepool:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    @autoreleasepool {
        NSString *str = @"ssuuuuuuuuuuuuuuuuuuuu";
        tracePtr = str;
    }
    NSLog(@"viewDidLoad tracePtr: %@", tracePtr);
}

輸出:
Example1[42074:471180] viewDidLoad tracePtr: ssuuuuuuuuuuuuuuuuuuuu
Example1[42074:471180] viewWillAppear tracePtr: ssuuuuuuuuuuuuuuuuuuuu
Example1[42074:471180] viewDidAppear tracePtr: ssuuuuuuuuuuuuuuuuuuuu

看起來字面量好像即使用了@autoreleasepool也不會釋放了摊崭,它大概是由系統(tǒng)管理吧讼油,string和number應該是比較特殊的兩種,但不用擔心這種的內(nèi)存問題呢簸,畢竟系統(tǒng)肯定會把這種管理好矮台。

最后有個小問題:子線程默認沒有runloop,而autoreleasepool依賴于runloop阔墩,那么子線程沒有autoreleasepool么嘿架?它的變量如何釋放呢?
可以參考下下面的文章啸箫,總的而言就是最好自己創(chuàng)建一個
http://www.reibang.com/p/90d08a99da20

參考:
http://www.reibang.com/p/8133439812d4
原理寫的比較好:http://www.reibang.com/p/d0558e4b0d21
http://www.reibang.com/p/30c4725e142a
http://www.reibang.com/p/5559bc15490d
http://www.reibang.com/p/505ae4c41f31

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末耸彪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子忘苛,更是在濱河造成了極大的恐慌蝉娜,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扎唾,死亡現(xiàn)場離奇詭異召川,居然都是意外死亡,警方通過查閱死者的電腦和手機胸遇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門荧呐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人纸镊,你說我怎么就攤上這事倍阐。” “怎么了逗威?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵峰搪,是天一觀的道長。 經(jīng)常有香客問我凯旭,道長概耻,這世上最難降的妖魔是什么使套? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮鞠柄,結(jié)果婚禮上侦高,老公的妹妹穿的比我還像新娘。我一直安慰自己春锋,他們只是感情好矫膨,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著期奔,像睡著了一般侧馅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上呐萌,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天馁痴,我揣著相機與錄音,去河邊找鬼肺孤。 笑死罗晕,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的赠堵。 我是一名探鬼主播小渊,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼茫叭!你這毒婦竟也來了酬屉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤揍愁,失蹤者是張志新(化名)和其女友劉穎呐萨,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體莽囤,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡谬擦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了朽缎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惨远。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖话肖,靈堂內(nèi)的尸體忽然破棺而出北秽,到底是詐尸還是另有隱情,我是刑警寧澤狼牺,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布羡儿,位于F島的核電站礼患,受9級特大地震影響是钥,放射性物質(zhì)發(fā)生泄漏掠归。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一悄泥、第九天 我趴在偏房一處隱蔽的房頂上張望虏冻。 院中可真熱鬧,春花似錦弹囚、人聲如沸厨相。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蛮穿。三九已至,卻和暖如春毁渗,著一層夾襖步出監(jiān)牢的瞬間践磅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工灸异, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留府适,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓肺樟,卻偏偏與公主長得像檐春,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子么伯,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

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

  • 內(nèi)存管理一直是學習 Objective-C 的重點和難點之一疟暖,在實際的軟件開發(fā)工作中,經(jīng)常會遇見由于內(nèi)存原因而導致...
    高思陽閱讀 3,845評論 1 4
  • 原文鏈接 AutoreleasePool對于iOS開發(fā)者來說蹦狂,可以說是"熟悉的陌生人"誓篱。熟悉是因為每個iOS程序都...
    acBool閱讀 1,176評論 0 19
  • 為什么需要 AutoreleasePool 1. 延長對象生命周期 我們都知道,系統(tǒng)內(nèi)存是有限的凯楔,要想系統(tǒng)一直正常...
    klike閱讀 1,300評論 1 3
  • AutoreleasePool(自動釋放池)是OC中的一種內(nèi)存自動回收機制窜骄,它可以延遲加入AutoreleaseP...
    coder_my閱讀 513評論 0 0
  • 好記性不如爛筆頭,勿在浮沙筑高臺摆屯,不積跬步無以至千里邻遏,做人做事要有安排有計劃。 內(nèi)存管理一直是學習 Objecti...
    BigLuckyHaha閱讀 7,069評論 9 19