开发小王:我用dispatch_once创建了一个串行Queue,我的代码都是在这个queue中执行的,为什么线上还会有线程安全的问题?
看了下,在Common.h
头文件中有这么段代码:
#ifndef Common_h
#define Common_h
static dispatch_queue_t myQueue() {
static dispatch_once_t onceToken;
static dispatch_queue_t __myQueue;
dispatch_once(&onceToken, ^{
__myQueue = dispatch_queue_create("com.my.queue", DISPATCH_QUEUE_SERIAL);
});
return __myQueue;
}
#endif /* Common_h */
从上面的代码看,通过dispatch_once
创建了一个串行Queue,那为什么还是会创建多个线程呢,我们demo下:
在本地创建demo工程,创建Common.h
头文件,分别在AppDelegate.m
和 SceneDelegate.m
文件中引入Common.h
,为了测试系统是否真的创建了两个线程,做如下修改:
//AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
dispatch_async(myQueue(), ^{
while (1) {
}
});
return YES;
}
//SceneDelegate.m
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
dispatch_async(myQueue(), ^{
while (1) {
}
});
}
分别在两个文件中起两个任务,用while(1)
保证线程不退出,App运行后,使用Xcode的暂停功能运行中的线程如下:
验证后发现,确实创建了两个线程,那到底是什么原因呢?
我们知道#import
是预编译处理的,那么我们使用Xcode工具自带的预编译工具(Assistant
-Preprocess
)看下这两个文件预处理后的文件,发现都有这样一段代码:
# 1 "/Users/my/Desktop/demo/demo/Common.h" 1
#pragma clang module import Foundation /* clang -E: implicit import for #import <Foundation/Foundation.h> */
static dispatch_queue_t myQueue() {
static dispatch_once_t onceToken;
static dispatch_queue_t __myQueue;
_dispatch_once(&onceToken, ^{
__myQueue = dispatch_queue_create("com.my.queue", ((void*)0));
});
return __myQueue;
}
是的,在AppDelegate.m 和 SceneDelegate.m有两份拷贝,这也就解释了为什么会有多个线程。
在平时的开发过程中,很多同学不注意会将方法实现写到头文件中(除了有明确需要内联方式的情况),这样做轻则增大可执行文件大小(被import的越多,拷贝的越多),重则影响程序逻辑错误。
网友评论