You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS 16设备点击“Use photo”按钮App崩溃,iOS 17正常的排查求助

iOS 16设备拍照后进入编辑界面崩溃排查与解决

问题现象

App支持设备相机拍照,拍摄完成点击相机视图的“Use photo”按钮后,iOS 16设备上App立即崩溃,iOS 17设备运行正常。崩溃触发在viewWillAppear方法中,若将部分代码移至viewDidLoad,首次拍照流程可正常完成,但再次返回拍照界面时仍会崩溃。

相关代码

PhotoSelectViewController 核心代码

#define IMAGE_FRAME_SIZE 5
#define FRAME_COLOR_R 1.0
#define FRAME_COLOR_G 1.0
#define FRAME_COLOR_B 1.0
#define FRAME_COLOR_ALPHA 1

#define VIEW_AREA_WIDTH         ((UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) ? 768 : 320)
#define VIEW_AREA_HEIGHT        ((UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) ? 800 : 368)
#define IMAGEVIEW_MARGIN        ((UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) ? 40 : 20)
#define SHADOW_WIDTH            ((UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) ? 16 : 8)


@interface PhotoSelectViewController ()

@end

@implementation PhotoSelectViewController

@synthesize popoverController;
@synthesize cameraPicker;
@synthesize backgroundImageView;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // 自定义初始化
    }
    return self;
}
- (void)viewDidLoad
{
    [super viewDidLoad];
      
    if ([UIScreen mainScreen].bounds.size.height == 568)
        self.view.backgroundColor = [UIColor colorWithPatternImage: [UIImage imageNamed: @"background-568h@2x.png"]];
    else
        self.view.backgroundColor = [UIColor colorWithPatternImage: [UIImage imageNamed: @"background.png"]];

}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // 释放主视图中保留的子视图
}

- (void)viewWillAppear:(BOOL)animated
{
    [[UIApplication sharedApplication] setStatusBarHidden:YES];
    
    [self.view setBounds:self.view.frame];
    
    backgroundImageView.image = [[[GlobalData compressImage:[GlobalData sharedData].backgroundImage withSize:CGSizeMake((VIEW_AREA_WIDTH - IMAGEVIEW_MARGIN * 2), (VIEW_AREA_HEIGHT - IMAGEVIEW_MARGIN * 2))] imageAddFrame:IMAGE_FRAME_SIZE withColor:[UIColor colorWithRed:FRAME_COLOR_R green:FRAME_COLOR_G blue:FRAME_COLOR_B alpha:FRAME_COLOR_ALPHA].CGColor] imageWithShadow:SHADOW_WIDTH];

    [backgroundImageView setFrame:CGRectMake(([UIScreen mainScreen].bounds.size.width - backgroundImageView.image.size.width) / 2, ([UIScreen mainScreen].bounds.size.height - backgroundImageView.image.size.height) / 2, backgroundImageView.image.size.width, backgroundImageView.image.size.height)];
    
}

UIImage自定义分类方法

- (UIImage*)imageWithShadow:(int)shadowSize {
    CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef shadowContext = CGBitmapContextCreate(NULL, self.size.width + shadowSize, self.size.height + shadowSize, CGImageGetBitsPerComponent(self.CGImage), 0,
                                                       colourSpace, kCGImageAlphaPremultipliedLast);
    CGColorSpaceRelease(colourSpace);
    
    CGContextSetShadowWithColor(shadowContext, CGSizeMake(shadowSize / 2, -shadowSize / 2), 6, [UIColor blackColor].CGColor);
    CGContextDrawImage(shadowContext, CGRectMake(0, 10, self.size.width, self.size.height), self.CGImage);
    
    CGImageRef shadowedCGImage = CGBitmapContextCreateImage(shadowContext);
    CGContextRelease(shadowContext);
    
    UIImage * shadowedImage = [UIImage imageWithCGImage:shadowedCGImage];
    CGImageRelease(shadowedCGImage);
    
    return shadowedImage;
}

崩溃堆栈信息

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x8)
    frame #0: 0x00000001ad11f4dc libobjc.A.dylib`objc_retain_x2 + 8
    frame #1: 0x00000001e2f2088c TelephonyUI`-[UIImage(TelephonyUI) imageWithShadow:] + 40
  * frame #2: 0x0000000104dea7cc Cut me in`-[PhotoSelectViewController viewWillAppear:](self=0x000000010637ed20, _cmd="viewWillAppear:", animated=YES) at PhotoSelectViewController.m:76:33
    frame #3: 0x00000001b5df2880 UIKitCore`-[UIViewController _setViewAppearState:isAnimating:] + 612
    frame #4: 0x00000001b5e8c6a8 UIKitCore`-[UIViewController __viewWillAppear:] + 100
    frame #5: 0x00000001b5df4114 UIKitCore`-[UINavigationController viewWillAppear:] + 280
    frame #6: 0x00000001b5df2880 UIKitCore`-[UIViewController _setViewAppearState:isAnimating:] + 612
    frame #7: 0x00000001b5e8c6a8 UIKitCore`-[UIViewController __viewWillAppear:] + 100
    frame #8: 0x00000001b64e4888 UIKitCore`__56-[UIPresentationController runTransitionForCurrentState]_block_invoke_3 + 740
    frame #9: 0x00000001b5f7391c UIKitCore`-[_UIAfterCACommitBlock run] + 64
    frame #10: 0x00000001b5f73858 UIKitCore`-[_UIAfterCACommitQueue flush] + 184
    frame #11: 0x0000000105c87f08 libdispatch.dylib`_dispatch_call_block_and_release + 24
    frame #12: 0x0000000105c897a0 libdispatch.dylib`_dispatch_client_callout + 16
    frame #13: 0x0000000105c9834c libdispatch.dylib`_dispatch_main_queue_drain + 920
    frame #14: 0x0000000105c97fa4 libdispatch.dylib`_dispatch_main_queue_callback_4CF + 40
    frame #15: 0x00000001b3edd9ac CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
    frame #16: 0x00000001b3ec1648 CoreFoundation`__CFRunLoopRun + 2080
    frame #17: 0x00000001b3ec5d20 CoreFoundation`CFRunLoopRunSpecific + 584
    frame #18: 0x00000001eb23c998 GraphicsServices`GSEventRunModal + 160
    frame #19: 0x00000001b615780c UIKitCore`-[UIApplication _run] + 868
    frame #20: 0x00000001b6157484 UIKitCore`UIApplicationMain + 312
    frame #21: 0x0000000104de4054 Cut me in`main(argc=1, argv=0x000000016b023670) at main.m:16:16
    frame #22: 0x00000001d167c344 dyld`start + 1860

问题原因

从崩溃堆栈可以看到,代码实际调用的是TelephonyUI框架中UIImage分类的imageWithShadow:方法,而非自定义的同名方法。这是因为iOS 16的TelephonyUI新增了同名分类方法,导致命名冲突,系统方法被优先执行,但系统方法的参数要求、内部实现和自定义方法完全不同,最终引发EXC_BAD_ACCESS内存访问错误。

iOS 17可能调整了框架加载顺序或方法优先级,或是该系统方法实现做了兼容,因此未触发崩溃。

解决方案

  1. 重命名自定义分类方法:给自定义的imageWithShadow:方法添加业务前缀,比如改为app_imageWithShadow:,避免和系统API重名,同时同步修改调用处代码:
    backgroundImageView.image = [[[GlobalData compressImage:... imageAddFrame:... app_imageWithShadow:SHADOW_WIDTH];
    
  2. 排查所有自定义分类:全面检查项目中系统类的自定义分类方法,确保没有和不同iOS版本新增API重名的情况,可对照苹果官方文档确认API版本差异。
  3. 优化图像处理逻辑:viewWillAppear会被多次调用,每次重复执行压缩、加框、加阴影操作浪费性能,建议缓存处理后的图片,仅在原始图片变化时重新处理。

内容的提问来源于stack exchange,提问作者Dave Bradford

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 11:44:54