iOS內(nèi)存管理(三)ARC內(nèi)存泄漏

最近在對一個項目的內(nèi)存泄漏進(jìn)行排查,發(fā)現(xiàn)存在很多內(nèi)存泄漏,還有第三方類庫里面存在對象釋放不掉的情況

AFNetWorking

作為一位iOS開發(fā)者,網(wǎng)絡(luò)請求類AFNetWorking是再熟悉不過了,如果你不知道,那你就太奧特了。對于AFNetWorking的使用通常會對用參數(shù)、網(wǎng)址環(huán)境切換、網(wǎng)絡(luò)狀態(tài)監(jiān)測、請求錯誤信息等進(jìn)行封裝。在封裝請求類時需要注意的是需要請求隊列管理者AFHTTPSessionManager聲明為單例創(chuàng)建形式。對于這個問題,AFNetWorking的作者在github上也指出建議使用者在相同配置下保證AFHTTPSessionManager只有一個,進(jìn)行全局管理,因此我們可以通過單例形式進(jìn)行解決。

@implementation MXHTTPRequst
+ (AFHTTPSessionManager *)manager {
    static AFHTTPSessionManager *manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [AFHTTPSessionManager manager];
        manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    });
    return manager;
}
+ (void)GET:(NSString *)url parameters:(NSDictionary *)getDictionary returnData:(void (^)(NSData * resultData,NSError * error))returnBlock {
    //請求隊列管理者 單例創(chuàng)建形式 防止內(nèi)存泄漏
    AFHTTPSessionManager *manager = [self manager];
    [manager GET:url parameters:getDictionary progress:^(NSProgress * _Nonnull downloadProgress) {
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        returnBlock(responseObject,nil);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        returnBlock(nil,error);
    }];
}
@end

Block循環(huán)引用

Block循環(huán)引用的問題是老生常談了,至今網(wǎng)上已經(jīng)有很多文章解釋原理及造成循環(huán)引用的原因,在下就不逼逼了??傊涀∫痪湓挕皩ο笾g不要出現(xiàn)封閉的環(huán)”,就能防止Block循環(huán)引用。我就拿MJRefresh列舉一下吧,其他的代碼太多了??:

self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
        self.page = 1;
        [self.dataArr removeAllObjects];
        [self loadData];
}];

若在MJRefresh的執(zhí)行Block中調(diào)用當(dāng)前self或者屬性,一定要注意循環(huán)引用問題

#pragma mark - 構(gòu)造方法
+ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock
{
    MJRefreshHeader *cmp = [[self alloc] init];
    cmp.refreshingBlock = refreshingBlock;
    return cmp;
}

上面為MJRefreshNormalHeader的構(gòu)造方法,注意看一下這一行代碼“cmp.refreshingBlock = refreshingBlock”,refreshingBlock是屬于MJRefreshHeader的強(qiáng)引用屬性,最后header會成為我們自己tableView的強(qiáng)引用屬性mj_header,也就是說self.tableView強(qiáng)引用header, header強(qiáng)引用refreshingBlock,如果refreshingBlock里面強(qiáng)引用self,就成了循環(huán)引用,如圖:


image

所以需要破掉這個環(huán)就得使用weakSelf

__weak id weakSelf = self;
self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
          __strong typeof(self) strongSelf = weakSelf;
        strongSelf.page = 1;
        [strongSelf.dataArr removeAllObjects];
        [strongSelf loadData];
}];

結(jié)果為
image

delegate循環(huán)引用

delegate是一個非?;A(chǔ)的問題了,吧修飾符改成weak就??:@property (nonatomic,weak)id delegate;
這里就不再贅述
比較形象的事UITextView和UIViewController

image

NSTimer循環(huán)引用

對于定時器NSTimer,使用不正確也會造成內(nèi)存泄漏問題,在初始化傳入target參數(shù)時記得用weak,還有當(dāng)不需要定時器時需要執(zhí)行這兩行代碼:

 [_timer invalidate];
 _timer = nil;

非OC對象內(nèi)存處理

對于iOS開發(fā),ARC模式已發(fā)揚(yáng)光大多年,可能很多人早已忘記當(dāng)年retain、release的年代,但ARC的出現(xiàn)并不是說我們完全可以忽視內(nèi)存泄漏的問題。對于一些非OC對象,使用完畢后其內(nèi)存仍需要我們手動釋放。

舉個例子,比如常用的濾鏡操作調(diào)節(jié)圖片亮度

CIImage *beginImage = [[CIImage alloc]initWithImage:[UIImage imageNamed:@"yourname.jpg"]];

CIFilter *filter = [CIFilter filterWithName:@"CIColorControls"];

[filter setValue:beginImage forKey:kCIInputImageKey];

[filter setValue:[NSNumber numberWithFloat:.5] forKey:@"inputBrightness"];//亮度-1~1

CIImage *outputImage = [filter outputImage];

//GPU優(yōu)化

EAGLContext * eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];

eaglContext.multiThreaded = YES;

CIContext *context = [CIContext contextWithEAGLContext:eaglContext];

[EAGLContext setCurrentContext:eaglContext];

CGImageRef ref = [context createCGImage:outputImage fromRect:outputImage.extent];

UIImage *endImg = [UIImage imageWithCGImage:ref];

_imageView.image = endImg;

CGImageRelease(ref);//非OC對象需要手動內(nèi)存釋放

在如上代碼中的CGImageRef類型變量非OC對象,其需要手動執(zhí)行釋放操作CGImageRelease(ref),否則會造成大量的內(nèi)存泄漏導(dǎo)致程序崩潰。其他的對于CoreFoundation框架下的某些對象或變量需要手動釋放、C語言代碼中的malloc等需要對應(yīng)free等都需要注意。

地圖類處理

如果項目中使用抵觸相關(guān)類,一定要檢測內(nèi)存情況,因為地圖是相當(dāng)耗費(fèi)內(nèi)存的,因此根據(jù)文檔實現(xiàn)地圖相關(guān)功能的同時,還必須注意內(nèi)存的正確釋放,大體需要注意的有需在使用完畢時將地圖、代理賦值為nil,注意地圖中的大頭針的服用,并且在使用完畢時清空大頭針數(shù)組等:

- (void)clearMapView{
    self.mapView = nil;
    self.mapView.delegate =nil;
    self.mapView.showsUserLocation = NO;
    [self.mapView removeAnnotations:self.annotations];
    [self.mapView removeOverlays:self.overlays];
    [self.mapView setCompassImage:nil];
}

大次數(shù)循環(huán)內(nèi)存暴漲問題

for (int i = 0; i < 100000; i++) {
        NSString *string = @"Abc";
        string = [string lowercaseString];
        string = [string stringByAppendingString:@"xyz"];
        NSLog(@"%@", string);
}

該循環(huán)內(nèi)產(chǎn)生大量的臨時對象,直至循環(huán)結(jié)束才釋放,可能導(dǎo)致內(nèi)存泄漏,解決方法在循環(huán)體內(nèi)加入自己的@autoreleasepool,用來及時釋放大量的臨時變量,減少內(nèi)存占用峰值。

for (int i = 0; i < 100000; i++) {
    @autoreleasepool{
        NSString *string = @"Abc";
        string = [string lowercaseString];
        string = [string stringByAppendingString:@"xyz"];
        NSLog(@"%@", string);
    }
}
最后編輯于
?著作權(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)容

  • 為自己學(xué)習(xí)方便,復(fù)制大神的學(xué)習(xí)性文章放在自己簡書里,僅作為學(xué)習(xí)方便使用,如果作者疑此行為侵權(quán),請隨時聯(lián)系本人刪除,...
    天地不仁以萬物為芻狗閱讀 4,997評論 2 3
  • 內(nèi)存管理 簡述OC中內(nèi)存管理機(jī)制。與retain配對使用的方法是dealloc還是release,為什么?需要與a...
    丶逐漸閱讀 2,081評論 1 16
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,553評論 19 139
  • 孩子長大了,快上小學(xué)了,沒有辦法自己單獨(dú)睡,和大人一個房間又相互影響睡眠質(zhì)量, 去上廁所,如果是黑著的,需要大人陪...
    小懶與小勤閱讀 965評論 4 7
  • “食不語,寢不言”,出自《論語·鄉(xiāng)黨》。 意思是說在吃飯時,不可聊天;睡覺時,也不能說話。然而,很多人喜歡吃飯...
    杏林園中一顆草閱讀 2,045評論 0 0

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