1.[CIContext createCGImage:fromRect:]调用崩溃
SIGSEGV
SEGV_ACCERR
libsystem_platform.dylib
__platform_memmove
1
AGXGLDriver
_gldUpdateDispatch
2
AGXCompilerConnection
CompilerFSCacheGetShaderCacheKeys(CompilerFSCache const*)
3
libdispatch.dylib
__dispatch_client_callout
4
libdispatch.dylib
__dispatch_lane_barrier_sync_invoke_and_complete
5
AGXCompilerConnection
0x00000001a58ed000 + 5124
6
libdispatch.dylib
__dispatch_client_callout
7
libdispatch.dylib
__dispatch_lane_barrier_sync_invoke_and_complete
8
AGXCompilerConnection
0x00000001a58ed000 + 3316
9
AGXGLDriver
_gldUpdateDispatch
10
AGXGLDriver
_gldGetDeviceString
11
AGXGLDriver
_gldUnbindPipelineProgram
12
AGXGLDriver
_gldUpdateDispatch
13
GLEngine
_gleDoDrawDispatchCoreES2
26
CoreImage
-[CIContext createCGImage:fromRect:]
根据上述堆栈可以定位到崩溃到了这里,在调用context createCGImage:
方法时发生了异常
CIContext *context = [CIContext contextWithOptions:nil];
CGImageRef outImage = [context createCGImage:outputImage fromRect:[inputImage extent]];
因为iOS不能在后台使用opengl,所以怀疑是因为这个原因,这里有两种修复方式:
- 方式一:
在调用该方法时加入前后台逻辑判断,后台情况不进行绘制 - 方式二:
在 CIContext 创建的时候,我们可以给它设置为基于 GPU 还是 CPU。
Core Image 的另外一个优势,就是可以根据需求选择 CPU 或者 GPU 来处理。在 CIContext 创建的时候,我们可以给它设置为基于 GPU 还是 CPU。
基于 GPU 的话,处理速度更快。以为利用了 GPU 的硬件并行优势。可以使用 OpenGL ES 或者 Metal 来渲染图像。这种方式 CPU 完全没有负担,应用程序的运行循环不会受到图像渲染的影响。
但是 GPU 受限于硬件纹理尺寸,而且如果你的程序在后台继续处理和保存图片的话,那么需要使用 CPU。因为当应用切换到后台状态时,GPU 处理会被打断。使用 CPU 渲染的 iOS 会采用 GCD 来对图像进行渲染。这保证了 CPU 渲染在大部分情况下更可靠,比 GPU 渲染更容易使用,可以在后台实现渲染过程。
综上,对于复杂图像滤镜,使用 GPU 更好。但如果在处理视频过程中,保存文件或保存照片到照片库中时,为避免程序进入后台对图片保存造成影响,这时应该使用 CPU 进行渲染。
这里修复我采用的是第二种方式,经验证,问题已修复
CIContext *context = nil;
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
if (device == nil) {
context = [CIContext contextWithOptions:@{ kCIContextUseSoftwareRenderer: @(YES), kCIContextCacheIntermediates: @(NO) }];
} else {
context = [CIContext contextWithMTLDevice:device options:@{ kCIContextCacheIntermediates: @(NO) }];
}
CGImageRef outImage = [context createCGImage:outputImage fromRect:[inputImage extent]];
2.NSMallocException Failed to grow buffer
崩溃堆栈信息
1
libobjc.A.dylib
_objc_exception_throw
2
Foundation
__NSInitializePlatform
3
CoreFoundation
___CFReallocationFailed
4
CoreFoundation
___CFSafelyReallocate
5
Foundation
__convertJSONString
6
Foundation
__writeJSONString
7
Foundation
____writeJSONObject_block_invoke
8
CoreFoundation
-[__NSDictionaryM enumerateKeysAndObjectsWithOptions:usingBlock:]
9
Foundation
__writeJSONObject
10
Foundation
____writeJSONArray_block_invoke
11
CoreFoundation
-[__NSArrayM enumerateObjectsWithOptions:usingBlock:]
12
Foundation
__writeJSONArray
13
Foundation
____writeJSONObject_block_invoke
14
CoreFoundation
-[__NSDictionaryM enumerateKeysAndObjectsWithOptions:usingBlock:]
15
Foundation
__writeJSONObject
16
Foundation
-[_NSJSONWriter dataWithRootObject:options:error:]
17
Foundation
+[NSJSONSerialization dataWithJSONObject:options:error:]
"Failed to grow buffer"表示在尝试扩展缓冲区时出现了问题,这种崩溃通常发生在使用动态内存分配函数(如malloc、realloc等)时,当尝试分配的内存大小超过了系统可用的内存大小时,就会抛出这种异常。
查看堆栈信息,最后是在调用[NSJSONSerialization dataWithJSONObject:options:error:]
方法时发生了异常,也就是当object过大时发生的,这里可以采用以下方式进行防御,但是在实际开发中,建议不要创建较大的object,优化上层逻辑,避免出现这种情况
NSData *jsonData = nil;
@try {
jsonData = [NSJSONSerialization dataWithJSONObject:object options:options error:nil];
} @catch (NSException *exception) {
EduAssert(false, @"NSString+NSJson jsonStringForNSJsonData [NSJSONSerialization dataWithJSONObject:] is failed! exception occurred: %@",
exception);
LogE(gLogTag, @"NSString+NSJson jsonStringForNSJsonData [NSJSONSerialization dataWithJSONObject:] is failed! exception occurred: %@",
exception);
return nil;
}
3. SIGABRT libsystem_kernel.dylib ___pthread_kill + 8
崩溃堆栈
0
libsystem_kernel.dylib
___pthread_kill + 8
1
libsystem_pthread.dylib
_pthread_kill + 268
2
libsystem_c.dylib
_abort + 180
3
libsystem_malloc.dylib
_malloc_vreport + 908
4
libsystem_malloc.dylib
_malloc_zone_error + 100
5
libsystem_malloc.dylib
_nanov2_guard_corruption_detected + 44
6
libsystem_malloc.dylib
_nanov2_free_definite_size
7
libsystem_c.dylib
_strdup + 40
8
ImageIO
_yylex + 2004
9
ImageIO
_yyparse + 244
10
ImageIO
_parse_metadata_path + 88
11
ImageIO
_parse_metadata_pathString + 56
12
ImageIO
_CGImageMetadataGetTagWithPath + 396
13
ImageIO
___CGImageMetadataCreateFromLegacyProps_block_invoke + 1920
14
ImageIO
_XMPMappingIteratePropertiesUsingBlock + 476
15
ImageIO
_CGImageMetadataCreateFromLegacyProps + 148
16
ImageIO
CopyTiffPropertiesToRoot(IIODictionary, CGImageMetadata) + 400
17
ImageIO
IIOImageSource::makeImagePlus(unsigned long, IIODictionary) + 820
18
ImageIO
IIOImageSource::createImageAtIndex(unsigned long, IIODictionary) + 80
19
ImageIO
_CGImageSourceCreateImageAtIndex + 276
这里根据堆栈信息,是在调用CGImageRef frame = CGImageSourceCreateImageAtIndex([self imageCache], i, NULL);
方法时发生了异常,当[self imageCache]被提前释放的时候,调用就会出现野指针崩溃的问题,所以此处需要注意的是,当[self imageCache]被很多地方调用的时候,就有可能出现野指针的情况,可以通过加锁,并在CFRelease的时候加上安全判断来修复
for (size_t i = 0; i < frameCount; ++i) {
// get each frame
CGImageRef frame = CGImageSourceCreateImageAtIndex([self imageCache], i, NULL);
[frames addObject:(id)frame];
CGImageRelease(frame);
修复代码如下:
+ (CGImageSourceRef)imageCache {
[gLock lock];
CGImageSourceRef ref = imageCache;
[gLock unlock];
return ref;
}
+ (void)setImageCache:(CGImageSourceRef)ref {
[gLock lock];
imageCache = ref;
[gLock unlock];
}
+ (void)safeRelease {
if (imageCache == NULL) {
return;
}
CFRelease(imageCache);
imageCache = NULL;
}
4. Application tried to present modally a view controller <EduLivePlayerController: 0x1321c4600> that is already being presented by <EduTabBarController: 0x1328c3200>.
这个crash 表示应用程序试图以模态方式呈现一个已经在显示中的视图控制器,即要弹出控制器B,而控制器B已经被弹出或者正在被弹出的时候又调用了一次present(B)
修复方案:
在present B之前添加判断:
当前的presentedViewController != B && B.presentingViewController == nil
5.野指针崩溃 SIGSEGV SEGV_ACCERR
0 Thread
SIGSEGV
SEGV_ACCERR
0
libobjc.A.dylib
_objc_release_x0
19
CoreFoundation
-[__NSArrayM dealloc]
20
xxxxx
-EduGifView clear
21
xxxxx
-EduFloatView dealloc
这里通过堆栈信息可以看到,在调用dealloc方法释放数组时,数组中的元素在其他地方同时在使用,导致的野指针crash,因此这里可以考虑将数组加锁防止释放同时在使用
6.CALayerInvalidGeometry
当CALayer的位置或大小属性包含了无效的值,例如NaN(Not a Number)等
崩溃堆栈信息:
0 Thread
CALayerInvalidGeometry
CALayer position contains NaN: [nan nan]. Layer: <CAEAGLLayer:0x281b576f0; position = CGPoint (282.375 408.625); bounds = CGRect (0 0; 512 288); delegate = <IJKSDLGLView: 0x2c2683430; frame = (26.375 264.625; 512 288); autoresize = LM+W+RM+TM+H+BM; tintColor = UIExtendedSRGBColorSpace 0.137255 0.721569 1 1; layer = <CAEAGLLayer: 0x281b576f0>>; contents = <CAImageQueue 0x10aa30e70>; allowsDisplayCompositing = YES; allowsGroupOpacity = YES; contentsScale = 2; backgroundColor = <CGColor 0x283480500> [<CGColorSpace 0x2833e6ac0> (kCGColorSpaceICCBased; kCGColorSpaceModelMonochrome; 普通灰度系数2.2描述文件; extended range)] ( 0 1 ); drawableProperties = { EAGLDrawablePropertyColorFormat = EAGLColorFormat8888; EAGLDrawablePropertyRetained = 0; }>
0
CoreFoundation
___exceptionPreprocess
6
UIKitCore
-[UIView(Geometry) setFrame:]
根据上述堆栈信息,可以看到在给view设置frame的时候出现了nan这种无效值,所以引发的崩溃,可以查看frame的计算赋值,是否有nan的情况,要特别注意是否有除数为0的可能性
7.数组越界
NSRangeException
*** -[__NSArrayM objectAtIndex:]: index 21 beyond bounds [0 .. 20]
这个比较常见,通过判断数组长度和下标即可