iPad Pro第三代杀死前景的应用程序,没有原因

问题描述 投票:0回答:1

我有一个已经使用了很多年的应用程序。

为了使离线状态下100%正常运行,此应用程序仅需要一次下载数十万张图像(每个对象1个)(需要根据需要处理增量更新)。

对象数据本身掉下来没有问题。

但是,最近,我们的应用程序仅下载图像时就开始崩溃,但仅在较新的iPad(具有足够存储空间的第三代iPad Pro)上崩溃。

图像下载过程在NSOperationQueue中使用NSURLSession下载任务。

[我们开始看到Energy Logs指出CPU使用率太高,因此我们修改了参数以使用[]在每个图像之间以及每批图像之间添加一个分隔符>

[[NSThread sleepForTimeInterval:someTime];

这将我们的CPU使用率从95%以上(这很合理)降低到18%以下!

[不幸的是,仅几个小时后,该应用仍会在较新的iPad上崩溃。但是,即使在下载24小时后,在我们的2016 iPad Pro 1st Gen上,该应用程序也完全不会崩溃。

[从设备中获取崩溃日志时,我们所看到的是CPU使用率超过50%的时间超过3分钟。没有其他崩溃日志出现。

这些设备都已插入电源,并且它们的锁定时间设置为从不,以使iPad保持清醒并且我们的应用程序处于前台。

为了解决此问题,我们降低了性能,基本上在每个图像之间等待30秒,在每批图像之间等待2分钟。这行得通,崩溃崩溃了,但是,下载我们所有的图像需要几天的时间。

[我们正在尝试找到性能合理且应用程序不会崩溃的快乐媒体。

但是,困扰我的是,无论设置如何,甚至在全孔性能下,应用程序都不会在旧设备上崩溃,而只会在新设备上崩溃。

传统观点认为这应该是不可能的。

我在这里想念什么?

[当我使用Instruments进行配置文件时,我发现该应用程序在下载时的平均舒适度为13%,并且批次之间的间隔为20秒,因此iPad应该有足够的时间进行任何清理。

有人有什么想法吗?随时要求其他信息,我不确定还有什么用。

编辑1:下面的下载程序代码:

//Assume the following instance variables are set up:

self.operationQueue = NSOperationQueue to download the images.

self.urlSession = NSURLSession with ephemeralSessionConfiguration, 60 second timeoutIntervalForRequest

self.conditions = NSMutableArray to house the NSConditions used below.

self.countRemaining = NSUInteger which keeps track of how many images are left to be downloaded.

//Starts the downloading process by setting up the variables needed for downloading.
-(void)startDownloading
{
    //If the operation queue doesn't exist, re-create it here.
    if(!self.operationQueue)
    {
        self.operationQueue = [[NSOperationQueue alloc] init];
        [self.operationQueue addObserver:self forKeyPath:KEY_PATH options:0 context:nil];
        [self.operationQueue setName:QUEUE_NAME];
        [self.operationQueue setMaxConcurrentOperationCount:2];
    }

    //If the session is nil, re-create it here.
    if(!self.urlSession)
    {
        self.urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]
                                                        delegate:self
                                                   delegateQueue:nil];
    }

    if([self.countRemaining count] == 0)
    {
        [self performSelectorInBackground:@selector(startDownloadForNextBatch:) withObject:nil];

        self.countRemaining = 1;
    }
}

//Starts each batch. Called again on observance of the operation queue's task count being 0.
-(void)startDownloadForNextBatch:
{
    [NSThread sleepForTimeInterval:20.0]; // 20 second gap between batches

    self.countRemaining = //Go get the count remaining from the database.

    if (countRemaining > 0)
    {
        NSArray *imageRecordsToDownload = //Go get the next batch of URLs for the images to download from the database.

        [imageRecordsToDownload enumerateObjectsUsingBlock:^(NSDictionary *imageRecord,
                                                                          NSUInteger index,
                                                                          BOOL *stop)
         {
            NSInvocationOperation *invokeOp = [[NSInvocationOperation alloc] initWithTarget:self
                                                                                   selector:@selector(downloadImageForRecord:)
                                                                                     object:imageRecord];
            [self.operationQueue addOperation:invokeOp];
         }];
    }
}

//Performs one image download.
-(void)downloadImageForRecord:(NSDictionary *)imageRecord
{
    NSCondition downloadCondition = [[NSCondition alloc] init];
    [self.conditions addObject:downloadCondition];

    [[self.urlSession downloadTaskWithURL:imageURL
                        completionHandler:^(NSURL *location,
                                            NSURLResponse *response,
                                            NSError *error)
              {
                  if(error)
                  {
                    //Record error below.
                  }
                  else
                  {
                    //Move the downloaded image to the correct directory.
                    NSError *moveError;
                    [[NSFileManager defaultManager] moveItemAtURL:location toURL:finalURL error:&moveError];

                    //Create a thumbnail version of the image for use in a search grid.
                  }

                  //Record the final outcome for this record by updating the database with either an error code, or the file path to where the image was saved.

                  //Sleep for some time to allow the CPU to rest.
                  [NSThread sleepForTimeInterval:0.05]; // 0.05 second gap between images.

                  //Finally, signal our condition.
                  [downloadCondition signal];
              }]
        resume];

    [downloadCondition lock];
    [downloadCondition wait];
    [downloadCondition unlock];
}

//If the downloads need to be stopped, for whatever reason (i.e. the user logs out), this function is called to stop the process entirely:
-(void)stopDownloading
{
    //Immediately suspend the queue.
    [self.operationQueue setSuspended:YES];

    //If any conditions remain, signal them, then remove them. This was added to avoid deadlock issues with the user logging out and then logging back in in rapid succession.
    [self.conditions enumerateObjectsUsingBlock:^(NSCondition *condition,
                                                  NSUInteger idx,
                                                  BOOL * _Nonnull stop)
     {

         [condition signal];
     }];

    [self setConditions:nil];
    [self setConditions:[NSMutableArray array]];
    [self.urlSession invalidateAndCancel];
    [self setImagesRemaining:0];
    [self.operationQueue cancelAllOperations];
    [self setOperationQueue:nil];
}

编辑2:Instruments的CPU使用率屏幕截图。峰值是〜50%,谷值是〜13%CPU使用率。

enter image description here

编辑3:运行应用程序,直到控制台出现故障,观察到内存问题

好吧!终于在下载图像超过一个小时后,观察到我的iPhone 11 Pro崩溃,这与我的其他测试人员报告的情况相符。

控制台报告我的应用程序因使用过多内存而被杀死。如果我正确阅读此报告,则我的应用程序使用了2 GB以上的RAM。我假设这与NSURLSESSIOND的内部管理有更多关系,因为在使用Xcode或Instruments进行调试时,它没有显示此泄漏。

控制台报告:“内核232912.788 memorystatus:killing_specific_process pid 7075 [PharosSales](每个进程限制为10)2148353KB-memorystatus_available_pages:38718”

]

幸好,我在1小时左右开始收到内存警告。我应该能够暂停(挂起)我的操作队列一段时间(例如30秒),以便让系统清除其内存。

或者,我可以调用stop,在调用再次启动后进行gcd调度。

你们对此解决方案有何看法?有没有更优雅的方式来响应内存警告?

您认为此内存使用量来自何处?

我有一个应用程序已经问世了很多年。为了在离线时100%正常运行,此应用仅需要一次下载数十万张图像(每个对象1个)...

ios xcode cpu kill
1个回答
0
投票

编辑4:尤里卡!发现内部Apple API内存泄漏

© www.soinside.com 2019 - 2024. All rights reserved.