在 iPad 编程中使用多线程时出现问题(用于加载大量图像)

Posted

技术标签:

【中文标题】在 iPad 编程中使用多线程时出现问题(用于加载大量图像)【英文标题】:Problem while using Multi - threading in iPad programming (for loading large number of images) 【发布时间】:2011-06-03 10:58:03 【问题描述】:

我应该将大量图像加载到滚动视图上,我想这需要一些时间和内存。所以,我使用一个单独的线程在后台加载这些图像。我使用了以下多线程方法

[NSThread detachNewThreadSelector:@selector(prepareSliderView) toTarget:self withObject:nil];

在单独的线程上加载图像。在实现此方法时,我在 GDB 中收到以下消息

NSAutoreleaseNoPool(): Object 0x10647180 of class NSPathStore2 autoreleased with no pool in place - just leaking
NSAutoreleaseNoPool(): Object 0x10647300 of class NSPathStore2 autoreleased with no pool in place - just leaking
NSAutoreleaseNoPool(): Object 0x10646a00 of class NSCFString autoreleased with no pool in place - just leaking
NSAutoreleaseNoPool(): Object 0x10647480 of class NSPathStore2 autoreleased with no pool in place - just leaking
NSAutoreleaseNoPool(): Object 0x10646a20 of class UIImage autoreleased with no pool in place - just leaking

我不知道这意味着什么,因为我以前从未执行过此方法。所以,我的问题是

1 - 我应该在此方法中使用一些 autoRelease 池

2 - 有没有其他方法可以加载大量图像而无需在主线程中放置大量负载

-(IBAction)prepareSliderView
NSLog(@"Preparing slider view");

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

int totalPages=[kbDataSource numberOfPagesInBook];
sliderScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height-300, self.view.frame.size.width,200 )];
sliderScrollView.contentSize = CGSizeMake(totalPages*150+((totalPages-1)*10), 200); 
sliderScrollView.backgroundColor=[[UIColor blackColor] colorWithAlphaComponent:1.0];
thumbnailContentView = [[UIView alloc] initWithFrame:CGRectMake(10, 0, sliderScrollView.contentSize.width, sliderScrollView.contentSize.height)];

newPageSlider.continuous=YES;
newPageSlider.minimumValue=1;
newPageSlider.maximumValue=totalPages;

 thumbnailFrame = CGRectMake(0, 25, 120, 150);
 pageNumFieldFrame = CGRectMake(0, thumbnailFrame.size.height+10, thumbnailFrame.size.width, 50);
for(int i=1;i<totalPages;i++)
    thumbnailView = [[UIView alloc] initWithFrame:thumbnailFrame];
    thumbnailView.backgroundColor=[UIColor clearColor];
    UIButton *thumbnail = [[UIButton alloc] initWithFrame:thumbnailFrame];
    UILabel *pageNumField = [[UILabel alloc] initWithFrame:pageNumFieldFrame];
    pageNumField.backgroundColor=[UIColor clearColor];
    thumbnail.tag=i+1;
    thumbnailView.tag=i+1;

    id<PageDataSource> pd = [kbDataSource pageAtIndex:i];

    thumbnailFrame = CGRectMake(thumbnailFrame.origin.x+150+10, thumbnailFrame.origin.y, 120, 150);
    pageNumFieldFrame = CGRectMake(pageNumFieldFrame.origin.x+150+10, pageNumFieldFrame.origin.y, thumbnailFrame.size.width, 50);
    [thumbnail setBackgroundImage:[pd thumbnailImageForPageNumber:i] forState:UIControlStateNormal];
    pageNumField.text =[NSString stringWithFormat:@"Page %d",i];
    pageNumField.textColor=[UIColor whiteColor];
    [thumbnailContentView addSubview:thumbnailView];
    [sliderScrollView addSubview:pageNumField];
    [sliderScrollView addSubview:thumbnail];
    [sliderScrollView addSubview:thumbnailView];
    [sliderScrollView bringSubviewToFront:thumbnail];
    [sliderScrollView bringSubviewToFront:pageNumField];
    [self.view bringSubviewToFront:thumbnail];
    [thumbnailView release];
    [pageNumField release];
    [thumbnail release];
    [thumbnail addTarget:self action:@selector(navigateToPage:) forControlEvents:UIControlEventTouchUpInside];
    [pool release];

【问题讨论】:

【参考方案1】:

对于您在新线程中执行的每个 main 方法,无论是通过NSThread 还是通过performsSelectorInBackground:,您都必须像这样创建一个自动释放池:

- (void)myBackgroundOperation

   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

   // Do some work.

   // At the very end, release the pool.
   [pool release];

另请参阅:Why is there no autorelease pool when I do performSelectorInBackground

但对于您的任务,我建议您也查看NSOperationQueue。例如见文章Cocoa Tutorial: NSOperation and NSOperationQueue。

【讨论】:

【参考方案2】:

作为documentation says:

aSelector 负责的方法 用于设置自动释放池 新分离的线程和释放 在它退出之前的那个池

所以,在您的prepareSliderView 方法中,您需要创建并释放一个自动释放池:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

// some work

[pool release];

【讨论】:

嗨,斯蒂芬,在我的 prepareSliderView() 中,我在开始时创建了一个自动释放池,并在方法结束时释放了它......但是 GDB 仍然显示相同的输出?我在这里错过了什么吗?? prepareSliderView 是如何工作的? release 语句需要在 thread 的生命周期结束时,而不一定是方法。这意味着如果您使用异步方法,它会稍微复杂一些。 prepareSliderView() 创建了很多视图、按钮和标签,并将它们全部添加到主滚动视图中。顾名思义,它创建并准备好显示滚动视图。 我在我的问题中添加了 prepareSliderView()。请看一下。 不,您不能在 dealloc 中释放池,因为 dealloc 将在主线程上运行。此外,您不应该从主线程以外的任何地方更新 UI。

以上是关于在 iPad 编程中使用多线程时出现问题(用于加载大量图像)的主要内容,如果未能解决你的问题,请参考以下文章

iPhone:在多线程环境中发布 UIViewController 时出现问题

iOS - 在 iPad 上测试时出现白屏

使用条件变量多线程时出现意外行为

链接到多线程调试 DLL 库时出现链接错误

在 UIWebView 中使用 tiff 和 gif 格式图像时出现问题

pandas df 在多线程中附加更改变量:为 df 创建初始索引时出现问题,pd 是不是是正确的工具?