在使用 第三方框架 GPUImage 的时候,项目中控制台会输出一下警告:
Main Thread Checker: UI API called on a background thread: -[UIView bounds] PID: 2791, TID: 521508, Thread name: (none), Queue name: com.sunsetlakesoftware.GPUImage.openGLESContextQueue
原因很明确就是在子线程访问 UI 元素了。
首先找到是哪里的代码在子线程访问 UI 元素,最后发现是在 GPUImageView.m文件的230行- (void)recalculateViewGeometry方法中的这一行代码CGSize currentViewSize = self.bounds.size;

既然找到问题了接下来就是处理问题,处理方法也非常简单,在这里我们做一个判断是否为主线程,然后自己写一个方法,最后调用 GPUImageView的原有实现:
- (void)recalculateViewGeometry
{
NSLog(@"Is main thread: %@", [NSThread isMainThread] ? @"YES" : @"NO");
if ([NSThread isMainThread] == NO) {
dispatch_async(dispatch_get_main_queue(), ^{
[self my_recalculateViewGeometryWithSize:self.bounds.size];
});
} else {
[self my_recalculateViewGeometryWithSize:self.bounds.size];
}
}
- (void)my_recalculateViewGeometryWithSize:(CGSize)size
{
runSynchronouslyOnVideoProcessingQueue(^{
CGFloat heightScaling, widthScaling;
CGSize currentViewSize = size;
// CGFloat imageAspectRatio = inputImageSize.width / inputImageSize.height;
// CGFloat viewAspectRatio = currentViewSize.width / currentViewSize.height;
CGRect insetRect = AVMakeRectWithAspectRatioInsideRect(inputImageSize, self.bounds);
switch(_fillMode)
{
case kGPUImageFillModeStretch:
{
widthScaling = 1.0;
heightScaling = 1.0;
}; break;
case kGPUImageFillModePreserveAspectRatio:
{
widthScaling = insetRect.size.width / currentViewSize.width;
heightScaling = insetRect.size.height / currentViewSize.height;
}; break;
case kGPUImageFillModePreserveAspectRatioAndFill:
{
// CGFloat widthHolder = insetRect.size.width / currentViewSize.width;
widthScaling = currentViewSize.height / insetRect.size.height;
heightScaling = currentViewSize.width / insetRect.size.width;
}; break;
}
imageVertices[0] = -widthScaling;
imageVertices[1] = -heightScaling;
imageVertices[2] = widthScaling;
imageVertices[3] = -heightScaling;
imageVertices[4] = -widthScaling;
imageVertices[5] = heightScaling;
imageVertices[6] = widthScaling;
imageVertices[7] = heightScaling;
});
// static const GLfloat imageVertices[] = {
// -1.0f, -1.0f,
// 1.0f, -1.0f,
// -1.0f, 1.0f,
// 1.0f, 1.0f,
// };
}
有的同学可能会担心,如果是主线程,那么后续的操作是不是都在主线程进行了,这和 GPUImage 原来的代码执行流程不一样。这个担心是多余的,我们可以查看runSynchronouslyOnVideoProcessingQueue()这个函数的具体实现,在里面可以发现在这个函数内进行了判断,如果当前队列是 videoProcessingQueue, 那么就直接执行代码块,不是的话就调用 dispatch_sync(videoProcessingQueue, block); 扔到 videoProcessingQueue里面执行这个代码块。下面是简化后的 runSynchronouslyOnVideoProcessingQueue() 函数实现代码。
void runSynchronouslyOnVideoProcessingQueue(void (^block)(void))
{
dispatch_queue_t videoProcessingQueue = [GPUImageContext sharedContextQueue];
if (dispatch_get_current_queue() == videoProcessingQueue) {
block();
} else {
dispatch_sync(videoProcessingQueue, block);
}
}