簡(jiǎn)介
項(xiàng)目開發(fā)完,提交測(cè)試之后精刷,解bug
之余舀寓,看看內(nèi)存相關(guān)的內(nèi)容。
靜態(tài)檢查
這個(gè)推薦做一下完慧,非常簡(jiǎn)單。Product =》Analyse
剩失。
這里可以檢查出一些低級(jí)錯(cuò)誤屈尼,比如viewWillAppear
缺少super
等。一般情況下拴孤,這里查出的問(wèn)題最好都能解決一下脾歧。
Swift
工程里用到了一個(gè)Swift
寫的第三方庫(kù),在用Instrument
的時(shí)候發(fā)現(xiàn)跑不起來(lái)演熟。輸出的信息有This copy of libswiftCore.dylib requires an OS version prior to 12.2.0
這樣的一句話鞭执。解決方法是,在Build Setting =》Runpath Search Path
標(biāo)簽下添加/usr/lib/swift
https://stackoverflow.com/questions/55361057/this-copy-of-libswiftcore-dylib-requires-an-os-version-prior-to-12-2-0
NSURLSession
工程中直接使用了NSURLSession
進(jìn)行網(wǎng)絡(luò)傳輸芒粹。由于NSURLSession
對(duì)于其delegate
是強(qiáng)引用兄纺,所以容易造成引用循環(huán)。需要調(diào)用其invalidateAndCancel
或者finishTasksAndInvalidate
方法化漆,進(jìn)行釋放估脆。
解決方案,一種是全局使用一個(gè)NSURLSession
進(jìn)行網(wǎng)絡(luò)傳輸座云。
NSURLSession時(shí)需要注意一個(gè)內(nèi)存泄漏問(wèn)題
https://www.cnblogs.com/lys-iOS-study/p/5684454.html
另外一種方法就是在網(wǎng)絡(luò)完成之后疙赠,主動(dòng)釋放。
關(guān)于NSURLSession內(nèi)存泄露解決方案
當(dāng)然疙教,這個(gè)只有在自己寫網(wǎng)絡(luò)傳輸?shù)臅r(shí)候才需要注意棺聊,如果用AFNetworking
之類的第三方庫(kù),這些問(wèn)題已經(jīng)考慮到了贞谓,不需要再操心限佩。
Block
block
在Object-C
中比較普遍。如果block
中包含self
裸弦,很容易造成引用循環(huán)祟同。當(dāng)然,只是“很容易”理疙,而不是"一定"
這個(gè)就是會(huì)造成引用循環(huán)的例子晕城,需要處理
kWeakSelf(self)
// 左邊的關(guān)閉按鈕
self.bankSelectView.leftButtonBlock = ^{
kStrongSelf(self)
self.viewIndex = KJTPayViewIndexPay;
};
這個(gè)其實(shí)是不會(huì)造成引用循環(huán)的。不過(guò)處理一下窖贤,也沒什么關(guān)系砖顷。
// 從網(wǎng)絡(luò)取數(shù)據(jù)
- (void)fetchData {
kWeakSelf(self)
[KJTRequestApi getMainPageInfo:^(KJTMainPageInfoModel * _Nonnull pageInfo) {
kStrongSelf(self)
self.errorView.hidden = YES;
} failureBlock:^(NSString * _Nullable message) {
kStrongSelf(self)
self.errorView.hidden = NO;
}];
}
所以贰锁,簡(jiǎn)單起見,只要block
里面包含了self
滤蝠,就用weak self ; strong self
對(duì)處理一下豌熄,反正也沒有什么副作用。
RACObserve
這個(gè)可以用來(lái)監(jiān)聽屬性值的改變物咳,然后在隨后的subscribeNext
中進(jìn)行相應(yīng)锣险。比如下面的例子
[RACObserve(self.viewModel, title) subscribeNext:^(NSString * title) {
self.title = title;
}];
由于RACObserve
宏隱含了對(duì)self
的引用,所以览闰,上面的代碼其實(shí)已經(jīng)造成了循環(huán)引用芯肤。
#define RACObserve(TARGET, KEYPATH) \\
({ \\
_Pragma("clang diagnostic push") \\
_Pragma("clang diagnostic ignored \\"-Wreceiver-is-weak\\"") \\
__weak id target_ = (TARGET); \\
[target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \\
_Pragma("clang diagnostic pop") \\
})
所以,這里要打破引用循環(huán)压鉴,可以這樣:
@weakify(self);
[RACObserve(self.viewModel, title) subscribeNext:^(NSString * title) {
@strongify(self);
self.title = title;
}];
更進(jìn)一步: 在有subscribeNext
的地方都用上weak self
崖咨,比如像下面這樣的代碼:
@weakify(self);
[self.viewModel.titleSignal subscribeNext:^(NSString * title) {
@strongify(self);
self.title = title;
}];
RACSubject
RACSubject
被稱為熱信號(hào),這個(gè)難以理解晴弃。我更愿意把它類比為一種消息發(fā)送和監(jiān)聽處理的一種比較方便的使用方式
這個(gè)用起來(lái)還是比較方便的掩幢,特別是網(wǎng)絡(luò)訪問(wèn)逊拍,只要?jiǎng)?chuàng)建成功上鞠,失敗兩個(gè)不同的RACSubject
就可以了。
不過(guò)要注意的是芯丧,如果忘了[subject sendCompleted];
這句芍阎,很可能會(huì)帶來(lái)內(nèi)存泄漏。所以缨恒,為了保險(xiǎn)起見谴咸,[subject sendNext:@1];
都跟上這么一句話,就好多了骗露。詳細(xì)信息可以參考下面這篇文章:
ReactiveCocoa中潛在的內(nèi)存泄漏及解決方案
嵌套的block
在項(xiàng)目中岭佳,還遇到一種情況:首先是接口1的網(wǎng)絡(luò)訪問(wèn),這里有一個(gè)異步block
萧锉;然后珊随,彈出一個(gè)對(duì)話框,等待用戶輸入柿隙,這里有一個(gè)異步block
叶洞;最后,調(diào)用接口2的網(wǎng)絡(luò)接口禀崖,這里有一個(gè)異步block
衩辟。這種異步block
嵌套多次的情況,斷點(diǎn)調(diào)試波附,最后能出來(lái)艺晴,不過(guò)要等好久昼钻。所以,這種時(shí)候封寞,也加上weak self
换吧,出來(lái)的速度就快多了。
@weakify(self);
[Newwork sucess:^(void) {
@strongify(self);
[InputDialog sucess:^(NSString * title) {
self.title = title;
[newwork sucess:^(void) {
NSLog(@"done");
}]
}];
}];
dealloc
在基類控制器中添加如下代碼:
- (void)dealloc {
NSLog(@"%@退出了沾瓦。",NSStringFromClass([self class]));
}
其實(shí),產(chǎn)品運(yùn)行起來(lái)后谦炒,都是一個(gè)Controller
在跳轉(zhuǎn)贯莺,其他的類,比如view
宁改,button
之類的缕探,并不能獨(dú)立存在。所以还蹲,一般來(lái)說(shuō)爹耗,只要Controller
能正常退出,就不會(huì)有內(nèi)存泄漏谜喊。這是檢測(cè)內(nèi)存是否泄漏的一個(gè)比較簡(jiǎn)單的方法潭兽。
單例
網(wǎng)上雖然有關(guān)于單例的消除相關(guān)文章,但是斗遏,實(shí)際執(zhí)行下來(lái)山卦,發(fā)現(xiàn)dealloc
并不能真正執(zhí)行。比如:
+ (void)tearDown{
sInstance=nil;
onceToken=0l;
}
所以诵次,單例并不能真正執(zhí)行账蓉。
不過(guò),既然是單例逾一,常駐內(nèi)存也是可以的铸本。并且,只有一個(gè)實(shí)例遵堵,所以箱玷,并不會(huì)造成內(nèi)存泄漏。
不過(guò)鄙早,有一種情況需要注意汪茧,如果把Controller
賦值給某個(gè)單例強(qiáng)引用屬性,那么這個(gè)Controller
就退不出來(lái)了限番。不過(guò)舱污,這種情況內(nèi)存占用是有限的,并不會(huì)造成內(nèi)存泄漏弥虐。下次再進(jìn)的話扩灯,Controller
會(huì)被替換媚赖,不會(huì)疊加,占用的內(nèi)存是有限的珠插。
解決這個(gè)問(wèn)題的方法是單例在把Controller
作為屬性的時(shí)候惧磺,修飾符用weak
,不要用strong
就可以了捻撑。
// 輸入Controller
@property (weak, nonatomic) UIViewController *inputVc;
Leaks
Xcode
自帶的Leaks
一般被認(rèn)為是查內(nèi)存泄漏的好工具磨隘。以前也用過(guò),雖然不熟練顾患,不過(guò)按照網(wǎng)上的介紹文章番捂,還是能夠找出幾個(gè)內(nèi)存泄漏的地方的。當(dāng)時(shí)江解,現(xiàn)在的10.2.1
版本的XCode设预,Leaks
幾乎沒什么用。工程跑起來(lái)犁河,就停止首頁(yè)不動(dòng)鳖枕,也能報(bào)出很多的紅叉,并且一會(huì)兒就自動(dòng)退出了桨螺,說(shuō)是遇到了error
宾符,但是卻找不到有效的錯(cuò)誤信息。這讓人感覺到彭谁,蘋果是越來(lái)越差吸奴,越來(lái)越?jīng)]前途。XCode
改得越來(lái)越難用缠局。
以前雖然感覺也很差,不過(guò)至少還有點(diǎn)作用考润,反正用得也不多狭园,湊合著也行。現(xiàn)在的糊治,真是太差了唱矛。庫(kù)克還真是個(gè)蠢貨。
網(wǎng)上的文章也很多井辜,不過(guò)幾乎沒有實(shí)用的绎谦,這實(shí)在是Leaks
這工具本身做得實(shí)在太差,太難用了粥脚。
IOS-Instrument-Leaks
iOS Instruments之Leaks
iOS性能優(yōu)化之內(nèi)存管理:Analyze窃肠、Leaks、Allocations的使用和案例代碼
聚焦ViewController
ViewController
能夠退出刷允,那么內(nèi)存泄漏的問(wèn)題就不大冤留。
如果ViewController
作為其他成員的屬性碧囊,那么就應(yīng)該用weak
修飾。特別是用子ViewController
的時(shí)候纤怒,在父ViewController
應(yīng)該作為weak
類型的屬性成員存在糯而。因?yàn)楦缸?code>ViewController難免要相互引用。
// child controller
@property (weak, nonatomic) KJTPasswordLoginChildViewController *passwordController;
@property (weak, nonatomic) KJTCodeLoginChildViewController *codeController;
騰訊基于Facebook
的庫(kù)泊窘,二次開發(fā)了一個(gè)內(nèi)存泄漏檢測(cè)的工具熄驼,非常好用,強(qiáng)烈推薦
MLeaksFinder
iOS開發(fā)中內(nèi)存泄漏檢測(cè)工具--MLeaksFinder
備注:內(nèi)存檢測(cè)工具烘豹,就不要加入正式版本中了谜洽。可以拉一個(gè)分支吴叶,加入阐虚。用的時(shí)候,這個(gè)分支只運(yùn)行蚌卤,不修改实束。修改都在開發(fā)分支中進(jìn)行,開發(fā)完后逊彭,再和到這個(gè)檢測(cè)分支咸灿,跑一下。如果發(fā)現(xiàn)內(nèi)存泄漏侮叮,就在開發(fā)分支上改避矢,改完后再把改動(dòng)合過(guò)來(lái)。
如果有Controller
釋放不了囊榜,會(huì)彈框提示审胸,然后再去查找代碼,就相對(duì)容易點(diǎn)了卸勺。這個(gè)和在Controller
的dealloc
函數(shù)中打log
的功能是一樣的砂沛,不過(guò)這個(gè)更直觀。推薦使用曙求。