初探OC底层原理之《类的底层原理结构02》

类的结构分析

  • 根据底层继承找到 objc_class


    image.png
  • obj_class 里面成员变量 isa, superclass,cache,bits 猜想 我们上述了解了isa superclass cache 现在探索一下bits


    image.png
  • 从上图可以看出cache里面结果是一个互斥的结构体 explicit_atomic _originalPreoptCache 是一个结构指针根据上图得知cache所占的内存大小是16字节, bits 总共偏移了32字节
  • bits 根据内存偏移计算 bits的内存地址 = 类的首的地址 +0x20

探索bits内部结构

  • 探索class_data_bits_t 内部结构
@interface LGPerson : NSObject{
    NSString *subject;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *hobby;

- (void)mayNB;
+ (void)maysay;
  • lldb 调试如下


    image.png
  • 进入class_rw_t 查看结构体如下
const method_array_t methods() const {
    auto v = get_ro_or_rwe();
    if (v.is()) {
        return v.get(&ro_or_rw_ext)->methods;
    } else {
        return method_array_t{v.get(&ro_or_rw_ext)->baseMethods()};
    }
}

const property_array_t properties() const {
    auto v = get_ro_or_rwe();
    if (v.is()) {
        return v.get(&ro_or_rw_ext)->properties;
    } else {
        return property_array_t{v.get(&ro_or_rw_ext)->baseProperties};
    }
}

const protocol_array_t protocols() const {
    auto v = get_ro_or_rwe();
    if (v.is()) {
        return v.get(&ro_or_rw_ext)->protocols;
    } else {
        return protocol_array_t{v.get(&ro_or_rw_ext)->baseProtocols};
    }
}
  • 在class_rw_t 找到methods(),properties(),protocols() 先探索一下properties()
(lldb) p $3.properties()
(const property_array_t) $9 = {
  list_array_tt = {
     = {
      list = {
        ptr = 0x0000000100008260
      }
      arrayAndFlag = 4295000672
    }
  }
}
  Fix-it applied, fixed expression was: 
    $3->properties()
(lldb) p $9.list
(const RawPtr) $10 = {
  ptr = 0x0000000100008260
}
(lldb) p $10.ptr
(property_list_t *const) $11 = 0x0000000100008260
(lldb) p *$11
(property_list_t) $12 = {
  entsize_list_tt = (entsizeAndFlags = 16, count = 2)
}
(lldb) p $12.get(0)
(property_t) $13 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb) p $12.get(1)
(property_t) $14 = (name = "hobby", attributes = "T@\"NSString\",C,N,V_hobby")

  • 从上面输出结果得到properties()里面是存放属性变量
  • 探索一下methods()
(lldb) p $3.methods()
(const method_array_t) $15 = {
  list_array_tt = {
     = {
      list = {
        ptr = 0x0000000100008160
      }
      arrayAndFlag = 4295000416
    }
  }
}
  Fix-it applied, fixed expression was: 
    $3->methods()
(lldb) p $15.list
(const method_list_t_authed_ptr) $16 = {
  ptr = 0x0000000100008160
}
(lldb) p $16.ptr
(method_list_t *const) $17 = 0x0000000100008160
(lldb) p *$17
(method_list_t) $18 = {
  entsize_list_tt = (entsizeAndFlags = 27, count = 6)
}
(lldb) p *$18.get(0)
error: :1:1: indirection requires pointer operand ('method_t' invalid)
*$18.get(0)
^~~~~~~~~~~
(lldb) p $18.get(0)
(method_t) $19 = {}
(lldb) p $18.get(1)
(method_t) $20 = {}
(lldb) p $18.get(2)
(method_t) $21 = {}
(lldb) 
  • methods 没有存值吗?看下methods的内部结构如下图


    image.png
  • method_t 的结构体中找到了big成员变量的结构体 以及big调用方法


    image.png
(lldb) p $2->data()
(class_rw_t *) $24 = 0x0000000100692250
(lldb) p $24->methods()
(const method_array_t) $25 = {
  list_array_tt = {
     = {
      list = {
        ptr = 0x0000000100008160
      }
      arrayAndFlag = 4295000416
    }
  }
}
(lldb) p $25.list
(const method_list_t_authed_ptr) $26 = {
  ptr = 0x0000000100008160
}
(lldb) p $26.ptr
(method_list_t *const) $27 = 0x0000000100008160
(lldb) p *$27
(method_list_t) $28 = {
  entsize_list_tt = (entsizeAndFlags = 27, count = 6)
}
(lldb) p $28.get(0).big()
(method_t::big) $29 = {
  name = "sayNB"
  types = 0x0000000100003f77 "v16@0:8"
  imp = 0x0000000100003d40 (KCObjcBuild`-[LGPerson sayNB])
}
(lldb) p $28.get(1).big()
(method_t::big) $30 = {
  name = "hobby"
  types = 0x0000000100003f6f "@16@0:8"
  imp = 0x0000000100003db0 (KCObjcBuild`-[LGPerson hobby])
}
(lldb) p $28.get(2).big()
(method_t::big) $31 = {
  name = "setHobby:"
  types = 0x0000000100003f8b "v24@0:8@16"
  imp = 0x0000000100003de0 (KCObjcBuild`-[LGPerson setHobby:])
}
(lldb) p $28.get(3).big()
(method_t::big) $32 = {
  name = "init"
  types = 0x0000000100003f6f "@16@0:8"
  imp = 0x0000000100003ce0 (KCObjcBuild`-[LGPerson init])
}
(lldb) p $28.get(4).big()
(method_t::big) $33 = {
  name = "name"
  types = 0x0000000100003f6f "@16@0:8"
  imp = 0x0000000100003d50 (KCObjcBuild`-[LGPerson name])
}
(lldb) p $28.get(5).big()
(method_t::big) $34 = {
  name = "setName:"
  types = 0x0000000100003f8b "v24@0:8@16"
  imp = 0x0000000100003d80 (KCObjcBuild`-[LGPerson setName:])
}
  • 在methods里面没有找到subject成员变量 和 + (void)maysay 类方法 只找到属性方法 那这2个在哪里呢?????
  • 探索一下ro()输出如下:
(lldb) p $2->data()
(class_rw_t *) $35 = 0x0000000100692250
(lldb) p $35.ro()
(const class_ro_t *) $36 = 0x0000000100008118
  Fix-it applied, fixed expression was: 
    $35->ro()
(lldb) p *$36
(const class_ro_t) $37 = {
  flags = 0
  instanceStart = 8
  instanceSize = 32
  reserved = 0
   = {
    ivarLayout = 0x0000000000000000
    nonMetaclass = nil
  }
  name = {
    std::__1::atomic = "LGPerson" {
      Value = 0x0000000100003ea8 "LGPerson"
    }
  }
  baseMethodList = 0x0000000100008160
  baseProtocols = 0x0000000000000000
  ivars = 0x00000001000081f8
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000100008260
  _swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $37.ivars
(const ivar_list_t *const) $38 = 0x00000001000081f8
(lldb) p *$38
(const ivar_list_t) $39 = {
  entsize_list_tt = (entsizeAndFlags = 32, count = 3)
}
(lldb) p $39.get(0)
(ivar_t) $40 = {
  offset = 0x0000000100008318
  name = 0x0000000100003f2f "subject"
  type = 0x0000000100003f7f "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $39.get(1)
(ivar_t) $41 = {
  offset = 0x0000000100008320
  name = 0x0000000100003f37 "_name"
  type = 0x0000000100003f7f "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $39.get(2)
(ivar_t) $42 = {
  offset = 0x0000000100008328
  name = 0x0000000100003f3d "_hobby"
  type = 0x0000000100003f7f "@\"NSString\""
  alignment_raw = 3
  size = 8
}
  • 从lldb打印得到成员变量subject 最后还剩类方法在哪里呢???

  • 类方法归属分析
    1.根据上述分析没有找到类方法猜想类方法是否存在元类中????

(lldb) p/x object_getClass(LGPerson.class)
(Class) $13 = 0x00000001000083a8
(lldb) p/x 0x00000001000083c8
(long) $14 = 0x00000001000083c8
(lldb) p/x (class_data_bits_t *)0x00000001000083c8
(class_data_bits_t *) $15 = 0x00000001000083c8
(lldb) p $15.data()
(class_rw_t *) $16 = 0x00000001008339c0
  Fix-it applied, fixed expression was: 
    $15->data()
(lldb) p $16.methods()
(const method_array_t) $17 = {
  list_array_tt = {
     = {
      list = {
        ptr = 0x00000001000082d0
      }
      arrayAndFlag = 4295000784
    }
  }
}
  Fix-it applied, fixed expression was: 
    $16->methods()
(lldb) p $17.list
(const method_list_t_authed_ptr) $18 = {
  ptr = 0x00000001000082d0
}
(lldb) p $18.ptr
(method_list_t *const) $19 = 0x00000001000082d0
(lldb) p *$19
(method_list_t) $20 = {
  entsize_list_tt = (entsizeAndFlags = 27, count = 1)
}
(lldb) p $20.get(0).big()
(method_t::big) $21 = {
  name = "say666"
  types = 0x0000000100003f77 "v16@0:8"
  imp = 0x0000000100003e10 (KCObjcBuild`+[LGPerson say666])
}
  • 总结: 类方法所在位置在元类中的methods()中

二.观察类的属性

  • 在上面输出0x0000000100003f77 "v16@0:8" 可以看到"v16@0:8"
  • 了解TypeEncoding 官方图表如下:


    image.png
  • 在上面输出0x0000000100003f77 "v16@0:8" 可以看到"v16@0:8"
  • 创建.cpp文件中观察到一个特殊的字符串


    image.png

二.分析属性的get和set方法(objc_getProperty和objc_setProperty)

  • 1.clang分析属性setter方法
    • 在main.m中 定义SBTeacher类如下:
@interface SBTeacher : NSObject

@property (nonatomic, strong)  NSString *sbName;

@property (nonatomic, copy)  NSString *name;

@end


@implementation SBTeacher

@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        SBTeacher *teacher = [SBTeacher alloc];
        teacher.sbName = @"SB";
        teacher.name = @"Name666";
    }
    return 0;
}

用命令行编译成c++文件

clang -rewrite-objc main.m -o main.cpp
  • 分析main.cpp 文件找到set方法


    image.png
  • 从中发现 sbName (*(NSString **) 有个指针修饰 而name 是objc_setProperty 修饰

  • 使用llvm源码了解objc_setProperty,源码下载:[llvm] (https://github.com/llvm/llvm-project "llvm")

  • 在llvm 找到如图


    image.png
  • 根源方法调用反推找到getSetPropertyFn()方法找到GetPropertySetFunction()

image.png

找到GetPropertySetFunction()调用地方


image.png
  • PropertyImplStrategy strategy(CGM, propImpl);:这是C++语法,创建了一个PropertyImplStrategy类型的变量strategy,并调用PropertyImplStrategy的构造函数初始化
    根据strategy.getKind()条件判断执行switch语句case条件判断
    所以分析PropertyImplStrategy的实现


    image.png
namespace {
  class PropertyImplStrategy {
  public:
    enum StrategyKind {
      /// The 'native' strategy is to use the architecture's provided
      /// reads and writes.
      Native,

      /// Use objc_setProperty and objc_getProperty.
      GetSetProperty,

      /// Use objc_setProperty for the setter, but use expression
      /// evaluation for the getter.
      SetPropertyAndExpressionGet,

      /// Use objc_copyStruct.
      CopyStruct,

      /// The 'expression' strategy is to emit normal assignment or
      /// lvalue-to-rvalue expressions.
      Expression
    };

    StrategyKind getKind() const { return StrategyKind(Kind); }

    bool hasStrongMember() const { return HasStrong; }
    bool isAtomic() const { return IsAtomic; }
    bool isCopy() const { return IsCopy; }

    CharUnits getIvarSize() const { return IvarSize; }
    CharUnits getIvarAlignment() const { return IvarAlignment; }

    PropertyImplStrategy(CodeGenModule &CGM,
                         const ObjCPropertyImplDecl *propImpl);

  private:
    unsigned Kind : 8;
    unsigned IsAtomic : 1;
    unsigned IsCopy : 1;
    unsigned HasStrong : 1;

    CharUnits IvarSize;
    CharUnits IvarAlignment;
  };
}

/// Pick an implementation strategy for the given property synthesis.
PropertyImplStrategy::PropertyImplStrategy(CodeGenModule &CGM,
                                     const ObjCPropertyImplDecl *propImpl) {
  const ObjCPropertyDecl *prop = propImpl->getPropertyDecl();
  ObjCPropertyDecl::SetterKind setterKind = prop->getSetterKind();

  IsCopy = (setterKind == ObjCPropertyDecl::Copy);
  IsAtomic = prop->isAtomic();
  HasStrong = false; // doesn't matter here.

  // Evaluate the ivar's size and alignment.
  ObjCIvarDecl *ivar = propImpl->getPropertyIvarDecl();
  QualType ivarType = ivar->getType();
  auto TInfo = CGM.getContext().getTypeInfoInChars(ivarType);
  IvarSize = TInfo.Width;
  IvarAlignment = TInfo.Align;

  // If we have a copy property, we always have to use getProperty/setProperty.
  // TODO: we could actually use setProperty and an expression for non-atomics.
  if (IsCopy) {
    Kind = GetSetProperty;
    return;
  }
  • 根据构造函数的实现苹果官方解释(If we have a copy property, we always have to use getProperty/setProperty.)
  • 总结:使用copy修饰的属性,生成的setter方法会调用objc_setPropert和objc_getProperty

三.分析属性修饰的作用 strong,copy,retain,retain,atomic,nonatomic

  • 根据cpp 文件分析
@interface SBTeacher : NSObject

@property (atomic, copy)  NSString *sbName;
@property (atomic, strong)  NSString *atsbName;
@property (atomic, retain)  NSString * resbName;

@property (nonatomic, copy)  NSString *nosbName;
@property (nonatomic, strong)  NSString *noatsbName;
@property (nonatomic, retain)  NSString * noresbName;


@property (atomic, assign)  NSInteger age;

@property (nonatomic, assign)  NSInteger noage;

@end
extern "C" __declspec(dllimport) id objc_getProperty(id, SEL, long, bool);

static NSString * _I_SBTeacher_sbName(SBTeacher * self, SEL _cmd) { typedef NSString * _TYPE;
return (_TYPE)objc_getProperty(self, _cmd, __OFFSETOFIVAR__(struct SBTeacher, _sbName), 1); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_SBTeacher_setSbName_(SBTeacher * self, SEL _cmd, NSString *sbName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SBTeacher, _sbName), (id)sbName, 1, 1); }

static NSString * _I_SBTeacher_atsbName(SBTeacher * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SBTeacher$_atsbName)); }
static void _I_SBTeacher_setAtsbName_(SBTeacher * self, SEL _cmd, NSString *atsbName) { (*(NSString **)((char *)self + OBJC_IVAR_$_SBTeacher$_atsbName)) = atsbName; }

static NSString * _I_SBTeacher_resbName(SBTeacher * self, SEL _cmd) { typedef NSString * _TYPE;
return (_TYPE)objc_getProperty(self, _cmd, __OFFSETOFIVAR__(struct SBTeacher, _resbName), 1); }
static void _I_SBTeacher_setResbName_(SBTeacher * self, SEL _cmd, NSString *resbName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SBTeacher, _resbName), (id)resbName, 1, 0); }

static NSString * _I_SBTeacher_nosbName(SBTeacher * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SBTeacher$_nosbName)); }
static void _I_SBTeacher_setNosbName_(SBTeacher * self, SEL _cmd, NSString *nosbName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SBTeacher, _nosbName), (id)nosbName, 0, 1); }

static NSString * _I_SBTeacher_noatsbName(SBTeacher * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SBTeacher$_noatsbName)); }
static void _I_SBTeacher_setNoatsbName_(SBTeacher * self, SEL _cmd, NSString *noatsbName) { (*(NSString **)((char *)self + OBJC_IVAR_$_SBTeacher$_noatsbName)) = noatsbName; }

static NSString * _I_SBTeacher_noresbName(SBTeacher * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SBTeacher$_noresbName)); }
static void _I_SBTeacher_setNoresbName_(SBTeacher * self, SEL _cmd, NSString *noresbName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SBTeacher, _noresbName), (id)noresbName, 0, 0); }

static NSInteger _I_SBTeacher_age(SBTeacher * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_SBTeacher$_age)); }
static void _I_SBTeacher_setAge_(SBTeacher * self, SEL _cmd, NSInteger age) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_SBTeacher$_age)) = age; }

static NSInteger _I_SBTeacher_noage(SBTeacher * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_SBTeacher$_noage)); }
static void _I_SBTeacher_setNoage_(SBTeacher * self, SEL _cmd, NSInteger noage) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_SBTeacher$_noage)) = noage; }
  • 总结 :
    • objc_getProperty调用如下:
      • sbName 使用atomic, copy修饰
      • resbName 使用atomic, retain修饰
    • objc_setProperty 调用如下
      • sbName 使用 atomic, copy 修饰
      • resbName 使用 atomic, retain 修饰
      • nosbName 使用 nonatomic, copy 修饰
      • noresbName 使用 nonatomic, retain 修饰
    • 根据分析得到copy,retain,retain 修饰会调用objc_setProperty方法,atomic和nonatomic会影响objc_getProperty的调用。其它的是内存地址平移取值或赋值

你可能感兴趣的:(初探OC底层原理之《类的底层原理结构02》)