iOS開發篇-內存管理

現在iOS開發已經是ARC甚至是swift的時代,但是內存管理仍是一個重點關注的問題,如果只知盲目開發而不知個中原理,踩坑就跳不出來了,理解好內存管理,能讓我們寫出更有質量的代碼。

內存管理是程序設計中很重要的一部分,程序在運行的過程中消耗內存,運行結束后釋放占用的內存。如果程序運行時一直分配內存而不及時釋放無用的內存,會造成這樣的后果:程序占用的內存越來越大,直至內存消耗殫盡,程序因無內存可用導致崩潰,這樣的情況我們稱之為內存泄漏

ObjC的內存管理比較簡潔,然而要深刻理解也不是一件易事,本文將介紹如何使用ObjC進行內存管理。

1. 引用計數

在ObjC中,對象什么時候會釋放(或者對象占用的內存什么時候會被回收利用)?
答案:當對象沒有被任何變量引用(也可以說是沒有指針指向該對象)的時候,就會被釋放。

那怎么知道對象已經沒有被引用了呢?

ObjC采用引用計數(reference counting)的技術進行管理:

  • 每個對象都有一個關聯的整數,稱為引用計數器
  • 當代碼需要使用該對象時,則將對象的引用技術加1
  • 當代碼結束使用該對象時,則將對象的引用技術減1
  • 當引用計數的值變為0時,表示對象沒有被任何代碼使用,此時對象將被釋放

與之對應的消息發送方法如下:

  • 當對象被創建(通過allocnewcopy等方法)時,其引用計數初始值為1
  • 給對象發送retain消息,其引用計數加1
  • 當對象引用計數歸0時,ObjC給對象發送dealloc消息銷毀對象

下面通過一個簡單的例子來說明:
場景:有一個寵物中心(內存):可以派出小動物(對象)陪小朋友們玩耍(對象引用者),現在xiaoming想和小狗一起玩耍。

新建Dog類,重寫其創建和銷毀的方法:

//
//  Dog.m
//  TextARC
//
//  Created by taobaichi on 2017/3/27.
//  Copyright ? 2017年 MaChao. All rights reserved.
//

#import "Dog.h"

@implementation Dog

-(instancetype)init{
    if (self = [super init]) {
        NSLog(@"小狗被派出去啦");
    }
    
    return self;
}

-(void)dealloc{
    NSLog(@"小狗回到寵物中心");
    
    [super dealloc];
}

@end

在main方法中創建dog對象,給dog發送消息

//
//  main.m
//  TextARC
//
//  Created by taobaichi on 2017/3/27.
//  Copyright ? 2017年 MaChao. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Dog.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Dog * dog = [[Dog alloc]init];
        //模擬:xiaoming需要和小狗玩耍,需要將其引用計數加1
        [dog retain];
        NSLog(@"小狗的引用計數為 %ld",dog.retainCount);
        
        //模擬:xiaoming不和小狗玩耍了,需要將其引用計數減1
        [dog release];
        NSLog(@"小狗的引用計數為 %ld",dog.retainCount);
        
        //沒人需要和小狗玩耍了,將其引用計數減1
        [dog release];
        
        //將其指針置為nil,否則變成野指針
        dog = nil;
        
    }
    return 0;
}

輸出結果為

2017-03-27 11:10:21.017977 TextARC[2645:84069] 小狗被派出去啦! 初始引用計數為 1
2017-03-27 11:10:21.018125 TextARC[2645:84069] 小狗的引用計數為 2
2017-03-27 11:10:21.018145 TextARC[2645:84069] 小狗的引用計數為 1
2017-03-27 11:10:21.018159 TextARC[2645:84069] 小狗回到寵物中心

可以看到,引用技術幫助寵物中心很好的標記了小狗的使用狀態,在完成任務的時候及時收回到寵物中心。

思考幾個問題:

  • NSString 引用技術問題

如果我們嘗試查看一個string的引用技術

NSString * str = @"hello guys";
NSLog(@"%ld",str.retainCount);

打印結果:

2017-03-27 11:14:33.191171 TextARC[2715:86088] str的引用計數: -1

str的引用計數為-1,這可以理解為NSString實際上是一個字符串常量,是沒有引用計數的(或者它的引用計數是一個很大的值(使用%lu可以打印查看),對它做引用計數操作沒實質上的影響)。

  • 賦值不會擁有某個對象
NSString * name = dog.name;

這里僅僅是指針賦值操作,并不會增加name的引用計數,需要持有對象必須要發送retain消息

  • dealloc
    由于釋放對象是會調用dealloc方法,因此重寫dealloc方法來查看對象釋放的情況,如果沒有調用則會造成內存泄露。在上面的例子中我們通過重寫dealloc讓小狗被釋放的時候打印日志來告訴我們已經完成釋放。

  • 在上面的例子中,如果我們增加這樣一個操作

//沒人需要和小狗玩耍了,將其引用計數減1
[dog release];
NSLog(@"小狗的引用計數為·:%ld",dog.retainCount);

會發現獲取到的引用計數為1,為什么不是0呢?

這是因為對引用計數為1的對象release時,系統知道該對象將被回收,就不會再對該對象的引用計數進行減1操作,這樣可以增加對象回收的效率。

另外,對已釋放的對象發送消息是不可取的,因為對象的內存已被回收,如果發送消息時,該內存已經被其他對象使用了,得到的結果是無法確定的,甚至會造成崩潰。

2. 自動釋放池

現在已經明確了,當不再使用一個對象時應該將其釋放,但是在某些情況下,我們很難理清一個對象什么時候不再使用(比如xiaoming和小狗玩耍結束的時間不確定),這可怎么辦

ObjC提供autorelease方法來解決這個問題,當給一個autorelease消息時,方法會在未來某個時間給這個對象發送release消息將其釋放,在這個時間段內,對象還是可以使用的。

autorelease的原理是什么呢

原理就是對象接收到autorelease消息時,它會被添加到了當前的自動釋放池中,當自動釋放池被銷毀時,會給池里所有對象發送release消息。

這里就引出了自動釋放池這個概念,什么是自動釋放池呢?顧名思義,就是一個池,這個池可以容納對象,而且可以自動釋放,這就大大增加了我們處理對象的靈活性。

自動釋放池怎么創建?

ObjC提供兩種方法創建自動釋放池:

  • 方法一:使用NSAutoreleasePool來創建
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc]init];

  • 方法二:使用@autoreleasepool創建
    @autoreleasepool {
        //這里寫代碼
    }

自動釋放池創建后,就會成為活動的池子,釋放池子后,池子將釋放其所包含的所有對象。

以上兩種方法推薦第一種,因為將內存交給ObjC管理更高效

自動釋放池什么時候創建?
app使用過程中,會定期自動生成和銷毀自動釋放池,一般是在程序事件處理之前創建,當然我們也可以自行創建自動釋放池,來達到我們一些特定的目的。

自動釋放池什么時候銷毀?

自動釋放池的銷毀時間是確定的,一般是在程序事件處理之后釋放,或者由我們自己手動釋放

下面舉例說明自動釋放池的工作流程:

場景:現在xiaoming和xiaohong都想和小狗一起玩耍,但是他們的需求不一樣,他們的玩耍時間不一樣,流程如下:

  • 方法一:
//
//  main.m
//  TextARC
//
//  Created by taobaichi on 2017/3/27.
//  Copyright ? 2017年 MaChao. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Dog.h"

int main(int argc, const char * argv[]) {
    
    //創建一個自動釋放池
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc]init];
    
    //模擬:寵物中心派出小狗
    Dog * dog  = [[Dog alloc]init];
    
    //模擬:xiaoming需要和小狗玩耍,需要將其引用計數加1
    [dog retain];
    NSLog(@"xiaoming和小狗玩耍,小狗的引用計數為 %ld",dog.retainCount);
    
    //模擬:xiaohong需要和小狗玩耍,需要將其引用計數加1
    [dog retain];
    NSLog(@"xiaohong和小狗玩耍,小狗的引用技術為 %ld",dog.retainCount);
    
    //模擬:xiaoming確定不想和小狗玩耍了,需要將其引用計數減1
    [dog release];
    NSLog(@"xiaoming確定不想和小狗玩了,小狗的引用計數:%ld",dog.retainCount);
    
    //模擬:xiaohong不確定何時不想和小狗玩耍了,將其設置為自動釋放
    [dog autorelease];
    NSLog(@"加入自動釋放池,小狗的引用計數: %ld",dog.retainCount);
    
    [dog release];
    
    NSLog(@"釋放池子");
    [pool release];
    
    return 0;
}
  • 方法二:
//
//  main.m
//  TextARC
//
//  Created by taobaichi on 2017/3/27.
//  Copyright ? 2017年 MaChao. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Dog.h"

int main(int argc, const char * argv[]) {
        @autoreleasepool {
            //模擬:寵物中心派出小狗
            Dog * dog = [[Dog alloc]init];
            
            //模擬:xiaoming需要和小狗玩耍,需要將其引用計數加1
            [dog retain];
            NSLog(@"xiaoming需要和小狗玩耍,小狗的引用計數為 %ld",dog.retainCount);
    
            //模擬:xiaohong需要和小狗玩耍,需要將其引用計數加1
            [dog retain];
            NSLog(@"xiaohong需要和小狗玩耍,小狗的引用計數: %ld",dog.retainCount);
            
            //模擬:xiaoming確定不想和小狗玩耍了,需要將其引用技術減1
            [dog release];
            NSLog(@"xiaoming確定不想和小狗玩耍了,小狗的引用計數: %ld",dog.retainCount);
            
            //模擬:xiaohong不確定何時不想和小狗玩耍了,將其設置為自動釋放
            [dog autorelease];
            NSLog(@"加入自動釋放池,小狗的引用計數: %ld",dog.retainCount);
            
            //沒人需要和小狗玩耍了,將其引用技術減1
            [dog release];
            NSLog(@"釋放池子");
            
        }

    
    return 0;
}

輸出結果如下(兩種方法輸出結果完全一致):

2017-03-27 14:10:40.808032 TextARC[3643:121392] 小狗被派出去啦! 初始引用計數為 1
2017-03-27 14:10:40.808204 TextARC[3643:121392] xiaoming和小狗玩耍,小狗的引用計數為 2
2017-03-27 14:10:40.808227 TextARC[3643:121392] xiaohong和小狗玩耍,小狗的引用技術為 3
2017-03-27 14:10:40.808246 TextARC[3643:121392] xiaoming不想和小狗玩了,小狗的引用計數:2
2017-03-27 14:10:40.808269 TextARC[3643:121392] 加入自動釋放池,小狗的引用計數: 2
2017-03-27 14:10:40.808291 TextARC[3643:121392] 釋放池子
2017-03-27 14:10:40.808339 TextARC[3643:121392] 小狗回到寵物中心

可以看到,當池子釋放后,dog對象才被釋放,因此在池子釋放之前,xiaohong都可以盡情地和小狗玩耍

使用自動釋放池需要注意:

  • 自動釋放池實質上只是在釋放的時候給池子中所有對象發送release消息,不保證對象一定會銷毀,如果自動釋放池向對象發送release消息后對象的引用計數仍大于1,對象就無法銷毀。

  • 自動釋放池中的對象會集中 在同一時間釋放,如果操作需要生成的對象較多占用內存空間大,可以使用多個釋放池來進行優化,比如在一個循環中需要創建大量的臨時變量,可以創建內部的池子降低占用內存峰值。

-autorelease不會改變對象的引用計數

自動釋放池的常見問題:
在管理對象釋放的問題上,自動釋放池幫助我們節省了大量的時間,但是有時候它卻未必會達到我們期望的效果,比如在一個循環事件中,如果循環次數較大或者事件處理占用內存較大,就會導致內存占用不斷增長,可能會導致不希望看到的后果。

示例代碼:

    for (int i = 0; i < 100000; i++) {
        NSString * log = [NSString stringWithFormat:@"%d",i];
        NSLog(@"%@",log);
    }

前面講過,自動釋放池的釋放時間是確定的,這個例子中自動釋放池會在循環事件結束時釋放,那么問題來了:在這個十萬次的循環中,每次都會生成一個字符串并打印,這些字符串對象都放在池子中直到循環結束才會釋放,因此在循環期間內存不增長。

這類問題的解決方案是在循環中創建新的自動釋放池,多少個就你和釋放一次由我們自行決定。

    for (int i = 0; i < 100000; i++) {
        @autoreleasepool {
            NSString * log = [NSString stringWithFormat:@"%d",i];
            NSLog(@"%@",log);
        }
    }

3. iOS的內存管理規則

3.1 基本原則

  • 當你通過newalloccopy方法創建一個對象時,它的引用計數為1,當不再使用該對象時應該向對象發送release或者autorelease消息釋放對象。
  • 當你通過其他方法獲得一個對象時,如果對象引用計數為1且被設置為autorelease,則不需要執行任何釋放對象的操作
  • 如果你打算取得對象所有權,就需要保留對象并在操作完成之后釋放,且必須保證retainrelease次數對等

應用到文章開頭的例子中,小朋友每申請一個小狗(生成對象),最后都要歸還到寵物中心(釋放對象),如果只申請而不歸還(對象創建了沒有釋放),那寵物中心的小狗就會越來越少(可用內存越來越少),到最后一個小狗都沒有了(內存被耗盡),其他小朋友就再也沒有小狗可申請了(無資源可使用),因此,必須要遵守規則:申請必須歸還(規則1),申請幾個必須歸還幾個(規則3),如果小狗被設定歸還時間則不用小朋友主動歸還(規則2)。

3.2 ARC

在MRC時代,必須嚴格遵守以上規則,否則內存問題將成為惡魔一樣的存在,然而來到ARC時代,事情似乎變得輕松了,不用再寫無止盡的retainrelease似乎讓開發變得輕松,對初學者變得更友好。

ObjC2.0引入了垃圾回收機制,然而由于垃圾回收機制會對移動設備產生某些不好的影響(例如由于垃圾清理造成的卡頓),iOS并不支持這個機制,蘋果的解決方案就是ARC(自動引用計數)。

iOS5 以后,我們可以開啟ARC模式,ARC可以理解成一位管家,這個管家會幫我們向對象發送retainrelease語句,不再需要我們手動添加了,我們可以更舒心地創建或引用對象,簡化內存管理步驟,節省大量的開發時間。

實際上,ARC并不是垃圾回收,也并不是不需要內存管理了,它是隱式的內存管理,編譯器在編譯的時候會在代碼插入合適的retainrelease語句,相當于在背后幫我們完成了內存管理的工作。
注意:

  • 如果你的工程歷史比較悠久,可以將其從MRC轉換成ARC,跟上時代的步伐更好的維護
  • 如果你的工程引用了某些不支持ARC的庫,可以在Build PhasesCompile Sources將對應的.m文件的編譯器參數配置為-fno-objc-arc
  • ARC能幫我們簡化內存管理,但不代表它是萬能的,還是有它不能處理的情況,這些需要我們手動處理,比如循環引用、非ObjC對象、Core Foundation中的malloc()或者free()等等

思考:MRC有什么缺點,ARC有什么局限性?

3.3 ARC的修飾符

ARC提供四種修飾符,分別是strongweakautoreleasingunsafe_unretained

__strong:強引用,持有所指向對象的所有權,無修飾符情況下的默認值。如需強制釋放,可置nil。
比如我們常用的定時器

NSTimer * timer = [NSTimer timerWit...];

相當于

NSTimer * __strong timer = [NSTimer timerWith...];

當不需要使用的時候,強制銷毀定時器

[timer invalidate];
 timer = nil;

__weak:弱引用,不持有所指向對象的所有權,引用指向的對象內存被回收之后,引用本身會置nil,避免野指針.

比如避免循環引用的弱引用聲明:

__weak typeof(self)weakSelf = self;

__autoreleasing:自動釋放對象的引用,一般用于傳遞參數
比如一個讀取數據的方法

-(void)loadData:(NSError **)error

當你調用的時候會發現這樣的提示

NSError * error;
[self loadData:(NSError *__autoreleasing *)]

這是編譯器自動幫我們插入以下代碼

NSError * error;
NSError * __autoreleasing  temErr = error;
[self loadData:&tmpErr];

__unsafe_unretained:為兼容iOS5以下版本的產物,可以理解成MRC下的weak,現在基本用不到,這里不做描述.

思考:

  • __strong NSTimer * timerNSTimer * __strong timer哪個寫法是正確的,為什么編譯器不報錯?

  • 使用__autoreleasing可能會遇到哪些問題?

3.4屬性的內存管理

ObjC2.0引入了@property,提供成員變量訪問方法、權限、環境、內存管理類型的聲明,下面主要說明ARC中屬性的內存管理.

屬性的參數分為三類,基本數據類型默認為(atomicreadwriteassign),對象類型默認為(atomic,readwrite,strong),其中第三個參數就是該屬性的內存管理方式修飾,修飾詞可以是以下之一:

  • assign: 直接賦值
    assign 一般用來修飾基本數據類型
    @property(nonatomic,assign)NSInteger count;

當然也可以修飾ObjC對象,但是不推薦,因為assign被修飾的對象釋放后,指針還是釋放前的內存,在后續操作中可能會導致內存問題引發崩潰。

  • retain: release舊值,再retain新值(引用計數+1)
    retainstrong一樣,都用來修飾ObjC對象
    使用set方法賦值時,實質上是會先保留新值,再釋放舊值,再設置新值,避免新舊值一樣時導致對象被釋放的問題

MRC寫法:

-(void)setCount:(NSInteger)count
{
    [count retain];
    [_count release];
    _count = count;
}

ARC對應寫法:

-(void)setCount:(NSInteger)count
{
    _count = count;
}

  • copy: release舊值,再copy新值(拷貝內容)
    一般用來修飾StringDictArray等需要保護其封裝性的對象,尤其是在其內容可變的情況下,因此會拷貝(深拷貝)一份內容給屬性使用,避免可能造成的對源內容進行改動。

使用set方法賦值時,實質上是會先拷貝新值,再釋放舊值,再設置新值

實際上,遵守NSCopying的對象都可以使用copy,當然,如果你確定是要共用同一份可變內容,你也可以使用strong或retain

@property (nonatomic, copy) NSString * name;

  • weak:ARC新引入修飾詞,可代替assign,比assign多增加一個特性:置nil
    weakstrong一樣用來修飾ObjC對象。
    使用set方法賦值時實質上不保留新值,也不釋放舊值,只設置新值

比如常用的代理的聲明

@property (weak)id<myDelegate>delegate;

XIb控件額引用

@property (weak, nonatomic) IBOutlet UILabel *nickNameLabel;
  • strongARC新引入修飾詞,可代替retain
    可參照retain,這里不再做描述

思考:

  • 各個屬性修飾詞和3.3中的修飾詞的對應關系
  • 屬性的本質是什么

3.5 block的內存管理

iOS中使用block必須自己管理內存,錯誤的內存管理將導致循環引用等內存泄露問題,這里主要說明在ARC下block聲明和使用的時候需要注意的兩點:

  1. 如果你使用@property去聲明一個block的時候,一般使用copy來進行修飾(當然也可以不寫,編譯器自動進行copy操作),盡量不要使用retain
@property (nonatomic, copy) void(^block)(NSData * data);

block會對內部使用的對象進行強引用,因此在使用的時候應該確定不會引起循環引用,當然保險的做法就是添加弱引用標記。

__weak typeof(self)weakSelf = self;

深入了解:

  1. block的內部實現原理是什么
  2. 從內存位置來看block有幾種類型?他們的內存管理方式各是怎樣的?
  3. 對于不同類型的外部變量,block的內存管理都是怎樣的?

4 經典內存泄露及其解決方案

雖然ARC好處多多,然而也無法避免內存泄露問題,下面介紹在ARC中常見的內存泄露。

4.1 僵尸對象和野指針

僵尸對象:內存已經被回收的對象

野指針 :指向僵尸對象的指針,向野指針發送消息會導致崩潰

野指針錯誤形式在Xcode中通常表現為:Thread 1:EXC_BAD_ACCESS,因為你訪問了一塊已經不屬于你的內存

例子代碼:(沒有出現錯誤的話多運行幾遍,,因為獲取野指針指向的結果是不確定的)

Dog * dog = [[Dog alloc]init];
NSLog(@"---before");
NSLog(@"---%s",object_getClassName(dog));
            
[dog release];
NSLog(@"---after");
NSLog(@"---%s",object_getClassName(dog));

運行結果:

2017-03-29 13:35:42.806557 TextARC[3235:116238] ---before
2017-03-29 13:35:42.806763 TextARC[3235:116238] ---Dog
2017-03-29 13:35:42.806827 TextARC[3235:116238] 小狗回到寵物中心
2017-03-29 13:35:42.806845 TextARC[3235:116238] ---after
(11db)

可以看到,當運行到弟六行的時候崩潰了,并給出了EXC_BAD_ACCESS的提示。

解決方案:
對象已經被釋放后,應將其指針置為空指針(沒有指向任何對象的指針,給空指針發送消息不會報錯)。

然而在實際開發中實際遇到EXC_BAD_ACCESS錯誤時,往往很難定位到錯誤點,幸好Xcode提供方便的工具給我們來定位及分析錯誤。

1.在produce - scheme - edit scheme - diagnostics中將zombie objects勾選上,下次再出現這樣的錯誤就可以準確定位了。

運行結果:

2017-03-29 13:35:42.806557 TextARC[3235:116238] ---before
2017-03-29 13:35:42.806763 TextARC[3235:116238] ---Dog
2017-03-29 13:35:42.806827 TextARC[3235:116238] 小狗回到寵物中心
2017-03-29 13:35:42.806845 TextARC[3235:116238] ---after
2017-03-29 13:35:42.806845 TextARC[3235:116238]_NSZombie_Dog

可以看到,當運行到第六行時并沒有崩潰,并給出了NSZOmbie的提示

  1. Xcode- Open Developer Tool- Instruments打開工具集,選擇Zombies工具可以對已安裝的應用進行僵尸檢測。
4.2 循環引用

循環引用是ARC中最常出現的問題

一般來講循環引用也是可以使用工具來檢測的,分為兩種:

  1. peoduct - Analyze中使用靜態分析來檢測代碼中可能存在循環引用的問題。

  2. Xcode - Open Developer Tool - Instruments打開工具集,選擇Leaks工具可以對已安裝的應用進行內存泄露檢測,此工具能檢測靜態分析不會提示,但是到運行時才會出現的內存泄露問題。

Leaks工具雖然強大,但是它不能檢測到block循環引用導致的內存泄露,這種情況一般需要自行排查問題,傻瓜式的方案當然是重寫對象的dealloc方法來監測對象是否正常釋放,來確認沒有形成循環引用.

4.3 循環中對象占用內存大

這個問題常見于循環次數較大,循環體生成的對象占用內存較大的情景。

代碼示例:

for (int i = 0; i < 10000; i++) {
       Dog * dog = [[Dog alloc]init];
       [dog eat];
}

該循環內產生大量的臨時對象,直至循環結束才釋放,可能導致內存泄露,解決方法方法和自動釋放池常見問題類似,在循環中創建自己額autoreleasePool,及時釋放占用內存大的臨時變量,減少內存占用峰值。

for (int i = 0; i < 10000; i++) {
        @autoreleasepool {
              Dog * dog = [[Dog alloc]init];
              [dog eat];
         }
 }

當然有時候autoreleasePool也不是萬能的

例子:假如有20000張圖片,每張1M左右,現在要獲取所有圖片的尺寸,你會怎么做?

如果這樣做

    for (int i = 0; i < 2000; i++) {
        CGSize size = [UIImage imageNamed:[NSString stringWithFormat:@"%d",i]].size;
    }

用imageNamed方法加載圖片占用Cache的內存,autoreleasePool也不能釋放,對此問題需要另外的解決辦法,最保險的當然是雙管齊下了

    for (int i = 0; i < 2000; i++) {
        @autoreleasepool {
            CGSize size = [UIImage imageWithContentsOfFile:filePath].size;
        }
    }

4.4 無限循環

這個是比4.3更極端的情況,無論你出于什么原因,當你啟動了一個無限循環的時候,ARC會默認該方法不會執行完畢,方法里面的對象就永不釋放,內存無限上漲,導致內存泄露

代碼示例:

NSLog(@"start!");               
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      BOOL isSucc = YES;
      while (isSucc) {
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"create an obj");
      }
});

輸出結果為

2017-03-29 15:00:41.371 duiduipeng[4311:156775] start!
2017-03-29 15:00:42.440 duiduipeng[4311:157083] create an obj
2017-03-29 15:00:43.514 duiduipeng[4311:157083] create an obj
2017-03-29 15:00:44.552 duiduipeng[4311:157083] create an obj
2017-03-29 15:00:45.625 duiduipeng[4311:157083] create an obj
2017-03-29 15:00:46.626 duiduipeng[4311:157083] create an obj
2017-03-29 15:00:47.696 duiduipeng[4311:157083] create an obj
2017-03-29 15:00:48.770 duiduipeng[4311:157083] create an obj
2017-03-29 15:00:49.836 duiduipeng[4311:157083] create an obj

可以看到,當控制器釋放后該循環還在繼續

對于這類問題解決方案是什么呢?
提示:解決方法有autoreleasePoolblocktimer等

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內容

  • 29.理解引用計數 Objective-C語言使用引用計數來管理內存,也就是說,每個對象都有個可以遞增或遞減的計數...
    Code_Ninja閱讀 1,521評論 1 3
  • 內存管理 簡述OC中內存管理機制。與retain配對使用的方法是dealloc還是release,為什么?需要與a...
    丶逐漸閱讀 1,989評論 1 16
  • 前言 現在iOS開發已經是arc甚至是swift的時代,但是內存管理仍是一個重點關注的問題,如果只知盲目開發而不知...
    明仔Su閱讀 26,595評論 16 175
  • iOS內存管理 概述 什么是內存管理 應用程序內存管理是在程序運行時分配內存(比如創建一個對象,會增加內存占用)與...
    蚊香醬閱讀 5,748評論 8 119
  • 丙申年臘月二十九的 陰霾天 長安街清冷起來 大雁塔下少了一大半的游人 安靜到可以聽聞 每層浮屠的邊角青銅鈴聲 叮叮...
    嫏嬛素素閱讀 306評論 5 1