【问题标题】:Where did these lines of code memory leak in this ARC code?这些 ARC 代码中的这些代码行内存泄漏在哪里?
【发布时间】:2023-08-03 06:57:01
【问题描述】:
[client enqueueBatchOfHTTPRequestOperations:@[profileOperation,commFriendsOperation] progressBlock:^(NSUInteger finished, NSUInteger total){} completionBlock:^(NSArray *completedRequests){
//Code here
}]

以下代码位于 AFNetworking 批处理请求成功块中。仪器表明,正如它所评论的那样,代码爆炸存在内存泄漏。但是,我无法弄清楚它是如何泄漏的,我该如何修复它,有人知道吗?

提前致谢。

AFHTTPRequestOperation *commFriendsRequest = [completedRequests objectAtIndex:1];
    if(!commFriendsRequest.error&&commFriendsRequest.responseData){
        //Leak 46.2%
        NSDictionary *commDic = [NSJSONSerialization JSONObjectWithData:commFriendsRequest.responseData options:NSJSONReadingMutableContainers error:NULL];
        int status1 = [[commDic objectForKey:@"status"]intValue];
        if(status1 == kSuccessCode){
            NSArray *commFriendsArray = [commDic objectForKey:@"data"];
            if(commFriendsArray&&commFriendsArray.count){
                //Leak 30.8%
                NSMutableArray *userItems = [NSMutableArray arrayWithCapacity:commFriendsArray.count];
                for(NSDictionary *dic in commFriendsArray){
                    //Leak 23.1%
                    userItem *item = [[userItem alloc]initFromServer:dic];
                    [userItems addObject:item];
                }
                if(profile){//Both Success
                    profile.commonFriends = userItems;
                    successBlock(profile);//block call back
                }else{
                    if(status0==kSuccessCode){
                        successBlock(userItems);//block call back
                    }else{
                        NSError *error = [NSError errorWithDomain:kErrorDomain code:status0 userInfo:nil];
                        failedBlock(error);//block call back
                    }
                }
            }else if(status0==kSuccessCode){//CommFriends 0 has profile
                successBlock(profile);//block call back
            }
        }else{
            if(status0==kSuccessCode){//CommFriends failed has profile
                successBlock(profile);//block call back
            }else{//Both failed
                NSError *error = [NSError errorWithDomain:kErrorDomain code:status1 userInfo:nil];
                failedBlock(error);//block call back
            }
        }
    }else{
        failedBlock(commFriendsRequest.error);
    }


//[[userItem alloc] initFromServer:dict] code
-(id)initFromServer:(NSDictionary *)dict{
if ((self = [super init])) {
    NSString *_userID = [dict objectForKey:@"userID"];
    NSString *_userName = [dict objectForKey:@"userName"];
    genderType _userGender = [[dict objectForKey:@"userGender"] integerValue];
    NSString *_userHeadImageURL = [dict objectForKey:@"userHeadImageURL"];
    NSString *_source = [dict objectForKey:@"source"];
    int _likeCount = [[dict objectForKey:@"likedCount"] intValue];
    int _sameFriends = [[dict objectForKey:@"sameFriends"] intValue];
    NSString *_homePage = [dict objectForKey:@"homePage"];

    NSString *_createTime = [dict objectForKey:@"createTime"];
    if([dict objectForKey:@"dis"])
        _distance = [[dict objectForKey:@"dis"]floatValue];
    else
        _distance = 0;

    if ([dict objectForKey:@"uid"]) {
        self.uid = [[dict objectForKey:@"uid"] intValue];
    }
    else{
        self.uid = -1;
    }

    self.userID = _userID;
    self.userName = _userName;
    self.userGender = _userGender;
    self.userHeadImageURL = _userHeadImageURL;
    self.source = _source;
    self.likeCount = _likeCount;
    self.sameFriends = _sameFriends;
    if (_homePage && ![_homePage isKindOfClass:[NSNull class]]) {
        self.homePage = _homePage;
    }

    if([dict objectForKey:@"hasJoin"]){
        self.hasJoined = [[dict objectForKey:@"hasJoin"]boolValue];
    }

    if (_createTime && ![_createTime isKindOfClass:[NSNull class]]) {
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss z"];
        self.createTime = [dateFormatter dateFromString:_createTime];
    }

    NSString *c = [dict objectForKey:@"comment"];
    if (c && ![c isKindOfClass:[NSNull class]]) {
        self.comment = c;
    }
}
return self;

}

【问题讨论】:

  • 你是否使用ARC?
  • 抱歉添加,原因是 ARC。
  • 如果没有,泄漏的远不止这些
  • 请将代码从 userDict 类发布到您的-(id)initFromServer,泄漏可能就在其中。
  • @Cyrille 我认为这不是 initFromServer 的问题,否则仪器会告诉您。

标签: ios memory memory-leaks afnetworking


【解决方案1】:

alloccopynew创建的对象添加到容器后,应该释放它。添加消息将增加保留计数。

//Leak 23.1%
userItem *item = [[userItem alloc]initFromServer:dic];  // [item retainCount] == 1 
[userItems addObject:item];  // [item retainCount] == 2
[item release];  // [item retainCount] == 1

如果 userItems 被释放,[item retainCount] == 0,将被销毁。

【讨论】:

    【解决方案2】:

    请注意,Instruments 会告诉您泄漏对象的分配位置(因为这有助于您了解它是哪个对象),但这并不意味着泄漏位于分配位置或附近。泄漏可能是由于某些其他代码过度保留或释放对象不足引起的。您必须查看特定泄漏对象的历史记录(单击地址旁边的带圆圈箭头按钮),以查看释放未通过保留平衡的位置。

    您可能在后台线程上运行此代码,并且没有适当的自动释放池。 ARC 有时仍会为您自动释放对象,如果您调用 Apple 框架,它们可能仍然是非 ARC,因此它们绝对可以为您自动释放对象。所以你仍然需要一个自动释放池。

    Cocoa 在主线程 上为您创建一个自动释放池,但在后台线程上不为您做任何事情。如果您要在不使用 NSOperation 或其他东西的情况下将某些内容启动到后台线程,您需要将该线程包装在 @autoreleasepool 中,如下所示:

    - (void)doSomething {
        [self performSelectorInBackground:@selector(backgroundSomething)];
    }
    
    - (void)backgroundSomething {
        @autoreleasepool {
            NSLog(@"Here I am in the background, doing something.");
            myArray = [[NSMutableArray alloc] init];
            // etc.
        }
    }
    

    【讨论】:

    最近更新 更多