前言
为什么要写NSObject呢?嗯嗯,主要因为手贱。本来,近段时间公司比较闲,然后,鄙人想进阶一下iOS开发,runtime之前看过一些简书,blog等等,但是实际上用的比较少,也不是很清晰,所以就去看runtime的相关资料,然后发现很多不懂的知识,就不断点连接,看看跳跳,然后就来到了NSObject对象模型,索性就先把它弄明白。推荐一篇深入浅出的文章《NSObject对象模型解析(上)》。
1.先看一下结构
大家可以先看一下NSObject.h头文件
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
- (Class)class;
上面可以看见,NSObject含有isa成员变量,是一个Class类型,继续点进去,到objc.h。
typedef struct objc_class *Class;
Class是一个结构体指针,现在我们知道了isa是一个objc_class结构体指针。继续点objc_class进去,到runtime.h。
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
激动人心的时刻,终于看到了这个经典的结构体objc_class。现在,我们来分析一下:
isa //指向本类meta-class的结构体指针,meta-class存在的意义在于,调用类方法和调用实例方法是同一套消息传递机制。
super_class //看名字就知道指向本类的结构体指针
name //类名
version //类版本信息
info //类相关的一些信息
instance_size //类所有实例变量的大小
ivars //类的成员变量列表
methodLists //函数列表
cache //缓存使用过的方法列表,提高访问速度
protocols //协议列表
类的结构大概就是这样的东西了,来实际编译一下看源代码
新一个类:ClassA
ClassA.h
@protocol ClassAProtocol <NSObject>
- (void)protocolMethod;
@end
@interface ClassA : NSObject<ClassAProtocol>
@property (nonatomic, strong) NSString *publicStr1;
@property (nonatomic, copy) NSString *publicStr2;
+ (void)classMethod;
- (void)publicMethod;
@end
ClassA.m
@interface ClassA ()
@property (nonatomic, strong) NSString *privateStr1;
@property (nonatomic, copy) NSString *privateStr2;
@end
@implementation ClassA {
NSString *privateStr3;
}
#pragma mark - Class method
+ (void)classMethod {}
#pragma mark - Public
- (void)publicMethod {}
#pragma mark - Private
- (void)privateMethod {}
#pragma mark - ClassAProtocol
- (void)protocolMethod {}
@end
打开终端,进入ClassA.m文件所在目录,执行下面命令
clang -rewrite-objc ClassA.m
打开生成的ClassA.cpp文件,定位到最底端,我们来一一分析。
找到ClassA.h声明头文件
#ifndef _REWRITER_typedef_ClassA
#define _REWRITER_typedef_ClassA
typedef struct objc_object ClassA;
typedef struct {} _objc_exc_ClassA;
#endif
extern "C" unsigned long OBJC_IVAR_$_ClassA$_publicStr1;
extern "C" unsigned long OBJC_IVAR_$_ClassA$_publicStr2;
extern "C" unsigned long OBJC_IVAR_$_ClassA$_privateStr1;
extern "C" unsigned long OBJC_IVAR_$_ClassA$_privateStr2;
struct ClassA_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *privateStr3;
NSString *_publicStr1;
NSString *_publicStr2;
NSString *_privateStr1;
NSString *_privateStr2;
};
上面可以看到我们定义的属性和成员变量,我们再往下看:
static void _C_ClassA_classMethod(Class self, SEL _cmd) {}
static void _I_ClassA_publicMethod(ClassA * self, SEL _cmd) {}
static void _I_ClassA_privateMethod(ClassA * self, SEL _cmd) {}
static void _I_ClassA_protocolMethod(ClassA * self, SEL _cmd) {}
找到类方法,公有实例方法和私有实例方法,继续往下看。
struct _objc_method {
struct objc_selector * _cmd;
const char *method_type;
void *_imp;
};
struct _protocol_t {
void * isa; // NULL
const char *protocol_name;
const struct _protocol_list_t * protocol_list; // super protocols
const struct method_list_t *instance_methods;
const struct method_list_t *class_methods;
const struct method_list_t *optionalInstanceMethods;
const struct method_list_t *optionalClassMethods;
const struct _prop_list_t * properties;
const unsigned int size; // sizeof(struct _protocol_t)
const unsigned int flags; // = 0
const char ** extendedMethodTypes;
};
struct _ivar_t {
unsigned long int *offset; // pointer to ivar offset location
const char *name;
const char *type;
unsigned int alignment;
unsigned int size;
};
struct _class_ro_t {
unsigned int flags;
unsigned int instanceStart;
unsigned int instanceSize;
unsigned int reserved;
const unsigned char *ivarLayout;
const char *name;
const struct _method_list_t *baseMethods;
const struct _objc_protocol_list *baseProtocols;
const struct _ivar_list_t *ivars;
const unsigned char *weakIvarLayout;
const struct _prop_list_t *properties;
};
struct _class_t {
struct _class_t *isa;
struct _class_t *superclass;
void *cache;
void *vtable;
struct _class_ro_t *ro;
};
struct _category_t {
const char *name;
struct _class_t *cls;
const struct _method_list_t *instance_methods;
const struct _method_list_t *class_methods;
const struct _protocol_list_t *protocols;
const struct _prop_list_t *properties;
};
上面可以看到method,protocol,ivar以及class结构体定义,重点放在_class_ro_t这个结构体,是不是和runtime里面定义的objc_class结构体有点像。再看一下_class_t这个结构体,是不是除了包含了isa指针和superclass指针还包含_class_ro_t这个结构体,答案呼之欲出了,_class_t结构体就是objc_class编译成c的结构。下面继续看一下成员变量和属性变成什么样子。
static struct /*_ivar_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count;
struct _ivar_t ivar_list[5];
} _OBJC_$_INSTANCE_VARIABLES_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_ivar_t),
5,
{{(unsigned long int *)&OBJC_IVAR_$_ClassA$privateStr3, "privateStr3", "@\"NSString\"", 3, 8},
{(unsigned long int *)&OBJC_IVAR_$_ClassA$_publicStr1, "_publicStr1", "@\"NSString\"", 3, 8},
{(unsigned long int *)&OBJC_IVAR_$_ClassA$_publicStr2, "_publicStr2", "@\"NSString\"", 3, 8},
{(unsigned long int *)&OBJC_IVAR_$_ClassA$_privateStr1, "_privateStr1", "@\"NSString\"", 3, 8},
{(unsigned long int *)&OBJC_IVAR_$_ClassA$_privateStr2, "_privateStr2", "@\"NSString\"", 3, 8}}
};
如上所示,成员变量都变成了类似OBJC_IVAR_$_ClassA$privateStr3这样的结构,很容易就能看出来。看一下方法列表:
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[11];
} _OBJC_$_INSTANCE_METHODS_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
11,
{{(struct objc_selector *)"publicMethod", "v16@0:8", (void *)_I_ClassA_publicMethod},
{(struct objc_selector *)"privateMethod", "v16@0:8", (void *)_I_ClassA_privateMethod},
{(struct objc_selector *)"protocolMethod", "v16@0:8", (void *)_I_ClassA_protocolMethod},
{(struct objc_selector *)"publicStr1", "@16@0:8", (void *)_I_ClassA_publicStr1},
{(struct objc_selector *)"setPublicStr1:", "v24@0:8@16", (void *)_I_ClassA_setPublicStr1_},
{(struct objc_selector *)"publicStr2", "@16@0:8", (void *)_I_ClassA_publicStr2},
{(struct objc_selector *)"setPublicStr2:", "v24@0:8@16", (void *)_I_ClassA_setPublicStr2_},
{(struct objc_selector *)"privateStr1", "@16@0:8", (void *)_I_ClassA_privateStr1},
{(struct objc_selector *)"setPrivateStr1:", "v24@0:8@16", (void *)_I_ClassA_setPrivateStr1_},
{(struct objc_selector *)"privateStr2", "@16@0:8", (void *)_I_ClassA_privateStr2},
{(struct objc_selector *)"setPrivateStr2:", "v24@0:8@16", (void *)_I_ClassA_setPrivateStr2_}}
};
实例方法都变成了objc_selector的结构体指针,还包含系统默认自定义的get和set的方法,但是仔细一看,貌似方法列表少了类方法。为什么呢?大家看一下这个结构体的命名_OBJC$_INSTANCE_METHODS_ClassA,是不是想到了什么?是的,这个只是实例方法,类方法继续往下看:
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CLASS_METHODS_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"classMethod", "v16@0:8", (void *)_C_ClassA_classMethod}}
};
_OBJC$_CLASS_METHODS_ClassA,这个命名够明显了吧,类方法列表。等等,我们是不是忘记了什么?有人会说,协议呢?协议方法呢?别急,下面给你呈现出来。
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_PROTOCOL_INSTANCE_METHODS_ClassAProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"protocolMethod", "v16@0:8", 0}}
};
struct _protocol_t _OBJC_PROTOCOL_ClassAProtocol __attribute__ ((used)) = {
0,
"ClassAProtocol",
(const struct _protocol_list_t *)&_OBJC_PROTOCOL_REFS_ClassAProtocol,
(const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_ClassAProtocol,
0,
0,
0,
0,
sizeof(_protocol_t),
0,
(const char **)&_OBJC_PROTOCOL_METHOD_TYPES_ClassAProtocol
};
是不是看见自己定义的协议和协议方法了。嘿嘿嘿,客官可真是慧眼呀😄。再往下看。
static struct /*_prop_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[2];
} _OBJC_$_PROP_LIST_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
2,
{{"publicStr1","T@\"NSString\",&,N,V_publicStr1"},
{"publicStr2","T@\"NSString\",C,N,V_publicStr2"}}
};
显而易见,上面就是属性列表。我们之前ClassA定义的属性,成员变量,方法,协议都能找到相应的实现。
2.解析一下类(Class)与元类(meta-class)的关系
先上一张大家很熟悉的图:
先说一下,一个类的isa指针,meta-class以及superclass怎么获取,代码呈上:
Class object_getClass(id obj) //获取isa指针
objc_getMetaClass(const char *name) //获取meta-class
objc_getClass(const char *name) //获取本类
class_getSuperclass(Class cls) //获取父类
下面解析一下这张图怎么得到的:
新建两个类:ClassA继承NSObject,ClassB继承ClassA
@interface ClassA : NSObject
@end
@interface ClassB : ClassA
@end
同样,clang一下,编译成.cpp文件,我们再来逐步分析。
先看ClassA:
static void OBJC_CLASS_SETUP_$_ClassA(void ) {
OBJC_METACLASS_$_ClassA.isa = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_ClassA.superclass = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_ClassA.cache = &_objc_empty_cache;
OBJC_CLASS_$_ClassA.isa = &OBJC_METACLASS_$_ClassA;
OBJC_CLASS_$_ClassA.superclass = &OBJC_CLASS_$_NSObject;
OBJC_CLASS_$_ClassA.cache = &_objc_empty_cache;
}
从上面的代码我们可以看出来:
ClassA.isa->ClassA_Meta_Class
ClassA.superclass->NSObject_Class
ClassA_Meta_Class.isa->NSObject_Meta_Class
ClassA_Meta_Class.superclass->NSObject_Meta_Class
我们再看ClassB:
static void OBJC_CLASS_SETUP_$_ClassB(void ) {
OBJC_METACLASS_$_ClassB.isa = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_ClassB.superclass = &OBJC_METACLASS_$_ClassA;
OBJC_METACLASS_$_ClassB.cache = &_objc_empty_cache;
OBJC_CLASS_$_ClassB.isa = &OBJC_METACLASS_$_ClassB;
OBJC_CLASS_$_ClassB.superclass = &OBJC_CLASS_$_ClassA;
OBJC_CLASS_$_ClassB.cache = &_objc_empty_cache;
}
同样,我们可以得到:
ClassB.isa->ClassB_Meta_Class
ClassB.superclass->ClassA
ClassB_Meta_Class.isa->NSObject_Meta_Class
ClassB_Meta_Class.superclass->ClassA_Meta_Class
结合ClassA和ClassB我们可以得出下图:
是不是觉得和上面的图还是有点不一样,不着急,现在我们通过代码来补全这张图,顺便验证一下刚才画的逻辑图的正确性。GoGoGo!
ClassB *classB = [ClassB new];
Class myClass = [classB class];
while (myClass != nil) {
Class superClass = class_getSuperclass(myClass);
Class metaClass = object_getClass(myClass);
Class superMetaClass = class_getSuperclass(metaClass);
Class isaMetaClass = object_getClass(metaClass);
const char *myClassName = class_getName(myClass);
NSString *className = [NSString stringWithUTF8String: myClassName];
NSLog(@"%@: %p\n%@_superClass: %p\n%@_meta_class: %p\n%@_meta_superclass: %p\n%@_isa_meta_class: %p\n", className, myClass, className, superClass, className, metaClass, className, superMetaClass, className, isaMetaClass);
myClass = superClass;
}
//输出
2017-03-02 22:05:49.520227 ObjCRuntimeDemo[9490:695334] ClassB: 0x100003b38
ClassB_superClass: 0x100003bd8
ClassB_meta_class: 0x100003b10
ClassB_meta_superclass: 0x100003bb0
ClassB_isa_meta_class: 0x7fffaec320f0
2017-03-02 22:05:49.520509 ObjCRuntimeDemo[9490:695334] ClassA: 0x100003bd8
ClassA_superClass: 0x7fffaec32140
ClassA_meta_class: 0x100003bb0
ClassA_meta_superclass: 0x7fffaec320f0
ClassA_isa_meta_class: 0x7fffaec320f0
2017-03-02 22:05:49.520570 ObjCRuntimeDemo[9490:695334] NSObject: 0x7fffaec32140
NSObject_superClass: 0x0
NSObject_meta_class: 0x7fffaec320f0
NSObject_meta_superclass: 0x7fffaec32140
NSObject_isa_meta_class: 0x7fffaec320f0
Program ended with exit code: 0
//每个类需要打印5个地址,分别是本类的地址,本类继承的父类地址,本类元类地址,本类元类父类地址,本类元类isa指针指向的地址
由上述代码以及输出,我们验证了第一张图的正确性,并且进一步完善了第一张图,如下:
看上去是不是和最开始的逻辑图很像,再加上实例对象的isa指针的指向,替换一下子类,父类和根类的概念,完全就一样了。所以说,类和元类之前的关系,就如上图,每个类都有特定的元类,类的isa指针指向元类,元类拥有着类一样的结构,并且每个元类的isa指针都是指向根元类,而元类的出现就是为了让类方法的调用和实例方法保持一致。下面看类和元类编译的c代码:
extern "C" __declspec(dllimport) struct _class_t OBJC_METACLASS_$_NSObject;
extern "C" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_ClassA __attribute__ ((used, section ("__DATA,__objc_data"))) = {
0, // &OBJC_METACLASS_$_NSObject,
0, // &OBJC_METACLASS_$_NSObject,
0, // (void *)&_objc_empty_cache,
0, // unused, was (void *)&_objc_empty_vtable,
&_OBJC_METACLASS_RO_$_ClassA,
};
extern "C" __declspec(dllimport) struct _class_t OBJC_CLASS_$_NSObject;
extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_ClassA __attribute__ ((used, section ("__DATA,__objc_data"))) = {
0, // &OBJC_METACLASS_$_ClassA,
0, // &OBJC_CLASS_$_NSObject,
0, // (void *)&_objc_empty_cache,
0, // unused, was (void *)&_objc_empty_vtable,
&_OBJC_CLASS_RO_$_ClassA,
};
//meta_class和class类型都是_class_t结构体
3.总结
上面说了一大堆,废话也不少,第一次这么卖力写文章,说起来都有点感动😹。其实,主要就是NSObject对象模型的解析,以及类和元类的关系,大家有兴趣的自己去编译一下,肯定会有收获的。