代码之家  ›  专栏  ›  技术社区  ›  Quentin

异步请求和委托可能存在的风险

  •  0
  • Quentin  · 技术社区  · 14 年前

    我想在uiImageView中添加使用URL设置图像的能力。结果我想做些类似的事情。

    [anImageView setImageWithContentAtUrl:[NSURL URLWithString:@"http://server.com/resource.png"]];
    

    所以我创建了一个类别(下面的代码)。

    NSString *kUserInfoImageViewKey = @"imageView";
    NSString *kUserInfoActivityIndicatorKey = @"activityIndicator";
    
    @implementation UIImageView (asynchronous)
    
    #pragma mark -
    - (void)setImageWithContentAtUrl:(NSURL *)imageUrl andActivityIndicator:(UIActivityIndicatorView *)activityIndicatorOrNil {
       [activityIndicatorOrNil startAnimating];
    
     NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
     [dict setValue:self forKey:kUserInfoImageViewKey];
     [dict setValue:activityIndicatorOrNil forKey:kUserInfoActivityIndicatorKey];
    
     ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:imageUrl];
     request.delegate = self;
     request.userInfo = dict;
       [dict release];
     [request startAsynchronous];
    }
    
    #pragma mark -
    #pragma mark private
    
    - (void)requestFinished:(ASIHTTPRequest *)aRequest {
     // get concerned view from user info
     NSDictionary *dictionary = aRequest.userInfo;
       UIImageView *imageView = (UIImageView *)[dictionary valueForKey:kUserInfoImageViewKey];
     UIActivityIndicatorView *activityIndicator = (UIActivityIndicatorView *) [dictionary valueForKey:kUserInfoActivityIndicatorKey];
    
       [activityIndicator stopAnimating];
    
       NSData *responseData = [aRequest responseData];
     UIImage * image = [[UIImage alloc] initWithData:responseData];
    
     imageView.image = image;
     [image release];
    }
    
    - (void)requestFailed:(ASIHTTPRequest *)request {
    }
    

    创建并启动asihttpRequest,并将图像作为委托。我认为,如果在asihttpRequest返回结果之前释放图像,则存在风险。

    所以,也许在setImageWithContentAturl中添加一个retain:在requestFinished中添加一个release:而requestFailed:但是我不是很有信心。

    怎么可能做这种事?

    当做, 昆廷

    1 回复  |  直到 14 年前
        1
  •  1
  •   makdad    14 年前

    昆廷

    我经常使用asihttpRequest进行异步调用,所以我知道您从这里来的位置。而且,第一次设置是一件痛苦的事情,但是你知道吗,Three20图书馆的TimageView(我想是它)已经做了你想做的事情?它甚至会在本地缓存图像,因此您不必每次都加载它。不管怎样。

    您的担心是正确的:asihttpRequest是nsOperation对象的包装器(它实际上是一个子类),因此只要请求处于活动状态,nsOperationQueue将保留asihttpRequest。

    如果用户更改视图(例如,在导航栏控制器上),然后取消分配uiImageView,则当您的代码试图回叫代理时,可能会崩溃。因此,当您取消分配图像视图时,最好保留对请求的引用,然后取消它。

    这可能是子类化更好的时候之一,而不是类别,因为您希望覆盖dealloc方法(这是我处理这个问题的方式)。

    首先,将此属性添加到子类:

    @property (nonatomic, retain) ASIHTTPRequest *request;
    

    然后将此行添加到您的方法中,这样您就可以继续使用它:

    self.request = request;
    

    最后,在asihttpRequest委托方法中,销毁引用:

    self.request = nil;
    

    然后你的DealLoc看起来像这样:

    - (void) dealloc
    {
      if (self.request)
      {
        // Cancels the NSOperation so ASIHTTPRequest doesn't call back to this
        [self.request cancel];
      }
      [request release];
      [super dealloc]
    }