子线程读取剪切板
主线程读取剪切板时,偶现APP主线程卡死,然后程序被看门狗杀死。后来我们把读取剪切板操作放到了子线程。虽然是UIKit下的接口,但经与苹果技术人员确认,以及上线一年没回收到任何与此相关的crash,证实可行。 具体代码如下。另外,如果有多个业务方需要读取剪切板,建议放到同一队列管理。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() {
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
NSString *string = [pasteboard.string copy];
NSLog(@"pasteboard:%@",string);});
});
}
iOS 14下读剪切板
iOS14 系统下,只要使用了pasteboard.string
读取剪切板,APP顶部都会出现读取剪切板的提示。 为了保护用户隐私(避免舆论风险),可以使用iOS14 的API来判断剪切板中的内容格式。 使用这两个 API 不会触发系统剪切板提示,但是也拿不到剪切板的具体内容。
- (void)detectPatternsForPatterns:(NSSet<UIPasteboardDetectionPattern> *)patterns
completionHandler:(void(^)(NSSet<UIPasteboardDetectionPattern> * _Nullable,
NSError * _Nullable))completionHandler NS_REFINED_FOR_SWIFT API_AVAILABLE(ios(14.0));
- (void)detectPatternsForPatterns:(NSSet<UIPasteboardDetectionPattern> *)patterns
inItemSet:(NSIndexSet * _Nullable)itemSet
completionHandler:(void(^)(NSArray<NSSet<UIPasteboardDetectionPattern> *> * _Nullable,
NSError * _Nullable))completionHandler NS_REFINED_FOR_SWIFT API_AVAILABLE(ios(14.0));
以下是子线程调用新API的代码。注意,不能在detectPatternsForPatterns
的回调里直接访问剪切板,必须在下一个runloop读取,因为苹果在检测剪切板内容时,给剪切板加锁了,在回调中直接访问剪切板会造成死锁。表现为只有第一次调detectPatternsForPatterns
时能收到回调, 然后子线程死锁,再也收不到detectPatternsForPatterns
回调。
//获取全局队列
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(){
//iOS14及以上,先检测剪切板内容,符合规则再读取剪切板
if (@available(iOS 14.0, *)) {
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
NSSet *pasteDetectionPatternSet = [NSSet setWithObjects:UIPasteboardDetectionPatternNumber,
UIPasteboardDetectionPatternProbableWebURL,
UIPasteboardDetectionPatternProbableWebSearch,
nil];
[pasteboard detectPatternsForPatterns: pasteDetectionPatternSet
completionHandler:^(NSSet<UIPasteboardDetectionPattern> *foundPattenSet, NSError *e) {
BOOL isNumber = NO, isUrl = NO, isWebSearch = NO;
for (UIPasteboardDetectionPattern pattern in foundPattenSet) {
if ([pattern isEqualToString: UIPasteboardDetectionPatternNumber]) {
isNumber = YES;
}
if ([pattern isEqualToString: UIPasteboardDetectionPatternProbableWebURL]) {
isUrl = YES;
}
if ([pattern isEqualToString: UIPasteboardDetectionPatternProbableWebSearch]) {
isWebSearch = YES;
}
}
if (isNumber && isUrl) {
//符合规则,在下一个runloop读取剪切板
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() {
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
NSString *string = [pasteboard.string copy];
NSLog(@"pasteboard:%@",string);
});
}
}];
} else {
//iOS14以下,直接读剪切板
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
NSString *string = [pasteboard.string copy];
NSLog(@"pasteboard:%@",string);
}
}