【问题标题】:NSURLConnection not "firing" until UITableView scrollsNSURLConnection 在 UITableView 滚动之前不会“触发”
【发布时间】:2010-06-09 06:19:59
【问题描述】:

我有一个 UITableView,它异步加载图像并在加载后将其放置在 UITableViewCell 中(我使用的代码与“LazyTableImages”教程中的代码几乎完全相同)。当我滚动表格时,这适用于所有图像,但它不会加载视图中的第一个图像。

代码确实可以正常工作,因为正确调用了实际发送 NSURLConnection 请求的类(我添加了一个 NSLog 并到达了控制台)。 NSURLConnection 只是不调用委托方法(didReceiveData、connectionDidFinishLoading 等)。

这是我的代码:


HomeController.m

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

 if (cell == nil) {

        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

  NSArray *feed = [feeds objectAtIndex: indexPath.row];

  /**
    * Name of person
   */
  [...]

  /**
    * Feed entry
   */
  [...]

  /**
    * Misc work
   */
  [...]

 }

 FeedRecord *feedRecord = [self.entries objectAtIndex:indexPath.row];

 if( !feedRecord.image ) {

  if (self.table.dragging == NO && self.table.decelerating == NO)
  {
   [self startIconDownload:feedRecord forIndexPath:indexPath];
  }

  cell.imageView.image = [UIImage imageNamed:@"Placeholder.png"];                

 }

    return cell;
 }

    - (void)startIconDownload:(FeedRecord *)feedRecord forIndexPath:(NSIndexPath *)indexPath
    {
        IconDownloader *iconDownloader = [imageDownloadsInProgress objectForKey:indexPath];
        if (iconDownloader == nil) 
        {
            iconDownloader = [[IconDownloader alloc] init];
            iconDownloader.feedRecord = feedRecord;
            iconDownloader.indexPathInTableView = indexPath;
            iconDownloader.delegate = self;
            [imageDownloadsInProgress setObject:iconDownloader forKey:indexPath];
            [iconDownloader startDownload];
            [iconDownloader release];   
        }
    }

IconDownload.m

#import "IconDownloader.h"
#import "FeedRecord.h"

#define kAppIconHeight 48

@implementation IconDownloader

@synthesize feedRecord;
@synthesize indexPathInTableView;
@synthesize delegate;
@synthesize activeDownload;
@synthesize imageConnection;

#pragma mark

- (void)dealloc
{
    [feedRecord release];
    [indexPathInTableView release];

    [activeDownload release];

    [imageConnection cancel];
    [imageConnection release];

    [super dealloc];
}

- (void)startDownload
{
 NSLog(@"%@ %@",@"Started downloading", feedRecord.profilePicture); // this shows in log
    self.activeDownload = [NSMutableData data];
    // alloc+init and start an NSURLConnection; release on completion/failure
    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:
                             [NSURLRequest requestWithURL:
                              [NSURL URLWithString:feedRecord.profilePicture]] delegate:self];
    self.imageConnection = conn;
 NSLog(@"%@",conn); // this shows in log
    [conn release];
}

- (void)cancelDownload
{
    [self.imageConnection cancel];
    self.imageConnection = nil;
    self.activeDownload = nil;
}


#pragma mark -
#pragma mark Download support (NSURLConnectionDelegate)

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
 NSLog(@"%@ %@",@"Got data for", feedRecord.profilePicture);
    [self.activeDownload appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
 NSLog(@"%@",@"Fail!");
 // Clear the activeDownload property to allow later attempts
    self.activeDownload = nil;

    // Release the connection now that it's finished
    self.imageConnection = nil;
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
  NSLog(@"%@ %@",@"Done", feedRecord.profilePicture);
   // Set appIcon and clear temporary data/image
    UIImage *image = [[UIImage alloc] initWithData:self.activeDownload];

 self.feedRecord.image = image;

    self.activeDownload = nil;

 [image release];

    // Release the connection now that it's finished
    self.imageConnection = nil;

 NSLog(@"%@ %@",@"Our delegate is",delegate);
    // call our delegate and tell it that our icon is ready for display
    [delegate feedImageDidLoad:self.indexPathInTableView];
}

@end

是否有其他人遇到过类似的情况或可以识别我的代码存在问题?谢谢!

【问题讨论】:

    标签: iphone objective-c uitableview nsurlrequest


    【解决方案1】:

    您可以使用此代码

    [tableView performSelector:@selector(reloadData) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES];
    

    改为

    [tableView reloadData];
    

    【讨论】:

      【解决方案2】:

      您不会调用您在 startDownload 方法中创建的 NSURLConnection 对象的 start 方法。

      一定要这样做:

      - (void)startDownload
      {
          NSLog(@"%@ %@",@"Started downloading", feedRecord.profilePicture); // this shows in log
          self.activeDownload = [NSMutableData data];
          // alloc+init and start an NSURLConnection; release on completion/failure
          NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:
                                  [NSURLRequest requestWithURL:
                                  [NSURL URLWithString:feedRecord.profilePicture]] delegate:self];
          self.imageConnection = conn;
          NSLog(@"%@",conn); // this shows in log
          [conn start];
          [conn release];
      }
      

      你也可以使用构造函数:initWithRequest:delegate:startImmediately:

      此外,如果用户滚动,您的下载将被阻止,因为它们正在运行的运行循环。只需在“常用模式”中注册您的连接:

      [conn scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
      

      摘自:how-to-avoid-blocked-downloads-during-scrolling

      【讨论】:

        【解决方案3】:

        您没有启动我们的NSURLConnection。要么用-[NSURLConnection initWithRequest:delegate:startImmediately:]初始化它,要么在初始化后手动调用-[NSURLConnection start]

        【讨论】:

        • 不。我得到的代码将初始化请求并立即开始下载 URL。 (Apple 的方法文档:“返回一个初始化的 URL 连接并开始加载 URL 请求的数据。”)。 [NSURLConnection start] 也不起作用:(
        • 很抱歉我误读了文档。实际上,我看不到您的代码有问题。是否在 startDownload 方法中设置了断点并检查是否一切都如预期的那样?
        【解决方案4】:

        我也有同样的问题。此外,我几乎使用与您相同的代码(来自 Apple 示例 LazyTableImages)。

        虽然 Apple 的测试项目中的代码有效,但它在我的项目中不起作用 - 尽管我只是复制了 Apple 的代码。

        我发现的是:我用的时候

        NSLog(@"Is%@ main thread", ([NSThread isMainThread] ? @"" : @" NOT"));
        

        在 cellForRowAtIndexPath: 以及 IconDownload.m 的 startDownload: 中(在两个项目中),我发现它是 Apple 示例中的主线程,但在我的代码中 NOT 主线程。 p>

        这可能是问题所在。

        知道怎么解决吗?


        编辑!!!解决了!

        我只是强制使用主线程

        NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:entry.imageURL, @"imageURL", indexPath, @"indexPath", nil];
        [self performSelectorOnMainThread:@selector(startIconDownload:) withObject:info waitUntilDone:NO];
        

        在 cellForRowAtIndexPath 中:您需要一个字典来向该方法发送多个参数。

        您可以在代码中执行类似的解决方案。换行:

        [self startIconDownload:feedRecord forIndexPath:indexPath];
        

        用我的代码修改 startIconDownload: 像这样

        - (void)startIconDownload:(NSDictionary *)info
        {
            NSString *url = [info objectForKey:@"imageURL"];
            NSIndexPath *indexPath = [info objectForKey:@"indexPath"];
            ...
        }
        

        您的应用中的某些变量可能会有所不同。

        但我只是不明白为什么它在不强制主线程的情况下在 Apple 的示例中工作。

        有什么想法吗?

        【讨论】:

          【解决方案5】:

          看看这里:http://www.depl0y.com/?p=345 也许会有所帮助。

          编辑:是的,正在为我工​​作。让我知道是否也适合您,或者您需要更多信息。

          【讨论】:

            猜你喜欢
            • 2015-11-26
            • 1970-01-01
            • 1970-01-01
            • 2023-04-11
            • 2012-06-08
            • 2019-01-28
            • 1970-01-01
            • 1970-01-01
            • 2017-06-18
            相关资源
            最近更新 更多