ios - SIGABRT 因 SDWebImageCache 而崩溃

标签 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;
}

最佳答案

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

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

正是报告导致 SDWebImage 死锁的 GitHub 错误报告 [1] .如果我没理解错的话,这个问题已经解决了,最新的补丁提交是在 2 小时前。

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

我的来源:

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

关于ios - SIGABRT 因 SDWebImageCache 而崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20211937/

相关文章:

ios - 如何避免在 iOS/Html/WebView 标题后面滚动显示

ios - 在添加到 subview 之前如何获取 UIView 的宽度和高度?

iphone - 渐变背景 UIScrollView

objective-c - *.h 和 *.m 文件中的两个接口(interface)

objective-c - 检索所有已安装的 macOS 服务的列表?

ios - 使用 NSSet 类跟踪和限制实例

ios - NSOperation 的 qualityOfService 是否可以低于 NSOperationQueue?

objective-c - 如何从 iPad 发送 SMS(文本消息)

ios - UIScrollView 以编程方式滚动

ios - 该整数是否等于文本字段中的数字