iOS静态库中使用Skia时的内存异常问题排查
iOS下Skia静态库集成的内存异常问题
Skia编译命令
bin/gn gen out/ios-apple --args='target_os="ios" target_cpu="arm64" ios_use_simulator=false skia_enable_tools=false is_official_build=false is_debug=true is_trivial_abi=false skia_use_metal=true skia_use_expat=false skia_use_system_expat=false skia_use_system_libpng=false skia_use_system_libwebp=false skia_use_system_zlib=false skia_use_system_freetype2=false skia_use_system_harfbuzz=false skia_use_system_icu=false skia_enable_gpu=true skia_enable_skottie=false skia_compile_modules=true extra_cflags=["-F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks"] extra_ldflags=["-framework", "CoreFoundation", "-framework", "Metal"]'
ViewController中的Skia使用代码
头文件
#import <MetalKit/MetalKit.h> #import <Metal/Metal.h> #import <UIKit/UIKit.h> #define SK_GANESH #define SK_METAL #import <include/gpu/ganesh/GrDirectContext.h> #import <include/core/SkSurface.h> @interface SkiaViewController : UIViewController <MTKViewDelegate> @property (nonatomic, strong) MTKView *mtkView; @property (nonatomic, strong) id<MTLDevice> metalDevice; @property (nonatomic, strong) id<MTLCommandQueue> metalQueue; @property (nonatomic, assign) sk_sp<GrDirectContext> grDirectContext; @property (nonatomic, assign) sk_sp<SkSurface> surface; @end
实现文件
#import <MetalKit/MetalKit.h> #import <Metal/Metal.h> #define SK_GANESH #define SK_METAL #import "include/gpu/ganesh/GrTypes.h" #import "include/gpu/ganesh/SkSurfaceGanesh.h" #import "include/gpu/ganesh/mtl/GrMtlTypes.h" #import <include/gpu/ganesh/GrBackendSurface.h> #import <include/gpu/ganesh/SkSurfaceGanesh.h> #import <include/gpu/ganesh/mtl/GrMtlBackendContext.h> #import <include/gpu/ganesh/mtl/GrMtlBackendSurface.h> #import <include/gpu/ganesh/mtl/GrMtlDirectContext.h> #import <include/gpu/ganesh/mtl/SkSurfaceMetal.h> #import <include/gpu/ganesh/GrDirectContext.h> #import <include/core/SkSurface.h> #import <include/core/SkCanvas.h> #import <include/core/SkColorSpace.h> #import "SkiaViewController.h" @implementation SkiaViewController - (void)viewDidLoad { [super viewDidLoad]; #ifndef SK_GANESH NSLog(@"SK_GANESH is undefined"); #endif #ifndef SK_METAL NSLog(@"SK_METAL is undefined"); #endif [self setMetalDevice:MTLCreateSystemDefaultDevice()]; [self setMetalQueue:[[self metalDevice] newCommandQueue]]; if(![self metalDevice]) { NSLog(@"Metal is not supported on this device"); return; } if(![self metalQueue]) { NSLog(@"Failed to create Metal command queue"); return; } // Initialize MTKView self.mtkView = [[MTKView alloc] initWithFrame:self.view.bounds device:self.metalDevice]; self.mtkView.delegate = self; self.mtkView.enableSetNeedsDisplay = YES; [self.mtkView setDepthStencilPixelFormat:MTLPixelFormatDepth32Float_Stencil8]; [self.mtkView setColorPixelFormat:MTLPixelFormatBGRA8Unorm]; [self.mtkView setSampleCount:1]; NSLog(@"self.metalDevice.maxBufferLength=%lu", self.metalDevice.maxBufferLength); self.mtkView.layer.borderWidth = 2.0; // Set border width self.mtkView.layer.borderColor = [UIColor redColor].CGColor; // Set border color [self.view addSubview:self.mtkView]; // Initialize Skia with Metal GrMtlBackendContext backendContext = {}; backendContext.fDevice.retain((__bridge void*)[self metalDevice]); // also tried __bridge_retained backendContext.fQueue.retain((__bridge void*)[self metalQueue]); GrContextOptions grContextOptions; self.grDirectContext = GrDirectContexts::MakeMetal(backendContext, grContextOptions); if (![self grDirectContext]) { NSLog(@"Failed to create GrDirectContext"); return; } NSLog(@"Created GrDirectContext"); } #pragma mark - MTKViewDelegate - (void)drawInMTKView:(nonnull MTKView *)view { } - (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size { } @end
问题现象
直接在主App项目中使用上述代码时运行正常,但将ViewController移入项目内的静态库,或把Skia初始化逻辑放到独立CMake编译的C++库中时,会出现内存异常:
- 调用
GrDirectContexts::MakeMetal时触发坏内存访问崩溃 - 刚初始化的
backendContext指针被莫名置为nullptr - 即使不传递Objective-C对象给Skia,
sk_sp智能指针内部的指针也会在释放等操作时失效
可能的原因分析
1. 符号冲突或链接剥离问题
静态库和主App如果都链接了Skia,会导致同一符号(比如sk_sp的引用计数方法)存在多份实现。iOS链接器在处理静态库时,可能错误地剥离掉部分必要符号,或者因为符号重复导致代码执行时调用了错误的实现,进而引发内存访问错误。
2. 编译选项不一致
静态库、主App与Skia的编译选项必须完全匹配,否则会导致内存布局或ABI不兼容:
- C++标准版本:确保三者都使用相同的C标准(比如C17)
- Debug/Release模式:Skia编译用了
is_debug=true,静态库和主App也必须是Debug模式 - Trivial ABI设置:Skia设置了
is_trivial_abi=false,静态库和主App的C++编译选项中也要关闭Trivial ABI(对应Xcode中的-fno-trivial-abi) - 架构一致性:确保所有目标都编译为arm64,没有混合架构
3. 符号可见性配置错误
Skia编译时如果没有正确设置符号可见性,会导致静态库中的Skia内部符号被隐藏,主App访问时出现未定义行为。可以在Skia的gn参数中添加visibility="default",确保必要符号对外可见。
4. 内存分配器不一致
虽然iOS默认使用统一的系统内存分配器,但如果静态库或Skia编译时修改了内存分配选项(比如自定义malloc),会导致sk_sp在释放对象时使用错误的分配器,引发崩溃。
5. 智能指针跨边界的生命周期问题
sk_sp是Skia自定义的智能指针,在静态库和主App之间传递时,如果两者的Skia版本不一致,或者编译时的宏定义不同(比如SK_GANESH、SK_METAL是否统一),会导致智能指针的内部逻辑不一致,进而引发引用计数错误。
内容的提问来源于stack exchange,提问作者artem
相关产品推荐
相关产品推荐

