【问题标题】:Memory management in iOS , Simulator vs idevice gives different resultiOS 中的内存管理,模拟器与 idevice 给出不同的结果
【发布时间】:2014-06-06 11:13:44
【问题描述】:

我正在从服务器下载图像,这是我的代码。

-(void) DownloadImages
{
   NSLog(@"Images count to download %lu",(unsigned long)[productID count]); // about 3000
   if ([productID count] > 0)
   {
       // Document Directory
       NSString *myDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
       for (i = 0; i < [productID count]; i++)
       {
       @autoreleasepool      // is this right of use autoreleasepool???
       {
           @autoreleasepool      
           {
               [self performSelectorInBackground:@selector(UpdateUI) withObject:nil];
           }
           NSLog(@"image no %d", i);
          NSString *imageURL = [NSString stringWithFormat:@"http://serverPath/%@/OnlineSale/images/products/%@img.jpg",clientSite,[productID objectAtIndex:i]];
           UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]]];
           // Reference path
           NSString *imagePath = [NSString stringWithFormat:@"%@/%@img.jpg",myDirectory,[productID objectAtIndex:i]];
          NSData *imageData = [NSData dataWithData:UIImageJPEGRepresentation(image, 1.0f)];
          [imageData writeToFile:imagePath atomically:YES];   
        }
     }
      NSLog(@"Downloading images completed...");
  }

}

编辑:这是 UpdateUI 方法

 -(void) UpdateUI
 {
    spinner.hidden = NO;
[spinner startAnimating];
    progressCounter.hidden = NO;
    status.text = @"Synchronising Product Images...";
    progressCounter.text = [NSString stringWithFormat:@"%d / %lu", i,(unsigned long)[productID count]];
 }

在处理这个时,当我在 simulator 中时,使用的最大内存是: 超过 1500 张图片下载。

但在 iPad 上进行测试,最大内存已达到 106.9 MB,并且应用程序在下载 500 张图像后崩溃并显示此消息:

屏幕显示此消息:

从过去两天开始,我就一直停留在这一点上,挠了挠头后找不到任何解决方案。请帮我解决这个问题..

编辑: 使用 autoreleasedpool 的方式对不对???

【问题讨论】:

  • 当您使用performSelectorInBackground 时,您必须为后台线程设置自己的NSAutoreleasePool。如果不是,它会导致内存泄漏。这是不推荐的方式。您应该使用 NSOperation 和队列或 GCD 块来满足后台处理需求。
  • 至于您观察到的差异,您应该始终依赖真实设备的内存/性能。模拟器在您的 mac 硬件上运行,并且不会 100% 复制设备行为。
  • @Amar 请你给我 NSAutoreleasePool 的想法。如何使用它。?在我使用 ARC 时是否有必要?
  • 这种方法对吗?

标签: ios iphone memory-management ios7


【解决方案1】:

首先,我建议将整个 for 循环块包含在 @autoreleasepool 内。它看起来像这样:

   for (i = 0; i < [productID count]; i++)
   {
     @autoreleasepool      // is this right of use autoreleasepool???
     {
        [self performSelectorInBackground:@selector(UpdateUI) withObject:nil];
         NSLog(@"image no %d", i);
         NSString *imageURL = [NSString stringWithFormat:@"http://serverPath/%@/OnlineSale/images/products/%@img.jpg",clientSite,[productID objectAtIndex:i]];
         UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]]];
         // Reference path
         NSString *imagePath = [NSString stringWithFormat:@"%@/%@img.jpg",myDirectory,[productID objectAtIndex:i]];
         NSData *imageData = [NSData dataWithData:UIImageJPEGRepresentation(image, 1.0f)];
         [imageData writeToFile:imagePath atomically:YES];   
      }
   }

如果您尝试使用 @autoreleasepool 块来处理在 updateUI 中分配的内存,那么这不是正确的方法。您应该将 updateUI 正文包含在 @autoreleasepool 内。

我也建议发布updateUI的内容;您甚至可以评论该调用并查看仅下载是否导致内存问题。

更一般地说,对我来说,您在后台线程上更新 UI 似乎很可疑。作为一般做法,UI 应该只在主线程上更新。 (但没有看到updateUI 的定义,我真的不能说这是否可以)。

关于您使用设备或模拟器获得的不同结果,这是“正常的”。模拟器在内存处理方面完全不可靠,您还可以使用模拟器检测设备上不存在的泄漏。

编辑:

作为一个简单的补丁,试试这个方法:

for (i = 0; i < [productID count]; i++)
   {
      dispatch_async(dispatch_get_main_queue(), ^{

        [self UpdateUI];

         NSLog(@"image no %d", i);
        NSString *imageURL = [NSString stringWithFormat:@"http://serverPath/%@/OnlineSale/images/products/%@img.jpg",clientSite,[productID objectAtIndex:i]];
         UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]]];
         // Reference path
         NSString *imagePath = [NSString stringWithFormat:@"%@/%@img.jpg",myDirectory,[productID objectAtIndex:i]];
        NSData *imageData = [NSData dataWithData:UIImageJPEGRepresentation(image, 1.0f)];
        [imageData writeToFile:imagePath atomically:YES];   
      });
 }

您会注意到,我正在单独调度循环中的每个步骤。这样,邮件循环将能够更新 UI。请注意,此实现不是最佳的,因为dataWithContentsOfURL 将需要一点时间来执行(因为它需要网络访问)并且不应该在主线程上执行(但您已经这样做了)。这应该在后台队列中调度。

【讨论】:

  • 我已经按照你的建议设置了@autoreleasepool,但不好的是我现在没有我的 iPad 来测试。 :(
  • 如果我简单地将此方法称为:[self UpdateUI];它不工作,为什么要使用后台线程
  • 查看我的编辑,当你拿回你的 iPad 时,试试看。顺便说一句:我不建议单独调用updateUI,而是将其注释掉(而不是执行它)——但鉴于其内容,我认为这不是问题。问题是你在主线程的循环中执行了太多的操作。
  • 谢谢,我在 iPad 上查看后会立即通知您结果
  • 清理项目后就好了
【解决方案2】:

您应该使用更合适的方法来获取图像数据。 我建议你试试NSURLDownload class。

或者,您可以使用流行的 3rd 方网络框架之一,例如 AFNetworking

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-16
    • 1970-01-01
    • 2017-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多