iOS開發(fā)中本人或同事碰到的內(nèi)存泄漏及解決辦法

首先需要理解兩個概念:

  • OC內(nèi)存管理采用的是引用計數(shù)的方式(詳細(xì)請百度)
  • 內(nèi)存泄漏:為什么要說這個,因?yàn)槲野l(fā)現(xiàn)好多人都在說內(nèi)存泄漏但并沒有理解,這里我用大白話講一下:
    就是有塊內(nèi)存你雖然不用了但還要占著不讓別人用,所以稱為內(nèi)存泄漏(專業(yè)的來了:memory leak)
  • weak關(guān)鍵的作用(請百度)

好的,進(jìn)入正題,我逐步演示碰到的各種內(nèi)存泄漏:


round1:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSMutableArray *arr1 = [NSMutableArray array];
    NSMutableArray *arr2 = [NSMutableArray array];

    [arr1 addObject:arr2];
    [arr2 addObject:arr1];
}

我們用Xcode的Instruments工具檢測一下

Paste_Image.png

上圖中我們已經(jīng)很明顯的看到了循環(huán)引用
解決辦法:打破環(huán)!
上述例子中將arr1或者arr2任意一個 弱引用就OK
貼張圖出來與上面代碼的不同會更加清晰(__)

再次用工具檢測:


看到leak那行都是綠色的是不是很棒?

round2:

首先我們建兩個model:Student、Teacher
Student類中有屬性teacher
Teacher類中有屬性student
并且都重寫了dealloc方法,打印信息

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

@interface Teacher : NSObject

@property (nonatomic, strong) Student *student;

@end

#import "Teacher.h"

@implementation Teacher

- (void)dealloc {
    NSLog(@"%s",__func__);
}

@end
#import <Foundation/Foundation.h>
@class Teacher;

@interface Student : NSObject

@property (nonatomic, strong) Teacher *teacher;

@end

#import "Student.h"
@implementation Student

- (void)dealloc {
    NSLog(@"%s",__func__);
}
@end

第一個頁面push到第二個頁面,第二個頁面代碼如下:

#import "ViewController2.h"
#import "Student.h"
#import "Teacher.h"

@interface ViewController2 ()
{
    Teacher *_teacher;
    Student *_student;
}
@end

@implementation ViewController2

// dealloc
- (void)dealloc {
    NSLog(@"%s",__func__);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    _student = [[Student alloc]init];
    _teacher = [[Teacher alloc]init];
    
    _student.teacher = _teacher;
    _teacher.student = _student;
    
}

運(yùn)行push到第二頁面在pop回第一個頁面看控制臺的打印信息

僅僅走了Controller的dealloc方法,兩個model的dealloc都沒有走

為什么呢?我們分析一下

覺得還是畫圖解釋的快一點(diǎn)((⊙﹏⊙)雖然小編累一點(diǎn))
如圖:
上半部分是兩個model的UML圖,
下半部分我們看到_student實(shí)例的teacher屬性引用了_teacher實(shí)例,而實(shí)例_teacher的student屬性引用了_student實(shí)例這樣就形成了一個引用環(huán),由于OC的內(nèi)存管理機(jī)制就導(dǎo)致了這兩塊內(nèi)存不能被釋放導(dǎo)致內(nèi)存泄漏
解決辦法:打破環(huán)!
我們將Student或Teacher類里的屬性 任意一個內(nèi)存語義strong改為weak,再跑一下看看結(jié)果:

三個dealloc方法都走了,是不是很nice?

round3:

最最常見的一種,block中的操作

typedef void(^TestBlock)(void);
@interface ViewController2 ()
@property (nonatomic, copy) TestBlock block;
@end

@implementation ViewController2

- (void)dealloc {
    NSLog(@"%s",__func__);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.block = ^ {
        self.view.backgroundColor = [UIColor redColor];
    };
}

@end

block為什么用copy修飾(請百度,不在本次討論重點(diǎn)內(nèi))
由于block會retain當(dāng)前對象,所以這里也形成了一個環(huán)
vc2引用block,block保留當(dāng)前對象self
解決辦法:打破環(huán)
將block內(nèi)的self弱引用就OK

round4:

上面3中情況都是循環(huán)引用造成的內(nèi)存泄漏,解決辦法都是打破環(huán)
ok,看如下代碼:

- (void)viewDidLoad {
    [super viewDidLoad];
    NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
    NSOperationQueue *mainQuene =[NSOperationQueue mainQueue];
    [center addObserverForName:UIKeyboardWillShowNotification
                        object:nil
                         queue:mainQuene
                    usingBlock:^(NSNotification *note) {
                        self.view.backgroundColor = [UIColor redColor];
                    }];
}

當(dāng)pop到上一頁面的時候 dealloc 方法也沒有走,why?
我們分析一下:
center -> block -> self
但是self有沒有擁有center?答案是有的,具體里邊的實(shí)現(xiàn)暫時我不清楚,但確定的是有環(huán)的存在

參考:http://stackoverflow.com/questions/12699118/view-controller-dealloc-not-called-when-using-nsnotificationcenter-code-block-me

解決辦法:打破環(huán)
    __weak typeof(self) weakSelf = self;
    NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
    NSOperationQueue *mainQuene =[NSOperationQueue mainQueue];
    [center addObserverForName:UIKeyboardWillShowNotification
                        object:nil
                         queue:mainQuene
                    usingBlock:^(NSNotification *note){
                        weakSelf.view.backgroundColor = [UIColor redColor];
                    }];

以上就是我碰到的或同事碰到的導(dǎo)致內(nèi)存泄漏的幾種情況,真正開發(fā)中碰到的應(yīng)該都比較復(fù)雜,利用好Instruments加上開發(fā)時注意保持良好習(xí)慣,大都可以避免

其實(shí)這篇文章很早就想寫,但是這邊涉及的內(nèi)容確實(shí)太多了
block為什么會retain當(dāng)前對象?
OC為什么采用引用計數(shù)來管理內(nèi)存?
管理內(nèi)存語義的那些關(guān)鍵字(strong,weak,copy等)之間的差異
Instruments工具怎么靈活運(yùn)用?
等等,所以本篇就只針對內(nèi)存泄漏,其他我也不很熟,很多沒想通,也不敢寫

這下也就很容易理解為什么delegate要用weak修飾了(不明白的話留言,我再補(bǔ)個圖上來)
so:到此結(jié)束?。ㄒ粴夂浅傻母杏X好爽O(∩_∩)O哈哈~)


希望會給大家?guī)韼椭?o)/~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容