很久很久沒有寫過文章了,割的時間有點長了,長的都讓我忘卻了學(xué)習(xí).這里用小錘錘錘一下自己,咋就不知道學(xué)習(xí)啦!
好了廢話不多說:導(dǎo)致內(nèi)存泄漏的幾點原因(不全面,請在回復(fù)區(qū)補充)
1.關(guān)于cf框架的使用
大家都知道在使用Core Foundation的API時要時刻注意自己需要手動釋放創(chuàng)建的內(nèi)存,凡是看到create,copy,mutablecopy,alloc等都要調(diào)用CFRelease
舉例如下:
CFUUIDRef uuid = CFUUIDCreate(NULL);
appUID = (NSString *) CFUUIDCreateString(NULL, uuid);
CFRelease(uuid);
2.關(guān)于block的循環(huán)引用
這個東西已經(jīng)被說過很多次了,導(dǎo)致循環(huán)引用的原因也是很容易分析的:實例對象引用了block,在block內(nèi)部訪問了實例對象(包括該對象的實例變量)就會產(chǎn)生循環(huán)引用的問題
舉例如下:
//情況一
-?(void)case1?{
NSLog(@"case?1?Click");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,?(int64_t)(0.3?*?NSEC_PER_SEC)),?dispatch_get_main_queue(),?^{
self.name?=?@"case?1";
});
}
這種情況不會造成內(nèi)存泄漏,原因很簡單,他并不是一個閉環(huán),雖然在block內(nèi)部引用了self,但是self并沒有持有block,這就是常見的系統(tǒng)級別的block不用使用weakself,和strongself的原因,如uiview的動畫block,gcd等
//情況二
-?(void)case2?{
NSLog(@"case?2?Click");
__weaktypeof(self)?weakSelf?=?self;
[self.teacher?requestData:^(NSData?*data)?{
typeof(weakSelf)?strongSelf?=?weakSelf;
strongSelf.name?=?@"case?2";
}];
}
第二種情況就是我們常見的容易出現(xiàn)循環(huán)引用的地方,self持有了block,block持有了self,形成了一個完美的閉環(huán),根本無法釋放,要想釋放內(nèi)存就必須保證一端為弱引用.但是在block內(nèi)部為了防止self提前釋放,又轉(zhuǎn)成了strongself,也就是強引用保證在block內(nèi)部self永遠存在!
//情況三
-?(void)case3?{
NSLog(@"case?3?Click");
[self.teacher?requestData:^(NSData?*data)?{
self.name?=?@"case?3";
}];
}
內(nèi)存泄漏,循環(huán)引用,原因就是相互強引用導(dǎo)致
//情況四
-?(void)case4?{
NSLog(@"case?4?Click");
[self.teacher?requestData:^(NSData?*data)?{
self.name?=?@"case?4";
self.teacher?=?nil;
}];
}
不存在內(nèi)存泄漏了和循環(huán)引用,在block執(zhí)行結(jié)束后主動釋放了block的持有者,最終或?qū)е聅elf的dealloc執(zhí)行.所以并不會內(nèi)存泄漏,常常會看見有些大神在解決block的問題時在block執(zhí)行結(jié)束后手動釋放掉block,原理就是如此.
//情況五
-?(void)case5?{
NSLog(@"case?5?Click");
Teacher?*t?=?[[Teacher?alloc]?init];
[t?requestData:^(NSData?*data)?{
self.name?=?@"case?5";
}];
}
不會造成循環(huán)引用,不會內(nèi)存泄漏,因為是局部變量持有block,在這個方法的大括號內(nèi)部有效,出了大括號局部變量就會被釋放.所以不存在內(nèi)存問題.
//情況六
-?(void)case6?{
NSLog(@"case?6?Click");
[self.teacher?callCase6BlackEvent];
self.teacher.case6Block?=?^(NSData?*data)?{
self.name?=?@"case?6";
//下面兩句代碼任選其一
self.teacher?=?nil;
//????????self.teacher.case6Block?=?nil;
};
}
self.teacher?=?nil; , self.teacher.case6Block?=?nil;這兩句代碼任選其一即可解決內(nèi)存泄漏,因為一端被置為nil,就會打破循環(huán)引用問題.
3.實際項目中可能會遇到AFNetworking的內(nèi)存泄漏問題
為什么會有這樣的問題?
在實例化?AFHTTPSessionManager中如果進行多次調(diào)用,就會導(dǎo)致內(nèi)存泄漏例如在項目中同事發(fā)起多個請求,創(chuàng)建多個manager就會有這樣的問題.
[[AFHTTPSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
解決辦法:采用單例的方式創(chuàng)建,或者是繼承AFHTTPSessionManager并將該類做成一個單例類
4.在for循環(huán)較大數(shù)據(jù)時,不斷創(chuàng)建局部變量導(dǎo)致的內(nèi)存泄漏問題
for (NSDictionary *dic in regionList) {
@autoreleasepool {
RegionList *model = [RegionList yy_modelWithDictionary:dic];
NSString *city;
if (model.city.length == 0) {
city = [NSString stringWithFormat:@"%@",model.region];
model.cityName = city;
}
else {
city = [NSString stringWithFormat:@"%@·%@",model.city,model.region];
model.cityName = city;
}
if (city.length > 0) {
model.cityPinyin = [city pinYin];
NSString *str = [model.cityPinyin uppercaseString];
model.firstChar = [str substringToIndex:1];
}
[regionListModel addObject:model];
}
}
舉例:
在項目中可能會遇到從服務(wù)端獲取城市列表,全國大概有三百多個市,每個市估算有10個區(qū)那么就會有3000多條數(shù)據(jù).
利用yymodel解析從后臺拿到的數(shù)據(jù)時是不是需要在for循環(huán)中創(chuàng)建3000多個局部變量.3000多個局部變量占用的內(nèi)存將會是爆發(fā)式的增長.到底有多恐怖還請自己測一下,可能3000看不出多少效果可以給大點數(shù)字試試.
結(jié)論:
這個for循環(huán)里如果不使用@autoreleasepool资锰,那臨時變量內(nèi)存可能是爆發(fā)式的,但是使用了@autoreleasepool,在每個@autoreleasepool結(jié)束時屡江,里面的臨時變量都會回收伟桅,內(nèi)存使用更加合理
5.mrc的基礎(chǔ)
自己生成的對象祖今,自己持有 (alloc ,new,create)
不是自己生成的對象歧斟,自己也能持有(retain,copy,mutablecopy)
誰持有紧武,誰釋放击你,不持有玉组,不能釋放,不再需要時丁侄,主動釋放 (release)
6,使用工具調(diào)試內(nèi)存泄漏問題
如何查看找到源代碼位置
選擇調(diào)用樹
這樣你就能看到你帶嗎內(nèi)存泄漏的問題所在了.
可能看到的代碼并不存在內(nèi)存泄漏問題,需要通過上下文分析內(nèi)存可能泄漏的原因.
本人聯(lián)系方式:qq:513961360
email:513961360@qq.com
也可以加我們的qq群希望能與朋友們一起聊天和學(xué)習(xí).群里還有很多iOS開發(fā)者,幫助我們解決問題,并且同時學(xué)習(xí).
qq群號:580284575