Skip to content

Commit d043775

Browse files
rigdernMartin Konicek
authored and
Martin Konicek
committed
iOS: Fix dequeueTasks crash in image loader
Summary: This fixes a crash occurring [on this line](https://github.com/facebook/react-native/blob/f9f32eb426b5385e199ef6e1d2b610dfe20e60e7/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 #11296 Differential Revision: D4277167 Pulled By: javache fbshipit-source-id: 1211b32935bab7f4080dc00b88d85348786e859a
1 parent 6432839 commit d043775

File tree

1 file changed

+13
-2
lines changed

1 file changed

+13
-2
lines changed

Libraries/Image/RCTImageLoader.m

+13-2
Original file line numberDiff line numberDiff line change
@@ -236,10 +236,14 @@ - (void)dequeueTasks
236236
{
237237
dispatch_async(_URLRequestQueue, ^{
238238
// Remove completed tasks
239+
NSMutableArray *tasksToRemove = nil;
239240
for (RCTNetworkTask *task in self->_pendingTasks.reverseObjectEnumerator) {
240241
switch (task.status) {
241242
case RCTNetworkTaskFinished:
242-
[self->_pendingTasks removeObject:task];
243+
if (!tasksToRemove) {
244+
tasksToRemove = [NSMutableArray new];
245+
}
246+
[tasksToRemove addObject:task];
243247
self->_activeTasks--;
244248
break;
245249
case RCTNetworkTaskPending:
@@ -248,13 +252,20 @@ - (void)dequeueTasks
248252
// Check task isn't "stuck"
249253
if (task.requestToken == nil) {
250254
RCTLogWarn(@"Task orphaned for request %@", task.request);
251-
[self->_pendingTasks removeObject:task];
255+
if (!tasksToRemove) {
256+
tasksToRemove = [NSMutableArray new];
257+
}
258+
[tasksToRemove addObject:task];
252259
self->_activeTasks--;
253260
[task cancel];
254261
}
255262
break;
256263
}
257264
}
265+
266+
if (tasksToRemove) {
267+
[self->_pendingTasks removeObjectsInArray:tasksToRemove];
268+
}
258269

259270
// Start queued decode
260271
NSInteger activeDecodes = self->_scheduledDecodes - self->_pendingDecodes.count;

0 commit comments

Comments
 (0)