iOS: Fix dequeueTasks crash in image loader

Summary:
This fixes a crash occurring [on this line](f9f32eb426/Libraries/Image/RCTImageLoader.m (L253)). It was reported in a comment in #10433.

The problem is that `task` is deallocated at this point and is unsafe to use. Removing it from `_pendingTasks` dropped its ref count to 0. [The ARC docs](http://clang.llvm.org/docs/AutomaticReferenceCounting.html#fast-enumeration-iteration-variables) state that, by default, loop variables in fast enumeration loops are not retained. That's why `task`'s ref count is 0.

It's likely we ran into this bug because the code disobeyed the [reverseObjectEnumerator docs](https://developer.apple.com/reference/foundation/nsarray/1415734-reverseobjectenumerator) which state that "you must not modify the array during enumeration". The default retention policy for fast enumeration seems to assume you follow this.

To fix this bug and avoid other potential pitfalls, the code now follows the docs and `_pendingTa
Closes https://github.com/facebook/react-native/pull/11296

Differential Revision: D4277167

Pulled By: javache

fbshipit-source-id: 1211b32935bab7f4080dc00b88d85348786e859a
This commit is contained in:
Adam Comella 2016-12-05 10:01:28 -08:00 committed by Facebook Github Bot
parent 03fd91063b
commit 2aca021504
1 changed files with 13 additions and 2 deletions

View File

@ -236,10 +236,14 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
{
dispatch_async(_URLRequestQueue, ^{
// Remove completed tasks
NSMutableArray *tasksToRemove = nil;
for (RCTNetworkTask *task in self->_pendingTasks.reverseObjectEnumerator) {
switch (task.status) {
case RCTNetworkTaskFinished:
[self->_pendingTasks removeObject:task];
if (!tasksToRemove) {
tasksToRemove = [NSMutableArray new];
}
[tasksToRemove addObject:task];
self->_activeTasks--;
break;
case RCTNetworkTaskPending:
@ -248,13 +252,20 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
// Check task isn't "stuck"
if (task.requestToken == nil) {
RCTLogWarn(@"Task orphaned for request %@", task.request);
[self->_pendingTasks removeObject:task];
if (!tasksToRemove) {
tasksToRemove = [NSMutableArray new];
}
[tasksToRemove addObject:task];
self->_activeTasks--;
[task cancel];
}
break;
}
}
if (tasksToRemove) {
[self->_pendingTasks removeObjectsInArray:tasksToRemove];
}
// Start queued decode
NSInteger activeDecodes = self->_scheduledDecodes - self->_pendingDecodes.count;