一道另类的GCD面试题

510 阅读2分钟

题目如下:下面代码输出结果是什么?是YES还是NO还是编译出错?

- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_queue_t queue = dispatch_get_main_queue();
    BOOL isEqual = [queue isKindOfClass:[NSObject class]];
    NSLog(@"isEqual=%d", isEqual);
}

在回答这个问题之前,首先考虑2个问题:
1. 变量queue被声明的类型是什么?
2. queue的真实类型是什么?

1.queue被声明的类型是什么?

很明显是 dispatch_queue_t ,但dispatch_queue_t又是什么呢?
点进去看一下会定位在这行代码里,DISPATCH_DECL(dispatch_queue)里;DISPATCH_DECL的定义如下 #define DISPATCH_DECL(name) OS_OBJECT_DECL_SUBCLASS(name, dispatch_object),则OS_OBJECT_DECL_SUBCLASS是什么就点进不去了,不过由于GCD是开源的,通过源码得到的宏定义如下:

dispatch_queue_t => ISPATCH_DECL(dispatch_queue);
#define DISPATCH_DECL(name) OS_OBJECT_DECL_SUBCLASS(name, dispatch_object)

#define OS_OBJECT_DECL_SUBCLASS(name, super) \
		OS_OBJECT_DECL_IMPL(name, NSObject, <OS_OBJECT_CLASS(super)>)

#define OS_OBJECT_DECL_IMPL(name, adhere, ...) \
		OS_OBJECT_DECL_PROTOCOL(name, __VA_ARGS__) \
		typedef adhere<OS_OBJECT_CLASS(name)> \
				* OS_OBJC_INDEPENDENT_CLASS name##_t

#define OS_OBJECT_DECL_PROTOCOL(name, ...) \
		@protocol OS_OBJECT_CLASS(name) __VA_ARGS__ \
		@end

#define OS_OBJECT_CLASS(name) OS_##name

DISPATCH_DECL(dispatch_queue)展开之后可以得到

OS_OBJECT_DECL_SUBCLASS(dispatch_queue, dispatch_object)
    OS_OBJECT_DECL_IMPL(dispatch_queue, NSObject, <OS_OBJECT_CLASS(dispatch_object)>)
            @protocol OS_dispatch_queue <OS_dispatch_object>
            @end
            typedef NSObject<OS_dispatch_queue> * dispatch_queue_t

通过上面的代码,可以看到dispatch_queue_tNSObject<OS_dispatch_queue> *别名,可以得出queue被声明的类型是NSObject,因此上面的代码是可以被编译通过的。

2. queue的真实类型是什么?

debug代码,得到真实类型如下:是OS_dispatch_queue_main,是NSObject的子类,到此已经可以上面的打印为true

截屏2021-05-16 下午2.22.39.png

3. Why,主队列为何是一个对象

这时,又产生了一个新的疑问,主队列是一个结构体,这里为什么会显示是一个类呢?那么看一下主队列结构体里的内容

struct dispatch_queue_static_s _dispatch_main_q = {
    DISPATCH_GLOBAL_OBJECT_HEADER(queue_main), 
#if !DISPATCH_USE_RESOLVERS
    .do_targetq = _dispatch_get_default_queue(true),
#endif
    .dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(1) |
            DISPATCH_QUEUE_ROLE_BASE_ANON,
    .dq_label = "com.apple.main-thread",
    .dq_atomic_flags = DQF_THREAD_BOUND | DQF_WIDTH(1),
    .dq_serialnum = 1,
};

可以看到结构体内部最上面是一个宏定义,完全展开后如下

struct dispatch_queue_static_s _dispatch_main_q = {
    .do_vtable = &OS_dispatch_queue_main_class,
    .do_ref_cnt = Int_Max,
    .do_xref_cnt = Int_Max,
#if !DISPATCH_USE_RESOLVERS
    .do_targetq = _dispatch_get_default_queue(true),
#endif
    .dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(1) |
            DISPATCH_QUEUE_ROLE_BASE_ANON,
    .dq_label = "com.apple.main-thread",
    .dq_atomic_flags = DQF_THREAD_BOUND | DQF_WIDTH(1),
    .dq_serialnum = 1,
};

看到了第一个字段指向了OS_dispatch_queue_main_class,猜测是OS_dispatch_queue_mainclass对象,若一个结构体第一个字段指向了某个类对象,则会认为这个结构体是此类的实例,验证如下:

截屏2021-05-16 下午2.50.16.png

若有错误,欢迎指出,如果对你有帮助,希望能得到你的点赞

参考资料:

深入浅出GCD之dispatch_queue

GCD源码地址(版本1271.100.5)