【问题标题】:Cleaning up hanging NSURLConnections when a controller is dealloc'ed释放控制器时清理挂起的 NSURLConnections
【发布时间】:2010-09-08 06:48:40
【问题描述】:

我有一个控制器,它使用自定义类发出 HTTP GET 请求,该类充当NSURLConnection 的委托。一旦NSURLConnection 失败或完成,自定义类调用控制器上的方法并传递接收数据的NSData 对象。

我遇到了一个问题,即动态创建正在运行的控制器并将其推送到导航控制器的堆栈中。该控制器在其viewDidLoad 方法中发出HTTP GET 请求。如果用户在导航栏上快速按下“返回”,这个控制器就会被释放。如果这发生在 HTTP GET 请求完成之前,则生成的 NSURLConnection 回调将成为对已解除分配对象的方法调用,从而导致 EXC_BAD_ACCESS。

清理任何由控制器启动但实际上可能已经被释放的待处理NSURLConnections 的最佳方法是什么?

我添加了一些NSLog 语句,似乎我用作NSURLConnection 委托的自定义类实际上并没有收到dealloc 消息。我确保在 viewDidUnload 中将控制器的此类实例设置为 nil,并对其调用 release,但它似乎仍然比控制器寿命更长。

【问题讨论】:

    标签: iphone objective-c memory-management nsurlconnection nsurlrequest


    【解决方案1】:

    如果我理解正确,您只需在 viewDidUnload 或 dealloc 方法中执行 [whateverConnection cancel] 即可。这将取消连接。如果你有一个自定义的下载器对象,例如一些使用 NSURLConnection 的大图像,这几乎是一样的。为您的类创建一个取消方法(取消连接并释放它)并在控制器的 dealloc 方法中调用它。您还应该使用类似 wasCanceled 的 bool 标志,并且如果从您的取消方法中设置了 wasCanceled,则不要从您的自定义对象的委托中调用任何方法。 (您只有一个指向您的委托的弱指针,因此当其他对象调用您的取消方法时,它可能已经被释放)。我假设您的自定义对象的委托是视图控制器。我有几个这样的下载器,它运行良好,即使我很快取消下载也没有泄漏。

    @interface CompaniesDownloader : NSObject /*<NSXMLParserDelegate>*/
    {
        id<CompaniesDownloaderDelegate> delegate; //a view controller is the delegate
        NSMutableArray *companies;
    
        BOOL isWorking;    
        BOOL wasCanceled;   
    
        @private
    
        //url connection object
        NSURLConnection *companiesConnection;
    
        //this is where i put the binary data that gets transformed into xml
        NSMutableData *webData;
    
        //temporary string used when parsing xml
        NSMutableString *tmpString;
    
        //temporary company used when parsing xml
        Company *tmpCompany;
    }
    

    在实现中:

    -(void) cancel
    {
      [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: FALSE];
    
      wasCanceled = TRUE;
      [companiesConnection cancel];
      [webData release];
      webData = nil;
      self.companiesConnection = nil; //OR [companiesConnection release]; companiesConnection=nil;
      isWorking = FALSE;
    }
    

    【讨论】:

    • 酷,这听起来像是要走的路。这或多或少是一些 Apple 示例代码在使用本质上只是 NSURLConnections 的委托的自定义下载器对象时所做的(参见 LazyTableLoading)。
    • 这种方式唯一的问题是,如果你调用一个库来做网络工作,那么你就不太可能调用它的取消:(
    • 满足我的网络需求,这就足够了。如果有什么问题请告诉我,其实我在做这个的时候并没有看苹果样品。
    【解决方案2】:

    您需要在发出请求时保留视图控制器,并在获取请求完成时释放

    YourViewController.m
    - (void)callGetRequest {
       [self retain];
    }
    
    - (void)didFinishAllGetTask {
       [self release];
    }
    

    【讨论】:

    • 我以前从未在任何 Objective-C 代码中看到过这种模式。它的代码味道很糟糕。
    • 我看到很多人这样做,这是怎么回事?我认为当你想保留你的视图控制器时这是有道理的
    • 仅仅因为“很多人”做某事并不意味着它是一个好主意。至于哪里出了问题,你这里的一个基本问题是对象覆盖了它自己的自然生命周期——创建对象的东西应该负责它的生命周期。不管怎样,这种方法是错误的解决方案。问题是当一个对象调用它的委托时,但它的委托是dealloced - 所以解决方案是确保不能调用委托,不要让视图控制器(可能无限期地)停留。
    • 换个角度想也没有错:当对象调用它的委托时,这个委托可以被释放,那么我们不应该让委托释放直到它的所有对象已经结束了。
    • 在 Objective-C 中,你不能 100% 负责一个对象的生命周期,你可以通过声明保留来拥有它,并通过在该对象上调用 release 来停止拥有它。所以,对我来说,对象可以拥有自己是有道理的。我说很多人这样做是因为您的第一个论点是“我从未见过这种模式”
    【解决方案3】:

    如果用户在导航栏上快速按下“返回”,这个控制器就会被释放。如果这发生在 HTTP GET 请求完成之前,则生成的 NSURLConnection 回调将成为对已释放对象的方法调用,从而导致 EXC_BAD_ACCESS。

    当用户点击后退按钮时,取消注册视图控制器类作为该对象的委托。 (在视图控制器类中保留对该对象的引用,以便您可以执行类似someObject.delegate = nil; 的操作)。您可以在视图控制器的 dealloc 方法中执行此操作。

    【讨论】:

    • 如果你的班级保留了someObject,它会在什么时候释放你的someObject;您不能在您的委托 dealloc 方法中释放它,因为它仍在使用网络
    • @vodkhang 请再读一遍我的回答,因为你不明白。我没有说要释放该对象。我说过要取消设置对象的委托,这可以在委托的dealloc方法中完成。
    • 我了解到您需要取消设置对象的委托。但我的问题是对象本身怎么样。你创建对象 (A),调用对象 A 做网络工作,然后谁以及何时释放对象 A?我认为你这里有内存泄漏
    • 如果视图控制器正在创建对象,那么它应该在对象本身为dealloced 时销毁该对象。
    • 但是你的对象还在做网络工作,对吧?如果你在你的 dealloc 中销毁对象,那么你不需要设置 someObject.delegate = nil,对吧?再看viewcontroller的dealloc方法。你有两条线someObject.delegate = nil; [someObject release];。那你为什么需要第一行呢?你的 someObject 也可能是某人做网络的代表,而你仍然崩溃
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-17
    • 1970-01-01
    • 2012-02-13
    • 2012-10-26
    • 2017-12-31
    • 2023-03-15
    • 1970-01-01
    相关资源
    最近更新 更多