评论

收藏

[iOS开发] OC源码剖析对象的本质

移动开发 移动开发 发布于:2021-09-25 21:01 | 阅读数:529 | 评论:0

1. 类的底层实现

先写一个 Person 类:
@interface Person : NSObject
@property (nonatomic, copy) NSString *p_name;
@property (nonatomic, assign) int p_age;
- (void)p_instanceMethod1;
@end
@implementation Person
- (void)p_instanceMethod1{
  NSLog(@"%s",__func__);
}
@end


使用 clang 编译器, clang -rewrite-objc Person.m -o Person.cpp  将 Person.m  编译成 Person.cpp 文件,部分代码如下:
/// 1: Person 类型的底层结构
struct NSObject_IMPL {
  Class isa;
};
struct Person_IMPL {
  struct NSObject_IMPL NSObject_IVARS;
  int _p_age;
  NSString * _Nonnull _p_name;
};
/// 2: p_name 属性的底层结构
// get
static NSString * _Nonnull _I_Person_p_name(Person * self, SEL _cmd) { return (*(NSString * _Nonnull *)((char *)self + OBJC_IVAR_$_Person$_p_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
// set
static void _I_Person_setP_name_(Person * self, SEL _cmd, NSString * _Nonnull p_name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct Person, _p_name), (id)p_name, 0, 1); }
/// 3: p_age 类型的底层结构 
// get
static int _I_Person_p_age(Person * self, SEL _cmd) { return (*(int *)((char *)self + OBJC_IVAR_$_Person$_p_age)); }
// set
static void _I_Person_setP_age_(Person * self, SEL _cmd, int p_age) { (*(int *)((char *)self + OBJC_IVAR_$_Person$_p_age)) = p_age; }
/// 4: p_instanceMethod1 方法的底层结构
static void _I_Person_p_instanceMethod1(Person * self, SEL _cmd) {
  NSLog((NSString *)&__NSConstantStringImpl__var_folders_86_0y_j3bzj65z6vw6hy1chw_4m0000gp_T_Person_f010c0_mi_0,__func__);
}

  • NSObject 类被编译成了 NSObject_IMPL 的结构体。
  • Person 类被编译成了 Person_IMPL 的结构体。


  • Person 类的内部还增加了一个 NSObject_IMPL 的结构体



    • 我们知道 Person 继承于 NSObject, 所以它的底层实现中是第一个成员是父类的结构体,就是底层继承的实现方式。用这样的方式拥有父类所有的成员变量。
    • NSObject_IMPL 是 NSObject 类的编译后的结构体,它的内部只有一个 Class 类型的 isa 成员变量。我们知道isa 是 isa_t 类型的,那为什么在这里定义成 Class 类型呢?这是为了更加直观的提现出它代表的是类的信息,所以在获取isa 的方法中,将它强制转换成了Class 类型, 代码如下:

inline Class objc_object::ISA() {
  ...
  
  return (Class)(isa.bits & ISA_MASK)
}
总结:
1.类的底层实现是结构体。
2.继承是通过把父类的结构体声明为本类结构体的第一个成员变量实现的。


2. isa_t 的类型

联合体: 所有成员可以是不同的类型,但是公用一块内存区域,设置了一个成员变量就会覆盖另一个成员变量的数据。优点是节省空间。
union isa_t { //联合体
  isa_t() { }
  isa_t(uintptr_t value) : bits(value) { }
  //提供了cls 和 bits ,两者是互斥关系
  Class cls;
  uintptr_t bits;
#if defined(ISA_BITFIELD)
  struct {
    ISA_BITFIELD;  // defined in isa.h
  };
#endif
};
isa 指针占用8字节,64位。64位中不同的位代表不同的含义:
DSC0000.png


对象与类的 isa 的指向关系
DSC0001.png


对象.isa -> 类.super -> 父类.super -> 根类.super -> nil
类.isa -> 元类.super -> 父元类.super -> 根元类.super -> 根类.super -> nil
元类.isa = 父元类.isa = 根元类.isa = 根元类
应用:判断对象类型
下面的打印结果是什么:
// [NSObject class] = NSObject
// object_getClass((id)[NSObject class]) = NSObject meta class
// 沿着 NSObject 的继承者链去找根元类 -> 根类 == NSObject meta class 或者 NSObject meta class 的父类的实例
BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [(id)[TestObject class] isKindOfClass:[TestObject class]];
BOOL res4 = [(id)[TestObject class] isMemberOfClass:[TestObject class]];
只有第一个是YES, 剩下的都是NO。
isKindOfClass: 判断自己的isa 指向的类是否等于传入的类,不等于的话,找自己的继承连中的父类看有没有等于传入的类,有则YES,没有则NO
isMemberOfClass 判断自己的isa 指向的类是否等于传入的类,等于则YES,不等于则NO

源码:
// 类对象,是否是指定的元类的实例
+ (BOOL)isMemberOfClass:(Class)cls {
  return object_getClass((id)self) == cls;
}
// 实例对象,是否是指定的类的实例
- (BOOL)isMemberOfClass:(Class)cls {
  return [self class] == cls;
}
// 类对象,是否是指定的元类cls的实例,或者是cls继承者链中子类的实例
+ (BOOL)isKindOfClass:(Class)cls {
  for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->super_class) {
    if(tcls == cls) return YES;
  }
  return NO;
}
// 实例对象,是否是指定的类的实例,或者是cls继承者链中子类的实例
-(BOOL)isKindOfClass:(Class)cls {
  for(Class tcls = [self class]; tcls; tcls = tcls->super_class) {
    if(tcls == cls) return YES;
  }
  return NO;
}
青山不改,绿水长流,感谢每位佳人支持!


   
   
   
                        

关注下面的标签,发现更多相似文章