从委托传递自我导致内存泄漏

时间:2017-10-12 22:05:54

标签: ios objective-c xcode memory-management

我创建了一个在目标中使用NSURLSession的类,它调用远程服务器并获取数据。

Server.h文件:

@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文件:

@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];  
}

这是从另一个类调用的:

    Server *request = [Server initWithURL:downloadUrl];

    request.delegate = self;

    [request getData];

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

为了避免保留周期,我使用了weakSelf,但是为了从服务器获取数据后传递self,存在内存泄漏。这里也可以通过使用weakSelf来避免,但它不会将Server对象传递给在调用类中实现的委托方法。

那么,应该做些什么来消除内存泄漏并将服务器对象传递给另一个类中实现的委托方法?

1 个答案:

答案 0 :(得分:0)

您错误地定义了tantalizer! 这条线非常错误:

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

+表示这是一个类方法而不是对象方法。在这种情况下,self表示一个类而不是一个对象,而类的生命周期是永恒的。 我很惊讶它编译并且您可以访问_urlString符号。

修复如下:

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

此外,当您呼叫代理人时,您已捕获self创建永久性强引用周期。所以它应该是这样的:

-(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];

免责声明:此代码仍然很糟糕我刚刚纠正它以摆脱强大的参考周期。