iOS:如果将dispatch_once写在头文件...

开发小王:我用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.mSceneDelegate.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  */


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的越多,拷贝的越多),重则影响程序逻辑错误。

你可能感兴趣的:(iOS:如果将dispatch_once写在头文件...)