本节讲什么
本节从源码及lldb的形式来分析类中的属性、成员变量、方法、类方法。
class_data_bits_t
从源码中可以知道类在底层是以objc_class形式存在
struct objc_class : objc_object {
objc_class(const objc_class&) = delete;
objc_class(objc_class&&) = delete;
void operator=(const objc_class&) = delete;
void operator=(objc_class&&) = delete;
// 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
...
}
复制代码
从上面源码中可以看出,objc_class中有isa、superclass、cache、bits等。我们上一节中讲到了继承以及isa的指向,我们接下来看一下bits中存在着什么?
创建一个Person类如下:
@interface Person : NSObject{
NSString *subject;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *hobby;
- (void)sayNB;
+ (void)say666;
@end
@implementation Person
- (instancetype)init{
if (self = [super init]) {
self.name = @"Cooc";
}
return self;
}
- (void)sayNB{
}
+ (void)say666{
}
@end
复制代码
找到首地址
0x100008380然后我们需要找到到class_data_bits_t bits;的偏移量,就可以取到bits
我们来看cache_t的结构
struct cache_t {
private:
explicit_atomic<uintptr_t> _bucketsAndMaybeMask;//8
union {
struct {
explicit_atomic<mask_t> _maybeMask;//4
#if __LP64__
uint16_t _flags;//2
#endif
uint16_t _occupied;//2
};
explicit_atomic<preopt_cache_t *> _originalPreoptCache;//8
};
......
}
复制代码
由共用体结构可知,cache_t占16字节
// Class ISA; 8
Class superclass; 8
cache_t cache; 16 // formerly cache pointer and vtable
class_data_bits_t bits;
复制代码
所以首地址移动32就可以到bits的位置。
struct class_data_bits_t {
friend objc_class;
......
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
......
}
复制代码
通过data()方法取出bits
properties
我们来看一下class_rw_t中有什么
struct class_rw_t {
......
const class_ro_t *ro() const {
auto v = get_ro_or_rwe();
if (slowpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
}
return v.get<const class_ro_t *>(&ro_or_rw_ext);
}
void set_ro(const class_ro_t *ro) {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro = ro;
} else {
set_ro_or_rwe(ro);
}
}
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
} else {
return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
}
}
const property_array_t properties() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
} else {
return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
}
}
......
}
复制代码
我们打印一下properties里面的内容
可以看到有两个属性name,和hobby
ivars
但是成员变量subject在哪里呢,属性=成员变量+setter+getter
我们看到class_rw_t有一个ro,我们来读取一下
class_ro_t中有一个ivars,打印如下,有三个成员变量
可以看到class_ro_t中还有一个baseProperties,我们也来打印一下
methods_t
我们来打印一下方法:
有6个方法,我们分别打印出来
但是这个里面并没有我们的+ (void)say666这个➕方法。
对象方法和类方法在底层都是方法,我们知道对象方法是存在类中,那么类方法呢,类方法对于元类来说也是对象方法,存在于元类当中,在底层只有对象方法。(这就是为什么要存在一个元类)
我们首先拿到metaclass的地址,然后打印里面的methods如下:
得出如下关系图:
typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
struct objc_class : objc_object {
.....
}
复制代码
万物皆对象。
从runtime角度来分析
runtime已经提供给我们很多有用的api,我们可以根据api来验证上面的图片。
@interface Person : NSObject
{
NSString *subject;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *hobby;
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation Person
- (instancetype)init{
if (self = [super init]) {
self.name = @"Cooc";
}
return self;
}
- (void)sayHello{
}
+ (void)sayHappy{
}
@end
复制代码
获取对象方法
void lgObjc_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));
LGLog(@"Method, name: %@", key);
}
free(methods);
}
Person *person = [Person alloc];
Class pClass = object_getClass(person);
lgObjc_copyMethodList(pClass);
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
NSLog(@"*************");
lgObjc_copyMethodList(metaClass);
/***** 打印结果
Method, name: sayHello
Method, name: hobby
Method, name: setHobby:
Method, name: init
Method, name: name
Method, name: .cxx_destruct
Method, name: setName:
2021-06-20 10:46:08.432824+0800 002-类方法归属分析[10011:757512] *************
Method, name: sayHappy
***/
复制代码
void lgClassMethod_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));
// - (void)sayHello;
// + (void)sayHappy;
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
// 0x1000081c8-0x0-0x0-0x100008160
复制代码
我们来看一下class_getClassMethod的源码:
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
复制代码
所以底层实现中是不区分- +方法的,都是方法,只有对象方法,类方法对于元类来说也是对象方法。
当我们打印方法的实现的时候我们发现
void lgIMP_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));// 0
// sel -> imp 方法的查找流程 imp_farw
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy)); // 0
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
NSLog(@"%s",__func__);
}
//0x100003b50-0x7fff204805c0-0x7fff204805c0-0x100003b60
复制代码
imp2、imp3我们没有实现,但是也是有值, 我们来打印一下
这是为什么呢,我们来看一下class_getMethodImplementation的源码
IMP class_getMethodImplementation(Class cls, SEL sel)
{
IMP imp;
if (!cls || !sel) return nil;
lockdebug_assert_no_locks_locked_except({ &loadMethodLock });
imp = lookUpImpOrNilTryCache(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
// Translate forwarding function to C-callable external version
if (!imp) { //如果没有imp,则会返回_objc_msgForward
return _objc_msgForward;
}
return imp;
}
复制代码
可以看到如果么有imp,则会返回_objc_msgForward,_objc_msgFordward接下来的流程又是什么样子的呢。我们能通过这个流程以后做些什么呢,怎么给业务赋能呢?,先留作思考。
补充
1.关于 v16@0:8
developer.apple.com/library/arc…
2.lldb使用命令解释
3.objc_setProperty
@interface Person : NSObject
{
// STRING int double float char bool
NSString *hobby; // 字符串
int a;
NSObject *objc; // 结构体
}
@property (nonatomic, copy) NSString *nickName;
@property (atomic, copy) NSString *acnickName;
@property (nonatomic) NSString *nnickName;
@property (atomic) NSString *anickName;
复制代码
clang查看
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_LGPerson_setNickName_(LGPerson * self, SEL _cmd, NSString *nickName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _nickName), (id)nickName, 0, 1); }
复制代码
有一个这个方法:objc_setProperty,看objc的源码,没有看到调用,猜测应该是在llvm中进行了绑定,但是别的为什么没有这个方法呢,下载llvm,看到源码
是在有copy修饰的情况下,才会有这个objc_setProperty方法的调用,objc_setProperty在llvm中做了什么呢?