首先需要理解兩個概念:
- 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工具檢測一下

上圖中我們已經(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)的存在
解決辦法:打破環(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)/~