iOS-[UIImage initWithCGImage:scale:orientation:]在后台线程上崩溃

iOS-[UIImage initWithCGImage:scale:orientation:]在后台线程上崩溃,ios,uiimage,thread-safety,core-graphics,cgimage,Ios,Uiimage,Thread Safety,Core Graphics,Cgimage,我正在使用-[UIImage initWithCGImage:scale:orientation:://code>在后台队列上创建一个UIImage,结果它崩溃了: 撞车1 Crashed: com.apple.root.background-qos 0 libsystem_kernel.dylib 0x183778140 __pthread_kill + 8 1 libsystem_pthread.dylib 0x183840ef8 pthread_kill

我正在使用
-[UIImage initWithCGImage:scale:orientation:://code>在后台队列上创建一个
UIImage
,结果它崩溃了:

撞车1

Crashed: com.apple.root.background-qos
0  libsystem_kernel.dylib         0x183778140 __pthread_kill + 8
1  libsystem_pthread.dylib        0x183840ef8 pthread_kill + 112
2  libsystem_c.dylib              0x1836e9dac abort + 140
3  libsystem_malloc.dylib         0x1837acd34 free_list_checksum_botch + 438
4  libsystem_malloc.dylib         0x1837aced8 free_tiny_botch + 84
5  CoreFoundation                 0x183c08038 __CFBasicHashRehash + 2448
6  CoreFoundation                 0x183c09034 __CFBasicHashAddValue + 100
7  CoreFoundation                 0x183ab49c4 CFDictionarySetValue + 248
8  UIKit                          0x18926fd30 _UITraitCollectionCacheForBuiltinStorage + 152
9  UIKit                          0x189272090 +[UITraitCollection traitCollectionWithDisplayScale:] + 52
10 UIKit                          0x1888d5070 -[UIImage initWithCGImage:scale:orientation:] + 236
11 UIKit                          0x1888d4f74 +[UIImage imageWithCGImage:scale:orientation:] + 72
12 MyApp                          0x10001a168 -[UIImage(Additions) foo_scaledImageData] (Foo.m:1527)
13 MyApp                          0x100017304 -[Foo bar] (Foo.m:731)
14 MyApp                          0x100017094 __95-[Foo bar]_block_invoke_3 (Foo.m:691)
15 libdispatch.dylib              0x183629630 _dispatch_call_block_and_release + 24
16 libdispatch.dylib              0x1836295f0 _dispatch_client_callout + 16
17 libdispatch.dylib              0x183637a88 _dispatch_root_queue_drain + 2140
18 libdispatch.dylib              0x183637224 _dispatch_worker_thread3 + 112
19 libsystem_pthread.dylib        0x18383d470 _pthread_wqthread + 1092
20 libsystem_pthread.dylib        0x18383d020 start_wqthread + 4
我还遇到以下两个线程崩溃:

崩溃2,线程1:

Crashed: com.apple.root.background-qos
0  libsystem_kernel.dylib         0x237d3c5c __pthread_kill + 8
1  libsystem_pthread.dylib        0x23879b47 pthread_kill + 62
2  libsystem_c.dylib              0x237680c5 abort + 108
3  libsystem_malloc.dylib         0x238040e9 free_list_checksum_botch + 362
4  libsystem_malloc.dylib         0x23804105 free_list_checksum_botch + 28
5  libsystem_malloc.dylib         0x237fbbff tiny_malloc_from_free_list + 202
6  libsystem_malloc.dylib         0x237fa987 szone_malloc_should_clear + 218
7  libsystem_malloc.dylib         0x237fa879 malloc_zone_malloc + 88
8  CoreFoundation                 0x23a4e291 _CFRuntimeCreateInstance + 236
9  CoreGraphics                   0x24e792a9 CGTypeCreateInstance + 20
10 CoreGraphics                   0x24db7d23 CGColorTransformCreate + 246
11 ImageIO                        0x2533c64b IIO_ConvertCGColorToColorComponents + 26
12 ImageIO                        0x252f5775 CGImagePixelDataProviderCreate + 276
13 ImageIO                        0x25319af1 CGImagePixelDataProviderCreateConforming + 1684
14 ImageIO                        0x252f418d CGImageDestinationAddImage + 2852
15 UIKit                          0x283fe4f5 _UIImageJPEGRepresentation + 620
16 MyApp                          0xf6785 -[UIImage(Additions) foo_scaledImageData] (Foo.m:1538)
17 MyApp                          0xf3ecd -[Foo bar] (Foo.m:731)
18 MyApp                          0xf3d29 __95-[Foo bar]_block_invoke_3 (Foo.m:691)
19 libdispatch.dylib              0x236d7cbf _dispatch_call_block_and_release + 10
20 libdispatch.dylib              0x236e36a1 _dispatch_root_queue_drain + 1572
21 libdispatch.dylib              0x236e307b _dispatch_worker_thread3 + 94
22 libsystem_pthread.dylib        0x23876e0d _pthread_wqthread + 1024
23 libsystem_pthread.dylib        0x238769fc start_wqthread + 8
崩溃2,线程2:

com.apple.root.background-qos
0  libsystem_malloc.dylib         0x238041d8 free_tiny_botch
1  CoreFoundation                 0x23b6f560 __CFBasicHashRehash + 2968
2  CoreFoundation                 0x23b706d4 __CFBasicHashAddValue + 100
3  CoreFoundation                 0x23a4f933 CFDictionarySetValue + 206
4  UIKit                          0x28a87ffb _UITraitCollectionCacheForBuiltinStorage + 146
5  UIKit                          0x28a8a2a7 +[UITraitCollection traitCollectionWithDisplayScale:] + 50
6  UIKit                          0x2812390f -[UIImage initWithCGImage:scale:orientation:] + 214
7  UIKit                          0x2812382b +[UIImage imageWithCGImage:scale:orientation:] + 62
8  MyApp                          0xf676d -[UIImage(Additions) foo_scaledImageData] (Foo.m:1527)
9  MyApp                          0xf3ecd -[Foo bar] (Foo.m:731)
10 MyApp                          0xf3d29 __95-[Foo bar]_block_invoke_3 (Foo.m:691)
11 libdispatch.dylib              0x236d7cbf _dispatch_call_block_and_release + 10
12 libdispatch.dylib              0x236e36a1 _dispatch_root_queue_drain + 1572
13 libdispatch.dylib              0x236e307b _dispatch_worker_thread3 + 94
14 libsystem_pthread.dylib        0x23876e0d _pthread_wqthread + 1024
15 libsystem_pthread.dylib        0x238769fc start_wqthread + 8
崩溃2,线程2看起来像崩溃1,线程1,这让我认为它们是相关的,但崩溃2,线程2是一个异常值。我有很多其他崩溃看起来像没有线程2堆栈的崩溃2,这让我觉得
uiimagejpegresentation
也可能不是线程安全的。

这看起来类似于

我不能说出消息来源,但正在处理中。(或至少 雷达已经被苹果内部的人存档)

旁注:我已经看过UIKit的反汇编,API调用了它 traitCollection和其他一些东西,实际上是 线程安全;更重要的是,它真的应该只在主要方面被调用 理想的线程。我们可以快速旋转并将锁直接添加到 实施,这至少可以消除以下事实: 非AFN网络消费者仍然会在没有锁和 可能触发竞争条件;但它也非常不漂亮

直接使用CGImageSource需要更多的工作,但可能会更好 解决方案

正如
AFNetworking
问题所提到的,堆栈跟踪显示,这看起来像是
-[UIImage initWithCGImage:scale:orientation:][/code>调用
UITraitCollection
。我在
UITraitCollection.h
或中找不到任何说明
UITraitCollection
是线程安全的内容

因此,即使说
UIImage
s是线程安全的:

因为图像对象是不可变的,所以不能更改其属性 创建后的属性。大多数图像属性是自动设置的 在附带的图像文件或图像数据中使用元数据。这个 图像对象的不变性质也意味着它们可以安全使用 从任何线程

文档必须意味着它们在主线程上创建后可以安全使用,或者如果它们只是以串行方式创建的,则可以安全使用。(就是这样。)对于我的解决方法,我切换到使用
CoreGraphics

CGImageRef image = ...
CGSize scaledSize = ...

CGContextRef context = CGBitmapContextCreate(NULL,
                                             scaledSize.width,
                                             scaledSize.height,
                                             CGImageGetBitsPerComponent(image),
                                             CGImageGetBytesPerRow(image),
                                             CGImageGetColorSpace(image),
                                             CGImageGetAlphaInfo(image));

CGImageRef scaledImage;
if (context)
{
    CGContextDrawImage(context,
                       CGRectMake(0,
                                  0,
                                  scaledSize.width,
                                  scaledSize.height),
                       image);
    scaledImage = CGBitmapContextCreateImage(context);
    CGContextRelease(context);
}
else
{
    scaledImage = NULL;
}
然后,为了避免在后台线程上使用
uiimagejpegresentation
,我使用
ImageIO
导出JPEG数据:

NSData *NSDataFromCGImage(CGImageRef scaledImage) 
{
    NSMutableData *scaledImageMutableData = scaledImage ? [NSMutableData new] : nil;
    CGDataConsumerRef scaledImageDataConsumer = scaledImageMutableData ? CGDataConsumerCreateWithCFData((__bridge CFMutableDataRef) scaledImageMutableData) : NULL;
    // from the iOS 9.3 header:
    // The `options' dictionary is reserved for future use; currently, you
    // should pass NULL for this parameter.
    CGImageDestinationRef scaledImageDestination = scaledImageDataConsumer ? CGImageDestinationCreateWithDataConsumer(scaledImageDataConsumer,
                                                                                                                      kUTTypeJPEG,
                                                                                                                      1,
                                                                                                                      NULL) : NULL;
    BOOL success = (^BOOL ()
                    {
                        if (scaledImageDestination)
                        {
                            NSDictionary *addImageOptions = @{(__bridge NSString *)kCGImageDestinationLossyCompressionQuality : @(0.9),
                                                              };
                            CGImageDestinationAddImage(scaledImageDestination,
                                                       scaledImage,
                                                       (__bridge CFDictionaryRef)addImageOptions);

                            return (BOOL) CGImageDestinationFinalize(scaledImageDestination);
                        }
                        else
                        {
                            return NO;
                        }
                    })();
    if (scaledImageDestination)
    {
        CFRelease(scaledImageDestination);
    }
    CGDataConsumerRelease(scaledImageDataConsumer);

    if (success)
    {
        return scaledImageMutableData;
    }
    else
    {
        return nil;
    }
}
这看起来像一个

我不能说出消息来源,但正在处理中。(或至少 雷达已经被苹果内部的人存档)

旁注:我已经看过UIKit的反汇编,API调用了它 traitCollection和其他一些东西,实际上是 线程安全;更重要的是,它真的应该只在主要方面被调用 理想的线程。我们可以快速旋转并将锁直接添加到 实施,这至少可以消除以下事实: 非AFN网络消费者仍然会在没有锁和 可能触发竞争条件;但它也非常不漂亮

直接使用CGImageSource需要更多的工作,但可能会更好 解决方案

正如
AFNetworking
问题所提到的,堆栈跟踪显示,这看起来像是
-[UIImage initWithCGImage:scale:orientation:][/code>调用
UITraitCollection
。我在
UITraitCollection.h
或中找不到任何说明
UITraitCollection
是线程安全的内容

因此,即使说
UIImage
s是线程安全的:

因为图像对象是不可变的,所以不能更改其属性 创建后的属性。大多数图像属性是自动设置的 在附带的图像文件或图像数据中使用元数据。这个 图像对象的不变性质也意味着它们可以安全使用 从任何线程

文档必须意味着它们在主线程上创建后可以安全使用,或者如果它们只是以串行方式创建的,则可以安全使用。(就是这样。)对于我的解决方法,我切换到使用
CoreGraphics

CGImageRef image = ...
CGSize scaledSize = ...

CGContextRef context = CGBitmapContextCreate(NULL,
                                             scaledSize.width,
                                             scaledSize.height,
                                             CGImageGetBitsPerComponent(image),
                                             CGImageGetBytesPerRow(image),
                                             CGImageGetColorSpace(image),
                                             CGImageGetAlphaInfo(image));

CGImageRef scaledImage;
if (context)
{
    CGContextDrawImage(context,
                       CGRectMake(0,
                                  0,
                                  scaledSize.width,
                                  scaledSize.height),
                       image);
    scaledImage = CGBitmapContextCreateImage(context);
    CGContextRelease(context);
}
else
{
    scaledImage = NULL;
}
然后,为了避免在后台线程上使用
uiimagejpegresentation
,我使用
ImageIO
导出JPEG数据:

NSData *NSDataFromCGImage(CGImageRef scaledImage) 
{
    NSMutableData *scaledImageMutableData = scaledImage ? [NSMutableData new] : nil;
    CGDataConsumerRef scaledImageDataConsumer = scaledImageMutableData ? CGDataConsumerCreateWithCFData((__bridge CFMutableDataRef) scaledImageMutableData) : NULL;
    // from the iOS 9.3 header:
    // The `options' dictionary is reserved for future use; currently, you
    // should pass NULL for this parameter.
    CGImageDestinationRef scaledImageDestination = scaledImageDataConsumer ? CGImageDestinationCreateWithDataConsumer(scaledImageDataConsumer,
                                                                                                                      kUTTypeJPEG,
                                                                                                                      1,
                                                                                                                      NULL) : NULL;
    BOOL success = (^BOOL ()
                    {
                        if (scaledImageDestination)
                        {
                            NSDictionary *addImageOptions = @{(__bridge NSString *)kCGImageDestinationLossyCompressionQuality : @(0.9),
                                                              };
                            CGImageDestinationAddImage(scaledImageDestination,
                                                       scaledImage,
                                                       (__bridge CFDictionaryRef)addImageOptions);

                            return (BOOL) CGImageDestinationFinalize(scaledImageDestination);
                        }
                        else
                        {
                            return NO;
                        }
                    })();
    if (scaledImageDestination)
    {
        CFRelease(scaledImageDestination);
    }
    CGDataConsumerRelease(scaledImageDataConsumer);

    if (success)
    {
        return scaledImageMutableData;
    }
    else
    {
        return nil;
    }
}