简体   繁体   中英

Passing self from delegate is causing memory leak

I have created a class for using NSURLSession in objective which calls remote server and gets the data.

Server.h File:

@protocol SeverDelegate;

@interface Server : NSObject {

 NSString * _urlString; 

 NSURLSession * _session;
}

@property(nonatomic, weak)id <ServerDelegate> delegate;

+(id) initWithUrl: (NSString *) urlString;

- void getData();

@end

@protocol ServerDelegate<NSObject>

@optional

-(void) success:(Server *) server;

@end

Server.m File:

@implementation Server

@synthesis delegate;

+(id) initWithUrl: (NSString *) urlString {

   if (self = [super init]) {
       _urlString = urlString;
    }
}

-(void) getDataFromServer {

 NSURL *url = [NSURL URLWithString: _urlString];

__weak __typeof(self) weakSelf = self;

NSURLSessionDataTask *dataTask = [_session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

            if (error != nil) {

            }

            if (data != nil) {

                dispatch_async(dispatch_get_main_queue(), ^{

                    _responseData = data;

                    if ([weakSelf.delegate 
 respondsToSelector:@selector(success:)]) {

                        [weakSelf.delegate success:self]; // here is memory leak

                    }
                });
            }

        }];

        [dataTask resume];

        [_session finishTasksAndInvalidate];  
}

This called from another class like this:

    Server *request = [Server initWithURL:downloadUrl];

    request.delegate = self;

    [request getData];

#Pragma mark Delegate method:
-(void) success:(Server *) server {
   // do other stuff
}

In order to avoid retain cycle, I have used weakSelf but for passing self after getting data from server, there is memory leak. It can be avoided by using weakSelf here also, but it will not pass the Server object to the delegate method implemented in calling class.

So, what should be done in order to remove the memory leak and pass the server object to delegate method implemented in another class?

You have defined tantalizer incorrectly! This line is VERY WRONG:

+(id) initWithUrl: (NSString *) urlString {

+ means this is a class method not an object method. In this case self represent a class not an object and class lifetime is eternal. I'm surprised that it compiles and you have access to _urlString symbol.

Fix it like this:

-(instancetype) initWithUrl: (NSString *) urlString {
    if (self = [super init]) {
        _urlString = urlString;
    }
} 

Also when you are calling a delegate you have captured a self creating permanent strong reference cycle. So it should go like this:

-(void) getDataFromServer {
    NSURL *url = [NSURL URLWithString: _urlString];
    __weak Server *weakSelf = self;

    NSURLSessionDataTask *dataTask = [_session dataTaskWithURL:url
                                             completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error != nil) {
            // do something here
        }

        if (data != nil) {
            dispatch_async(dispatch_get_main_queue(), ^{
                Server *strongSelf = weakSelf;
                // _responseData = data; // here you are also using self and creating a cycle since you are accessing object field
                id<ServerDelegate> delegate = strongSelf.delegate;
                if ([delegate respondsToSelector:@selector(success:)]) {
                    [delegate success: strongSelf]; // here was a cycle
                }
            });
        }

    }];

    [dataTask resume];

Disclaimer: this code is still terrible I've just corrected it to get rid of strong reference cycle.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM