OC 与 C++ 混编导致 UTF8String 野指针崩溃 #2532
-
1 当第一次调用 PAGImageView 的 - (void)setPathAsync:completionBlock: 时,下面函数会去下载 PAG 文件: + (PAGFile*)Load:(NSString*)path {
if (path == nil) {
return nil;
}
if ([PAGFileImpl IsNetWorkPath:path]) {
NSData* cacheData = [PAGDiskCacheImpl ReadFile:path];
if (cacheData == nil) {
NSError* error = nil;
cacheData = [NSData dataWithContentsOfURL:[NSURL URLWithString:path]
options:NSDataReadingUncached
error:&error]; // 同步网络下载
if (error == nil && cacheData != nil) {
[PAGDiskCacheImpl WritFile:path data:cacheData];
}
}
return [PAGFileImpl Load:cacheData.bytes size:cacheData.length path:path];
}
auto pagFile = pag::PAGFile::Load([path UTF8String]);
if (pagFile == nullptr) {
return nil;
}
return (PAGFile*)[PAGLayerImpl ToPAGLayer:pagFile];
} 2 如果此时同一个 PAGImageView 对象触再次触发 - (void)setPathAsync:completionBlock: 调用,下面函数会将 PAGImageView 持有的文件 path 释放掉: - (void)setPathAsync:(NSString*)path
maxFrameRate:(float)maxFrameRate
completionBlock:(void (^)(PAGFile*))callback {
if (filePath != nil) {
[filePath release]; // 释放之前的 filePath
filePath = nil;
}
filePath = [path retain];
[PAGFile LoadAsync:path
completionBlock:^(PAGFile* pagFile) {
[self setComposition:pagFile maxFrameRate:maxFrameRate];
callback(pagFile);
}];
} 这样造成的结果是,第一步的网络下载成功之后,调用后面的 [PAGDiskCacheImpl WritFile:path data:cacheData]; 方法访问了 path 变量,此时这个 path 变量已经被 release 了,会崩在这个方法的内部: + (BOOL)WritFile:(NSString*)key data:(NSData*)data {
if (key == nil || data == nil) {
return false;
}
std::string cacheKey = [key UTF8String]; // 会崩在 UTF8String 这里
auto cacheDatas = tgfx::Data::MakeWithoutCopy(data.bytes, data.length);
return pag::DiskCache::WriteFile(cacheKey, cacheDatas);
} 原因是下载是在一个 C++ block 里面执行的: + (void)LoadAsync:(NSString*)path completionBlock:(void (^)(PAGFile*))callback {
if (path == nil) {
callback(nil);
return;
}
void (^copyCallback)(PAGFile*) = Block_copy(callback);
tgfx::Task::Run([callBack = copyCallback, path]() { // 在这个 Block 里面执行下载
PAGFile* file = [PAGFileImpl Load:path]; // 下载函数
callBack(file);
Block_release(callBack);
});
} 但是 C++ block 捕获 path 变量并不会增加 OC 对象的引用计数,导致前面第 2 次调用 - (void)setPathAsync:completionBlock: 释放了 path,访问了野指针崩溃 同时,如果直接调用 PAGFile 的 LoadAsync:completionBlock:方法,这个问题也容易出现,原因和上面是一样的。 复现 Demo |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
这个问题比较明确,我们本周加下这里容错处理 |
Beta Was this translation helpful? Give feedback.
-
该问题已修复,可以升级至 4.3.68 或 4.4.15 |
Beta Was this translation helpful? Give feedback.
该问题已修复,可以升级至 4.3.68 或 4.4.15