drewex
drewex

Reputation: 317

AFNetworking 2 Post Asynchronous Memory Leak with ARC enabled

I am pretty new to iOS coding. I've inherited a piece of code from previous programmer has a good idea to separating the code into few pieces. I hope i can make this understandable.

portion of the postservice.h

@property(nonatomic)SEL ReturnAction;
@property(nonatomic)id  ReturnDelegate;

method called in postservice.m

-(void)Send:(NSString *)Pmethod  Mparams:(NSDictionary *)Mparams Mdelegate:(id)Mdelegate IsSycn:(BOOL)IsSycn ExternalUrl:(BOOL)ExternalUrl{
Murl=[Murl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:Murl]];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    manager.requestSerializer = [AFHTTPRequestSerializer serializer];
    [manager POST:Murl parameters:Mparams success:^(AFHTTPRequestOperation *operation, id responseObject) { 
        NSString *string = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
        //NSLog(@"success %@", string);
            if ([ReturnDelegate respondsToSelector:ReturnAction]) {
                ((void (*)(id, SEL, id))[ReturnDelegate methodForSelector:ReturnAction])(ReturnDelegate, ReturnAction, string);
        }
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            //NSLog(@"error %@", error);
            if ([ReturnDelegate respondsToSelector:ReturnAction]) {
                ((void (*)(id, SEL, id))[ReturnDelegate methodForSelector:ReturnAction])(ReturnDelegate, ReturnAction, error);
            }
          }];
}

Then this is called from another mid level class methods,

portion of SE_MidPostService.h

@property(nonatomic)SEL FetchCompleted;
@property(nonatomic)SEL FetchFailed;
@property(nonatomic)id  ReturnDelegate;//unsafe_unretained
@property(nonatomic, retain)PostService * service;

example portion of SE_MidPostService.m

  -(void)GetDeviceStatus:(NSString *)DeviceID RestID:(NSNumber *)ReID
{
    NSLog(@"GetDeviceStatus");
    NSDictionary * Parameters=[[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:DeviceID,ReID, @"GetStat", nil]
                                                             forKeys:[NSArray arrayWithObjects:@"DeviceUniqueId",@"ReID", @"OP",nil]];
    [service setReturnAction:@selector(GetDeviceStatusCallBack:)];
    [service setReturnDelegate:self];
    [service Send:@"GetStat" Mparams:Parameters Mdelegate:nil IsSycn:NO ExternalUrl:NO];

}
-(void)GetDeviceStatusCallBack:(id)value{
    NSLog(@"GetDeviceStatusCallBack");
    if([value isKindOfClass:[NSError class]]) {
        if ([ReturnDelegate respondsToSelector:FetchCompleted]) {
           ((void (*)(id, SEL, id, id))[ReturnDelegate methodForSelector:FetchCompleted])(ReturnDelegate, FetchCompleted, nil, value);
        }
        return;
    }
    if ([ReturnDelegate respondsToSelector:FetchCompleted]) {
        ((void (*)(id, SEL, id, id))[ReturnDelegate methodForSelector:FetchCompleted])(ReturnDelegate, FetchCompleted, value, nil);
    }
}

And these are getting called by different parts of the code which is simplifies the code,

    SE_MidPostService * service=[[SE_MidPostService alloc] init];
    [service setReturnDelegate:self];
    [service setFetchCompleted:@selector(DeviceStatusCheck:err:)];
    [service GetDeviceStatus:uuid RestID:id;
}

-(void)DeviceStatusCheck:(NSNumber *)Result err:(NSError *)err{
    if (err!=nil) {...}
//does what ever is needed
}

Instruments mostly highlights the code where returns happen.

((void (*)(id, SEL, id, id))[ReturnDelegate methodForSelector:FetchCompleted])(ReturnDelegate, FetchCompleted, value, nil);

I've assumed ARC would take care of this but it does not. i did read some about autoreleasepool. Do i need to add this in success coding.

@autoreleasepool {
    // Code that creates autoreleased objects.
}

I hope i have enough info here. Sorry for being new in iOS, doesn't help.

Upvotes: 1

Views: 1255

Answers (2)

drewex
drewex

Reputation: 317

Just so if anyone else wants my solution of the problem is, i did get rid of the sub classes all together.

This does add extra work on coding side but it will not have any leak.

Here is the code i used. I've also watched wwdc 2013 memory management session. helps a lot.

NSString * Murl;
        Murl = [NSString stringWithFormat:@"http://%@/service.aspx",[[GlobalData sharedGlobalData].settings ServerIP]];
        Murl=[Murl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

        AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:Murl]];
        __weak typeof(self) weakmyself = self;
        manager.responseSerializer = [AFHTTPResponseSerializer serializer];
        manager.requestSerializer = [AFHTTPRequestSerializer serializer];
        [manager POST:Murl parameters:Parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
            NSString *string = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
            NSNumber *num = [NSNumber numberWithInteger: [string integerValue]];
            @autoreleasepool{
                //NSLog(@"success %@", string);
                [weakmyself Callbackfunction:num err:nil];
            }
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            //NSLog(@"error %@", error);
            NSError * err = error;
            @autoreleasepool{
                [weakmyself Callbackfunction:nil err:err];

            }
        }];

Upvotes: 1

Wil Shipley
Wil Shipley

Reputation: 9553

When you call a method in this way:

 ((void (*)(id, SEL, id, id))[ReturnDelegate methodForSelector:FetchCompleted])(ReturnDelegate, FetchCompleted, nil, value);

ARC doesn’t have any way of inserting retains and releases sensibly, so it’s understandable this would causes leaks or crashes.

I’ve read about this elsewhere, and there doesn’t seem to be a good solution to this except to switch to blocks instead of delegates, which is generally cleaner and more flexible anyhow.

Just have what was your delegate pass in a block it wants executed instead of a selector.

Upvotes: 0

Related Questions