【问题标题】:Passing self from delegate is causing memory leak从委托传递 self 会导致内存泄漏
【发布时间】:2017-10-12 22:05:53
【问题描述】:

我创建了一个在目标中使用 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对象传递给调用类中实现的委托方法。

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

【问题讨论】:

    标签: ios objective-c xcode memory-management


    【解决方案1】:

    你错误地定义了 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];
    

    免责声明:这段代码仍然很糟糕,我刚刚更正它以摆脱强引用循环。

    【讨论】:

    • 没有 +(id) initWithUrl: (NSString *) urlString;我怎么能打这样的电话?服务器 *request = [服务器 initWithURL:downloadUrl];
    • Server *server = [[Server alloc] initWithUrl: url];。您的问题证明您应该阅读/观看有关 Objective C 基础知识的内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-02
    • 1970-01-01
    • 1970-01-01
    • 2011-05-23
    • 2012-05-18
    • 2014-05-18
    • 2010-12-29
    相关资源
    最近更新 更多