类
和元类
的建立时机前面简单提到类
和元类
的建立时机是在编译器,今天咱们经过一下两种方法来验证一下:c++
类
和元类
的指针首先看下面代码: 数组
在main函数
以前打印断点,bash
经过p/x
打印类
指针,若是能得到指针
, 说明已经在内存中申请了内存空间app
而后x/4gx
打印类
的内存结构,获得类 的isa
,而后isa & 掩码 ISA_MASK
得到元类
的isa
,若是这个过程当中能正常打印出相应的指针,则能简单验证类
和元类
的建立是在编译期建立的,打印结果以下:函数
command + B
生成可执行文件,而后使用 MachoView
打开程序二进制可执行文件查看由此,能够验证类
和元类
是在编译期建立的,在运行项目alloc
以前已经被建立出来了优化
int a = 10; //
int b = 10; //
LGNSLog(@"%d -- %p",a,&a);
LGNSLog(@"%d -- %p",b,&b);
// KC打印: 10 -- 0x7ffeefbff45c
// KC打印: 10 -- 0x7ffeefbff458
复制代码
// 对象 - 指针拷贝
LGPerson *p1 = [LGPerson alloc];
LGPerson *p2 = [LGPerson alloc];
LGNSLog(@"%@ -- %p",p1,&p1);
LGNSLog(@"%@ -- %p",p2,&p2);
// KC打印: <LGPerson: 0x100753be0> -- 0x7ffeefbff450
// KC打印: <LGPerson: 0x10074e740> -- 0x7ffeefbff448
复制代码
// 数组指针
int c[4] = {1,2,3,4};
int *d = c;
NSLog(@"%p - %p - %p",&c,&c[0],&c[1]);
NSLog(@"%p - %p - %p",d,d+1,d+2);
for (int i = 0; i<4; i++) {
// int value = c[i];
int value = *(d+i);
LGNSLog(@"%d",value);
}
NSLog(@"指针 - 内存偏移");
// 0x7ffeefbff470 - 0x7ffeefbff470 - 0x7ffeefbff474
// 0x7ffeefbff470 - 0x7ffeefbff474 - 0x7ffeefbff478
// KC打印: 1
// KC打印: 2
// KC打印: 3
// KC打印: 4
复制代码
首地址
是数组
的第一个元素
的地址,&c[0]
和 &c[1]
,相差一个元素的大小,指针d + 1
,至关于偏移一个所占位数的元素的大小ui
经过clang
查看看下面代码在c++
文件中的编译:this
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
NSLog(@"%@ - %p",person,pClass);
}
return 0;
}
复制代码
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
// id, SEL
LGPerson *person = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc"));
Class pClass = object_getClass(person);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5s_4100t0cd5rn_d7gx0n5wqh8w0000gn_T_main_60f7a3_mi_9,person,pClass);
}
return 0;
}
复制代码
咱们探究的类
的结构,就是Class
,在cpp
文件中不难发现类
结构是:atom
typedef struct objc_class *Class;
复制代码
能够看出,类
是 objc_class
类型的 结构体。spa
struct objc_object {
Class _Nonnull isa __attribute__((deprecated));
};
复制代码
咱们知道万物皆对象,objc_class
继承自objc_object
,那么咱们经过下图方法查看objc_class
的源码:
struct objc_class : objc_object {
// Class ISA; // 8
Class superclass; // 8
cache_t cache; // 16 不是8 // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() {
return bits.data();
}
··· // 方法和函数
}
复制代码
源码中能够看到,有个隐藏的Class isa
(为何有个隐藏的Class isa
?), 隐藏的属性必然是来自于继承
,继承
自objc_object
,看objc_object
源码:
object
源码:
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
复制代码
那么NSObject
的定义是什么样的呢?
其实NSObject
的定义是结构体
的一种仿写:
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
复制代码
问: 为何isa
是Class
类型?
答:万物皆对象,Clss
自己继承自object
,用来接收isa
能够的,早期调用isa
就是为了返回类
, 后期优化了 nonpointer isa
问:objc_class
和NSObject
的关系? objc_object
和NSObject
的关系?
NSObject
是一种objc_class
的类型,NSObject
也是一个类class
,底层也是objc_class
; OC
底层封装的C
,objc_object
是NSObject
底层编译的写法。 objc_object
和objc_class
是底层的实现,对应当前NSObject(Class)
和NSObject
。
一般咱们会在类
中定义属性
、成员变量
和方法
,
@interface LGPerson : NSObject{
NSString *hobby;
}
@property (nonatomic, copy) NSString *nickName;
- (void)sayHello;
+ (void)sayHappy;
@end
复制代码
那么在类
中是如何存储这些定义的属性 成员变量 方法
的呢? 接下来咱们来研究一下:
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
NSLog(@"%@ - %p",person,pClass);
}
return 0;
}
复制代码
经过x/4gx pClass
打印类
结构,经过前面的查看源码得知以下图:
objc_class
中 Class ISA
和Class superclass
分别占8字节
, cache_t cache
占16字节
struct cache_t {
struct bucket_t *_buckets; // 8
mask_t _mask; // 4 uint32_t mask_t
mask_t _occupied; // 4
public: // 下面是函数,函数不占内存
struct bucket_t *buckets();
// 方法
···
};
复制代码
由于objc_class
中cache_t cache
是结构体
,而不是结构体指针占
(结构体指针占8字节
), 因此cache_t cache
占内存8 + 4 + 4 = 16字节
。
猜想:属性 成员变量
存储在class_data_bits_t bits
中,经过指针偏移(偏移原理类比为数组),偏移32字节
获取class_data_bits_t bits
。
探索以下:
对pClass
首地址0x100001278 + 32
获得 0x100001298(16进制)
即bits
,经过bits.data()
获得class_rw_t *data()
,打印以下:
而class_rw_t
的结构以下:
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
#if SUPPORT_INDEXED_ISA
uint32_t index;
#endif
void setFlags(uint32_t set)
{
OSAtomicOr32Barrier(set, &flags);
}
void clearFlags(uint32_t clear)
{
OSAtomicXor32Barrier(clear, &flags);
}
// set and clear must not overlap
void changeFlags(uint32_t set, uint32_t clear)
{
assert((set & clear) == 0);
uint32_t oldf, newf;
do {
oldf = flags;
newf = (oldf | set) & ~clear;
} while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
}
};
复制代码
打印*data()
:
属性
应该存储在
properties
中,打印
properties
,而后并打印其中
list
:
methods
,一系列操做后以下:
由此咱们探究出了属性 方法
的存储位置,那么成员变量
存储在什么地方呢?
经过查看struct class_rw_t
中的const class_ro_t *ro
,
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
复制代码
里面分别有method_list_t * baseMethodList
property_list_t *baseProperties
const ivar_list_t * ivars
,咱们猜想类
的方法
属性
成员变量
分别存储在对应的变量中,打印ro
结果以下:
由此能够看出LGPerson
仅有的一个成员变量 nickName
存储在bit.data()
中的ro
中baseProperties
中,
那么为何bit.data()
中property_array_t properties
也等打印出成员变量
呢?暂时先抛出个问题。
接下来咱们用一样的方法分别打印ivars
baseMethodList
,如图:
baseMethodList
打印:
打印出count = 2
,分别打印ivars
中成员变量
,分别为hobby
_nickName
,再次验证了@property
生成的属性
,在系统底层会自动生成_属性
的成员变量
,而且会自动生成setter
getter
。
问题:从baseMethodList
中并未打印出类方法 sayHappy
,那么类方法
存储在什么地方呢?
猜想: 实例方法
存在 类
中,那么其实 类
也是元类
建立出来的类对象
,类
的类方法
应该存在元类
中。
经过下面代码,分别在类
和元类
中打印对象方法
和类方法
:
void testInstanceMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
NSLog(@"%s",__func__);
}
打印结果
2019-12-29 12:28:17.714554+0800 LGTest[799:13098] 0x100002198-0x0-0x0-0x100002130
2019-12-29 12:28:17.715541+0800 LGTest[799:13098] testInstanceMethod_classToMetaclass
复制代码
由打印结果看出,对象方法
存在于类
中,不存在于元类
中,类方法
存在于元类
中,不存在于类
中。
类
结构的分析,得出:成员变量
存在ivars
中,属性
存储在baseProperties
中,对象方法
存储在类
里面,类方法
存储在元类
里。