为了检测后台线程上刷新UI的行为,我使用了第三方库ODUIThreadGuard。 这个lib基于钩子UIView的setNeedsLayout,setNeedsDisplay,setNeedsDisplayInRect选择器。 当我测试它时,我发现UILabel的setFrame:在后台线程上可以被检测到,但是UIView的不行。谁能告诉我为什么?
代码:
// ViewController.m
[self.view addSubview:self.testLabel];
[self.view addSubview:self.testView];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
self.testLabel.frame = CGRectMake(20, 20, 100, 100);
});
将检测到上述代码,但以下代码不会被检测到。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
self.testView.frame = CGRectMake(20, 20, 100, 100);
});
钩码:
@implementation UIView (ThreadSafeGuard)
+ (void)load {
[DBHookUtil swizzlingInClass:[self class] originalSelector:@selector(setNeedsLayout) swizzledSelector:@selector(db_setNeedsLayout)];
[DBHookUtil swizzlingInClass:[self class] originalSelector:@selector(setNeedsDisplay) swizzledSelector:@selector(db_setNeedsDisplay)];
[DBHookUtil swizzlingInClass:[self class] originalSelector:@selector(setNeedsDisplayInRect:) swizzledSelector:@selector(db_setNeedsDisplayInRect:)];
}
- (void)db_setNeedsLayout {
NSAssert([NSThread isMainThread], @"refresh UI on background thread");
[self db_setNeedsLayout];
}
- (void)db_setNeedsDisplay {
NSAssert([NSThread isMainThread], @"refresh UI on background thread");
[self db_setNeedsDisplay];
}
- (void)db_setNeedsDisplayInRect:(CGRect)rect {
NSAssert([NSThread isMainThread], @"refresh UI on background thread");
[self db_setNeedsDisplayInRect:rect];
}
@end
崩溃日志:
2017-05-15 18:17:40.876 ThreadSafeGuardDemo[50800:19659410] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'refresh UI on background thread'
*** First throw call stack:
(
0 CoreFoundation 0x0000000105181b0b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x0000000104be6141 objc_exception_throw + 48
2 CoreFoundation 0x0000000105185cf2 +[NSException raise:format:arguments:] + 98
3 Foundation 0x00000001047803b6 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 193
4 ThreadSafeGuardDemo 0x0000000104615068 -[UIView(ThreadSafeGuard) db_setNeedsDisplay] + 264
5 UIKit 0x00000001058311f6 -[UILabel setNeedsDisplay] + 55
6 UIKit 0x00000001058279e6 -[UILabel _invalidateAsNeededForNewSize:oldSize:withLinkCheck:] + 336
7 UIKit 0x0000000105827c9f -[UILabel setFrame:] + 76
8 ThreadSafeGuardDemo 0x0000000104614a76 __29-[ViewController viewDidLoad]_block_invoke + 278
9 libdispatch.dylib 0x0000000107f744a6 _dispatch_call_block_and_release + 12
10 libdispatch.dylib 0x0000000107f9d05c _dispatch_client_callout + 8
11 libdispatch.dylib 0x0000000107f7f198 _dispatch_root_queue_drain + 1358
12 libdispatch.dylib 0x0000000107f7ebef _dispatch_worker_thread3 + 123
13 libsystem_pthread.dylib 0x00000001083c3746 _pthread_wqthread + 1299
14 libsystem_pthread.dylib 0x00000001083c3221 start_wqthread + 13
)
libc++abi.dylib: terminating with uncaught exception of type NSException