由于SDWebImageCache,SIGABRT崩溃

时间:2013-11-26 08:33:18

标签: ios objective-c

我收到此崩溃日志(从用户处获取,无法重现):

Threads
_________________________________
Thread: Unknown Name (Crashed)
0     libsystem_kernel.dylib                0x382dd1fc __pthread_kill + 8
1     libsystem_c.dylib                     0x3828dffd abort + 77
2     libc++abi.dylib                       0x375bccd7 abort_message + 75
3     libc++abi.dylib                       0x375d320d _ZSt11__terminatePFvvE + 149
4     libc++abi.dylib                       0x375d2d2d __cxa_increment_exception_refcount + 1
5     libobjc.A.dylib                       0x37d1e7f7 objc_exception_rethrow + 43
6     CoreFoundation                        0x2d811c9d CFRunLoopRunSpecific + 641
7     CoreFoundation                        0x2d811a0b CFRunLoopRunInMode + 107
8     GraphicsServices                      0x32538283 GSEventRunModal + 139
9     UIKit                                 0x300b5049 UIApplicationMain + 1137
10   Paok FC                                0x000c6723 main (main.m:12)

Thread: Unknown Name
0     libsystem_kernel.dylib                0x382ca83c kevent64 + 24
1     libdispatch.dylib                     0x3820af9b _dispatch_mgr_thread$VARIANT$mp + 39

Thread: Unknown Name
0     libsystem_kernel.dylib                0x382ddc7c __workq_kernreturn + 8

Thread: Unknown Name
0     libsystem_kernel.dylib                0x382caadc semaphore_wait_trap + 8
1     libdispatch.dylib                     0x38207a17 _dispatch_barrier_sync_f_slow + 139
2     Paok FC                               0x0010c369 __42-[SDImageCache queryDiskCacheForKey:done:]_block_invoke (SDImageCache.m:308)
3     libdispatch.dylib                     0x38201d1b _dispatch_call_block_and_release + 11
4     libdispatch.dylib                     0x38208273 _dispatch_queue_drain$VARIANT$mp + 375
5     libdispatch.dylib                     0x3820806b _dispatch_queue_invoke$VARIANT$mp + 43
6     libdispatch.dylib                     0x38208ce1 _dispatch_root_queue_drain + 77
7     libdispatch.dylib                     0x38208f59 _dispatch_worker_thread2 + 57
8     libsystem_pthread.dylib               0x38343dbf _pthread_wqthread + 299

Thread: Unknown Name
0     libsystem_kernel.dylib                0x382caa8c mach_msg_trap + 20
1     CoreFoundation                        0x2d8a87c3 __CFRunLoopServiceMachPort + 155
2     CoreFoundation                        0x2d8a6ee9 __CFRunLoopRun + 785
3     CoreFoundation                        0x2d811c27 CFRunLoopRunSpecific + 523
4     CoreFoundation                        0x2d811a0b CFRunLoopRunInMode + 107
5     Foundation                            0x2e24c2f7 +[NSURLConnection(Loader) _resourceLoadLoop:] + 319
6     Foundation                            0x2e2c1c87 __NSThread__main__ + 1063
7     libsystem_pthread.dylib               0x38345c1d _pthread_body + 141
8     libsystem_pthread.dylib               0x38345b8f _pthread_start + 103

Thread: Unknown Name
0     libsystem_kernel.dylib                0x382dd440 __select + 20
1     libsystem_pthread.dylib               0x38345c1d _pthread_body + 141
2     libsystem_pthread.dylib               0x38345b8f _pthread_start + 103

Thread: Unknown Name
0     libsystem_kernel.dylib                0x382ddc7c __workq_kernreturn + 8

Thread: Unknown Name
0     libsystem_kernel.dylib                0x382ddc7c __workq_kernreturn + 8



Crashed Registers
_________________________________
r12 0x148
r10 0x190
r11 0x1559ca80
cpsr    0x10
r4  0x6
r5  0x3a15318c
r6  0x0
r7  0x27d45a60
r0  0x0
r1  0x0
r2  0x0
r3  0x2060
sp  0x27d45a54
r8  0x1558f8e0
r9  0x1
pc  0x382dd1fc
lr  0x38346a33

SDWebImageCache.m中崩溃的方法是:

- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(void (^)(UIImage *image, SDImageCacheType cacheType))doneBlock
{
    NSOperation *operation = NSOperation.new;

    if (!doneBlock) return nil;

    if (!key)
    {
        doneBlock(nil, SDImageCacheTypeNone);
        return nil;
    }

    // First check the in-memory cache...
    UIImage *image = [self imageFromMemoryCacheForKey:key];
    if (image)
    {
        doneBlock(image, SDImageCacheTypeMemory);
        return nil;
    }

    dispatch_async(self.ioQueue, ^
    {
        if (operation.isCancelled)
        {
            return;
        }

        @autoreleasepool
        {
            UIImage *diskImage = [self diskImageForKey:key];
            if (diskImage)
            {
                CGFloat cost = diskImage.size.height * diskImage.size.width * diskImage.scale;
                [self.memCache setObject:diskImage forKey:key cost:cost];
            }

            dispatch_main_sync_safe(^
            {
                doneBlock(diskImage, SDImageCacheTypeDisk);
            });
        }
    });

    return operation;
}

有没有人遇到这次崩溃?如果是的话,有没有办法解决这个问题?

发生崩溃的第308行是:

  dispatch_main_sync_safe(^
            {
                doneBlock(diskImage, SDImageCacheTypeDisk);
            });

此方法在此方法的另一个类中调用:

- (id<SDWebImageOperation>)downloadWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedWithFinishedBlock)completedBlock
{    
    // Invoking this method without a completedBlock is pointless
    NSParameterAssert(completedBlock);

    // Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won't
    // throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString.
    if ([url isKindOfClass:NSString.class])
    {
        url = [NSURL URLWithString:(NSString *)url];
    }

    // Prevents app crashing on argument type error like sending NSNull instead of NSURL
    if (![url isKindOfClass:NSURL.class])
    {
        url = nil;
    }

    __block SDWebImageCombinedOperation *operation = SDWebImageCombinedOperation.new;
    __weak SDWebImageCombinedOperation *weakOperation = operation;

    BOOL isFailedUrl = NO;
    @synchronized(self.failedURLs)
    {
        isFailedUrl = [self.failedURLs containsObject:url];
    }

    if (!url || (!(options & SDWebImageRetryFailed) && isFailedUrl))
    {
        dispatch_main_sync_safe(^
        {
                NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
                completedBlock(nil, error, SDImageCacheTypeNone, YES);
        });
        return operation;
    }

    @synchronized(self.runningOperations)
    {
        [self.runningOperations addObject:operation];
    }
    NSString *key = [self cacheKeyForURL:url];

    operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType)
    {
        if (operation.isCancelled)
        {
            @synchronized(self.runningOperations)
            {
                [self.runningOperations removeObject:operation];
            }

            return;
        }

        if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]))
        {
            if (image && options & SDWebImageRefreshCached)
            {
                dispatch_main_sync_safe(^
                {
                    // If image was found in the cache bug SDWebImageRefreshCached is provided, notify about the cached image
                    // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
                    completedBlock(image, nil, cacheType, YES);
                });
            }

            // download if no image or requested to refresh anyway, and download allowed by delegate
            SDWebImageDownloaderOptions downloaderOptions = 0;
            if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
            if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
            if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;
            if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;
            if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;
            if (image && options & SDWebImageRefreshCached)
            {
                // force progressive off if image already cached but forced refreshing
                downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
                // ignore image read from NSURLCache if image if cached but force refreshing
                downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
            }
            id<SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished)
            {                
                if (weakOperation.isCancelled)
                {
                    dispatch_main_sync_safe(^
                    {
                        completedBlock(nil, nil, SDImageCacheTypeNone, finished);
                    });
                }
                else if (error)
                {
                    dispatch_main_sync_safe(^
                    {
                        completedBlock(nil, error, SDImageCacheTypeNone, finished);
                    });

                    if (error.code != NSURLErrorNotConnectedToInternet)
                    {
                        @synchronized(self.failedURLs)
                        {
                            [self.failedURLs addObject:url];
                        }
                    }
                }
                else
                {
                    BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);

                    if (options & SDWebImageRefreshCached && image && !downloadedImage)
                    {
                        // Image refresh hit the NSURLCache cache, do not call the completion block
                    }
                    // NOTE: We don't call transformDownloadedImage delegate method on animated images as most transformation code would mangle it
                    else if (downloadedImage && !downloadedImage.images && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)])
                    {
                        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^
                        {
                            UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];

                            dispatch_main_sync_safe(^
                            {
                                completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished);
                            });

                            if (transformedImage && finished)
                            {
                                NSData *dataToStore = [transformedImage isEqual:downloadedImage] ? data : nil;
                                [self.imageCache storeImage:transformedImage imageData:dataToStore forKey:key toDisk:cacheOnDisk];
                            }
                        });
                    }
                    else
                    {
                        dispatch_main_sync_safe(^
                        {
                            completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished);
                        });

                        if (downloadedImage && finished)
                        {
                            [self.imageCache storeImage:downloadedImage imageData:data forKey:key toDisk:cacheOnDisk];
                        }
                    }
                }

                if (finished)
                {
                    @synchronized(self.runningOperations)
                    {
                        [self.runningOperations removeObject:operation];
                    }
                }
            }];
            operation.cancelBlock = ^{[subOperation cancel];};
        }
        else if (image)
        {
            dispatch_main_sync_safe(^
            {
                completedBlock(image, nil, cacheType, YES);
            });
            @synchronized(self.runningOperations)
            {
                [self.runningOperations removeObject:operation];
            }
        }
        else
        {
            // Image not in cache and download disallowed by delegate
            dispatch_main_sync_safe(^
            {
                completedBlock(nil, nil, SDImageCacheTypeNone, YES);
            });
            @synchronized(self.runningOperations)
            {
                [self.runningOperations removeObject:operation];
            }
        }
    }];

    return operation;
}

1 个答案:

答案 0 :(得分:1)

正如报告所说,这与信号量有关。我找到了您评论过的问题

enterdispatch_main_sync_safe(^ { doneBlock(diskImage, SDImageCacheTypeDisk); });

正好是报告导致SDWebImage GitHub错误报告[1]死锁的那个。如果我理解正确,这个问题已经修复,最新的补丁提交是2小时前。

也许你可以在将来的某个版本中获得它,或者如果你可以使用git,那就是......

我的来源:

[1] https://github.com/rs/SDWebImage/issues/507