《目录-iOS & OpenGL & OpenGL ES & Metal》
我们之前分析了对象,那么今天来分析一下创建对象的 类~
一、准备工作
探索环境:libObjc - 779.1
Person(自定义类)中声明一个成员变量、一个属性、一个方法、一个类方法
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface Person : NSObject{
NSString *hobby;
}
@property (nonatomic, copy) NSString *nickName;
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation Person
- (void)sayHello{
NSLog(@"LGPerson say : Hello!!!");
}
+ (void)sayHappy{
NSLog(@"LGPerson say : Happy!!!");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [Person alloc];
//拿到person这个对象的类
Class pClass = object_getClass(person);
NSLog(@"%@ - %p",person,pClass);
}
return 0;
}
二、类的本质:类从哪里来
在oc中,所有的类都是可以用Class
来接收的。
1、Class
继承自objc_class
clang
一下,看一下底层编译,Class
是继承自哪里的:
typedef struct objc_class *Class;
2、objc_class
继承自objc_object
继续看一下objc_class
:
struct objc_class {
Class _Nonnull isa __attribute__((deprecated));
} __attribute__((unavailable));
这个方法好像被弃用了,那我们去源码libObjc
中找一下objc_class
,源码里有几个关于它的方法,大部分都被弃用了,我们找到了一个可用的:
struct objc_class : objc_object {
// Class ISA; //这里是一个隐藏属性
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
···下面是这个结构体的一些方法和函数,暂时用不到,省略掉···
}
我们看到,这里面有一个隐藏的isa,那它必然是继承自父类的一个属性,我们继续看一下objc_object
来验证一下:
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
3、Class ISA
我们之前的探索:isa
是isa_t
类型,为什么在objc_object
中的isa
是Class类型
呢?
1、万物皆对象,isa是可以用Class来接收的
2、在早期,isa的就是用来返回一个类,后来优化为返回nonpointer和纯净的isa,这里可能是延续了一个习惯
3、在ISA()
方法(分析isa的文章中有提到)中可以看到,返回值是一个(Class)
强转类型
4、NSObject
与objc_object
的关系 (对象)
我们知道在oc中,万物皆对象!其实objc_object
就是一切的鼻祖! NSObject
算是objc_object
的一个仿写、衍生类,结构按理来说应该和objc_object
是一模一样的,验证一下:
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
也可以这么说,objc_object
是c,NSObject
是oc,NSObject
是对objc_object
的封装,到底层编译还是会变成objc_object
。
NSObject
(对象)的本质是objc_object
5、NSObject
与objc_class
的关系 (类)
与objc_class
对应的,其实是NSObject Class
,是NSObject
这个类。底层编译就会变成objc_class
NSObject
(类)的本质是objc_class
三、类结构里存放了什么
从上面的分析,可以看出:类是一个结构体,里面存放了isa、superClass、cache、bits等。
1、Class ISA
isa指针
,不仅实例对象中有,类对象中也有。占8字节
2、Class superclass
superclass
父类,class* 本身也是一个指针。占8字节
3、cache_t cache
cache
缓存,追踪进去看一下cache_t
结构体的类型,而不是结构体指针类型(占8位),就需要计算一下了:
struct cache_t {
struct bucket_t *_buckets; // 结构体*类型=对象,8
mask_t _mask; // int32位类型,4
mask_t _occupied; // int32位类型,4
···一些方法函数,不占内存,省略···
}
typedef uint32_t mask_t;
cache_t
的类型,占16位
4、class_data_bits_t bits
bits
其实是用来存储数据的,我们在前面计算内存大小,是为了在内存中偏移,直接拿到bits
,来验证里面是否存放了我们在最前面准备工作中的属性、方法。
在objc_class
中,前面省略了一个方法data()
,这个是class_rw_t
类型的结构体
struct objc_class : objc_object {
//···省略前面
class_data_bits_t bits;
class_rw_t *data() {
return bits.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;
//···省略后面
}
偷偷开个上帝视角,看一下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;
}
};
我们可以看出,这里面其实是通过bits
来存储我们的methods
、properties
、protocols
等等信息的。(ro
中也有相关信息的属性)我们稍后来验证一下~
四、类的属性存储在哪
我们在第三部分,计算出从首地址偏移32位就能拿到bits
。32对应的16进制是0x20,然后(class_data_bits_t *)
强转,我们直接LLDB来操作一番(每次重新运行,内存地址可能就跟上一次不一样了):
我们看到bits
里面有properties!
我们声明的属性和成员变量是不是存在这里呢?打印一下:
(property_array_t 其实是一个二维数组的类型。我们继续打印里面的信息)
的确有!但是,有没有发现这个里面的信息并不全,因为直接在bits
里没有找到我们声明的成员变量的地方。
开启上帝视角:我们直接读ro
!我们来看一下:
属性 - baseProperties
:
成员变量 - ivars
:
果然,在这里面都找到了!
五、类的方法存储在哪
1、类的实例方法
继续看ro
下的baseMethodList
哇!list有4个,分别是:类的实例方法、属性帮我们生成的setter、getter方法,还有C++的方法。
但是,我们声明的 类方法 在哪里呢?
2、类的类方法
我们开启上帝视角:类方法在元类中,我们去验证一下:
类的类方法,的确在这里!
之前说过,万物皆对象,类也是个对象,对象的实例方法在类中;
那类方法可以理解为 类对象的实例方法,就在元类中。
六、API验证猜想
这里了解即可,直接把源码贴出来,有兴趣的小伙伴可以自行运行:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface Person : NSObject{
NSString *hobby;
}
@property (nonatomic, copy) NSString *nickName;
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation Person
- (void)sayHello{
NSLog(@"LGPerson say : Hello!!!");
}
+ (void)sayHappy{
NSLog(@"LGPerson say : Happy!!!");
}
@end
void testObjc_copyIvar_copyProperies(Class pClass){
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Ivar const ivar = ivars[i];
//获取实例变量名
const char*cName = ivar_getName(ivar);
NSString *ivarName = [NSString stringWithUTF8String:cName];
NSLog(@"class_copyIvarList:%@",ivarName);
}
free(ivars);
unsigned int pCount = 0;
objc_property_t *properties = class_copyPropertyList(pClass, &pCount);
for (unsigned int i=0; i < pCount; i++) {
objc_property_t const property = properties[i];
//获取属性名
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
//获取属性值
NSLog(@"class_copyProperiesList:%@",propertyName);
}
free(properties);
}
void testObjc_copyMethodList(Class pClass){
unsigned int count = 0;
Method *methods = class_copyMethodList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Method const method = methods[i];
//获取方法名
NSString *key = NSStringFromSelector(method_getName(method));
NSLog(@"Method, name: %@", key);
}
free(methods);
}
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__);
}
void testClassMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy)); // ?
NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
NSLog(@"%s",__func__);
}
void testIMP_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
NSLog(@"%s",__func__);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [Person alloc];
Class pClass = object_getClass(person);
testObjc_copyIvar_copyProperies(pClass);
testObjc_copyMethodList(pClass);
testInstanceMethod_classToMetaclass(pClass);
testClassMethod_classToMetaclass(pClass);
NSLog(@"Hello, World!");
}
return 0;
}
六、总结
今天通过LLDB一步步调试,虽然有点繁琐,但也验证了许多只知其一不知其二的地方:
-
类的本质是个结构体(万对皆对象,类的本质是个对象,对象的本质是个结构体)
-
类的结构里包含了 isa、superClass、cache、bits
-
Class
是一个objc_class
类型 -
objc_class
继承自objc_object
-
类的属性、成员变量、方法、协议等 存储在
class_ro_t
这个结构体里 -
类的类方法,存在元类的
class_ro_t
中 -
类和元类的创建时机是在编译时(探索isa时,在最后验证过)