由于前段時間我負責的app 嚴重發(fā)熱,由于上線時間短,所以出現(xiàn)了一些問題 結(jié)合網(wǎng)上一些教程我這邊也仔細檢查,檢查的結(jié)果原因如下假残。可能有些地方寫得不對,大神勿噴,請訂正.傳送門
1.Block循環(huán)引用問題
在蘋果使用ARC管理之前帆卓,Block的內(nèi)存管理(block如何進行內(nèi)存管理的)需要區(qū)分是Global(全局)澜建、Stack(棧)還是Heap(堆)送矩。而在使用了ARC之后蚕甥,蘋果自動會將所有原本應(yīng)該放在棧中的Block全部放到堆中,所以這使得我們現(xiàn)在的討論可以省去很大一部分的麻煩栋荸。下面我們就只討論ARC環(huán)境下全局Block和堆Block的內(nèi)存管理菇怀。
Block的循環(huán)引用是比較容易被忽視凭舶,原本也是相對比較難檢查出來的問題。當然現(xiàn)在蘋果在XCode編譯的層級就已經(jīng)做了循環(huán)引用的檢查爱沟,所以這個問題的檢查就突然變的沒有難度了帅霜。簡單說一下循環(huán)引用出現(xiàn)的原理:Block的擁有者在Block作用域內(nèi)部又引用了自己,因此導致了Block的擁有者永遠無法釋放內(nèi)存呼伸,就出現(xiàn)了循環(huán)引用的內(nèi)存泄漏
@interface ObjTest () {
NSInteger testValue;
}
@property (copy, nonatomic) void (^block)();
@end
@implement ObjTest
- (void)function {
self.block = ^() {
self.testValue = 100; 這里就會發(fā)生內(nèi)存泄漏問題
? };
}
@end
ObjTest擁有了一個名字叫block的Block對象身冀;然后在這個Block中,又對ObjTest的一個成員變量testValue進行了賦值括享。于是就產(chǎn)生了循環(huán)引用:ObjTest->block->ObjTest搂根。
要避免循環(huán)引用的關(guān)鍵就在于破壞這個閉合的環(huán)。在目前只考慮ARC環(huán)境的情況下铃辖,筆者所知的只有一種方法可以破壞這個環(huán):在Block內(nèi)部對擁有者使用弱引用剩愧。
@interfaceObjTest () {
NSInteger testValue;
}
@property (copy, nonatomic)void(^block)();
@end
@implement ObjTest
- (void)function {
__weak ObjTest* weakSelf =self;
self.block= ^() {weakSelf.testValue=100;
};
}@end
在Block外創(chuàng)建一個對于self的弱引用,然后在Block內(nèi)引用self的地方全部使用這個弱引用娇斩。這樣就使得Block內(nèi)部不會對self本身做引用計數(shù)+1的操作仁卷。那樣就可以打破循環(huán)引用的環(huán)了。
2.AFN框架和MJRefresh上下拉刷新(其實都是block引發(fā)的命案)
在封裝網(wǎng)絡(luò)請求類時需注意的是需要將請求隊列管理者AFHTTPSessionManager聲明為單例創(chuàng)建形式犬第。對于該問題锦积,AFNetWorking的作者在gitHub上也指出建議使用者在相同配置下保證AFHTTPSessionManager只有一個,進行全局管理歉嗓,因此我們可以通過單例形式進行解決充包。下方展示部分核心代碼:
然后在說MJRefresh(參考:傳送門)
一般的我們是怎么使用MJRefresh 這個玩意的 看代碼
self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
self.page =1;? ? ? ?
[self.dataArr removeAllObjects];? ? ? ?
[self loadData];
}];
若在MJRefresh的執(zhí)行Block中調(diào)用當前self或其所屬屬性,一定要注意循環(huán)引用問題遥椿。我們簡單分析下MJRefresh為什么會造成循環(huán)引用問題:點擊進入headerWithRefreshingBlock對應(yīng)方法即可
#pragmamark- 構(gòu)造方法
+ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock{? ?
MJRefreshHeader *cmp= [[selfalloc] init];
cmp.refreshingBlock= refreshingBlock;
return cmp;
}
這里僅有三行代碼基矮,無非就是創(chuàng)建了下拉刷新部分View然后返回,這里比較重要的是cmp.refreshingBlock = refreshingBlock;這一句冠场,這里的refreshingBlock是屬于MJRefreshHeader的強引用屬性家浇,最后header會成為我們自己tableView的強引用屬性mj_header,也就是說self.tableView強引用header, header強引用refreshingBlock碴裙,如果refreshingBlock里面強引用self钢悲,就成了循環(huán)引用,所以必須使用weakSelf舔株,破掉這個循環(huán).如何解決直接看傳送們.
3.大次數(shù)循環(huán)內(nèi)存暴漲問題(你永遠都不知道會存在這里的內(nèi)存泄漏)
for(inti =0; i <100000; i++) {
?NSString *string= @"Abc";
string= [string lowercaseString];
string= [stringstringByAppendingString:@"xyz"]; ?//引起內(nèi)存飆漲 ?
NSLog(@"%@",string);
}
該循環(huán)內(nèi)產(chǎn)生大量的臨時對象莺琳,直至循環(huán)結(jié)束才釋放,可能導致內(nèi)存泄漏载慈,解決方法為在循環(huán)中創(chuàng)建自己的autoReleasePool(autoReleasePool都干了啥,這么??)惭等,及時釋放占用內(nèi)存大的臨時變量,減少內(nèi)存占用峰值办铡。
for(inti =0; i < 100000; i++) {
@autoreleasepool{? ? ? ? ? ??
NSString *string= @"Abc";
string= [stringlowercaseString];string= [stringstringByAppendingString:@"xyz"];
NSLog(@"%@",string);}? ??
}
附辞做、如何檢測App的內(nèi)存泄漏問題
1琳要、借助Xcode自帶的Instruments工具(選取真機測試)
2、簡單暴力的重寫dealloc方法秤茅,加入斷點或打印判斷某類是否正常釋放稚补。
3、通過Facebook出品的FBMemoryProfiler工具類進行檢測框喳,感興趣的童鞋可進行了解课幕。
4、更多關(guān)于如何進行app性能優(yōu)化請看:微信讀書 iOS 性能優(yōu)化總結(jié)? ??MLeaksFinder簡介