OC底层5-类的探究分析(下)

2021-06-21  本文已影响0人  JEFF009

1.类的内存的ro数据

还是先上代码:


#import <Foundation/Foundation.h>
#import "LGPerson.h"

NS_ASSUME_NONNULL_BEGIN

@interface LGTeacher : LGPerson
@property (nonatomic, copy) NSString *hobby;
- (void)teacherSay;
@end

NS_ASSUME_NONNULL_END



#import "LGTeacher.h"

@implementation LGTeacher
- (instancetype)init{
    if (self == [super init]) {
        NSLog(@"我来了: %@",self);
        return self;
    }
    return nil;
}

- (void)teacherSay{
    NSLog(@"%s",__func__);
}

@end
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface LGPerson : NSObject{
    NSString *subject;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *hobby;

- (void)sayNB;
+ (void)say666;
@end

NS_ASSUME_NONNULL_END



#import "LGPerson.h"

@implementation LGPerson

- (instancetype)init{
    if (self = [super init]) {
        self.name = @"Cooci";
    }
    return self;
}

- (void)sayNB{
    
}
+ (void)say666{
    
}

@end

上一章节OC底层4-类的探究分析(上)分析时,LGPerson里面的成员变量 NSString *subject,类方法+say666还没有找到,先来打印下

(lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100004410 LGPerson
(lldb) p (class_data_bits_t *)0x0000000100004430
(class_data_bits_t *) $1 = 0x0000000100004430
(lldb) p $1->data()
(class_rw_t *) $2 = 0x000000010076dd60
(lldb) p *$2
(class_rw_t) $3 = {
  flags = 2148007936
  witness = 0
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4294984104
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
}
(lldb) 

这里还有个问题打印出class_rw_t里面firstSubclass = nil ,我们知道是有一个LGTeacher继承LGPerson,为什么会nil?接下来我们再来个东西,先打印下: p LGTeacher.class

(lldb) p LGTeacher.class
(Class) $4 = LGTeacher
(lldb) p $1->data()
(class_rw_t *) $5 = 0x000000010076dd60
(lldb) p *$5
(class_rw_t) $6 = {
  flags = 2148007936
  witness = 0
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4294984104
    }
  }
  firstSubclass = LGTeacher
  nextSiblingClass = NSUUID
}
(lldb) 

此时发现 firstSubclass = LGTeacher出现了,我们项目中没有做任何初始化,奇怪了?原因是这里类执行了懒加载,至于懒加载我们后续文章在做分析,接下来我们还是继续来看如何找到成员变量 NSString *subject;

(lldb) p $6.ro()
(const class_ro_t *) $7 = 0x00000001000041a8
(lldb) p *$7
(const class_ro_t) $8 = {
  flags = 0
  instanceStart = 8
  instanceSize = 32
  reserved = 0
   = {
    ivarLayout = 0x0000000000000000
    nonMetaclass = nil
  }
  name = {
    std::__1::atomic<const char *> = "LGPerson" {
      Value = 0x0000000100003ef8 "LGPerson"
    }
  }
  baseMethodList = 0x00000001000041f0
  baseProtocols = 0x0000000000000000
  ivars = 0x0000000100004288
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x00000001000042f0
  _swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) 

得到一个class_ro_t结构,继续往下执行

(lldb) p $8.ivars
(const ivar_list_t *const) $9 = 0x0000000100004288
(lldb) p *$9
(const ivar_list_t) $10 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 3)
}
(lldb) p $10.get(0)
(ivar_t) $11 = {
  offset = 0x00000001000043a8
  name = 0x0000000100003eae "subject"
  type = 0x0000000100003f7f "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $10.get(1)
(ivar_t) $12 = {
  offset = 0x00000001000043b0
  name = 0x0000000100003eb6 "_name"
  type = 0x0000000100003f7f "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $10.get(2)
(ivar_t) $13 = {
  offset = 0x00000001000043b8
  name = 0x0000000100003ebc "_hobby"
  type = 0x0000000100003f7f "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $10.get(3)
Assertion failed: (i < count), function get, file /Users/fengjiefeng/Desktop/逻辑教育V14--iOS底层开发课程/1.iOS底层大师班/20210616-大师班-第4节课-类的原理分析上/20210616-大师班第4天-类的原理分析上/01--课堂代码/004-类的结构分析/runtime/objc-runtime-new.h, line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb) 

到这,我们已经将LGPerson 成员变量和属性都找出来了,
备注:
属性&成员变量&实例变量的区别
成员变量:String、 int 、 double、 float、 char、 bool
属性 = 带下划线成员变量 + setter + getter ⽅法
实例变量 : 特殊的成员变量 (类的实例化)

接下来查找类方法+say666

KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) x/4gx LGPerson.class
0x100004410: 0x0000000100004438 0x0000000100354140
0x100004420: 0x000000010075e7f0 0x0002802800000003
(lldb) p/x 0x0000000100004438 & 0x00007ffffffffff8ULL
(unsigned long long) $1 = 0x0000000100004438
(lldb) po 0x0000000100004438
LGPerson

(lldb) p/x (class_data_bits_t *)0x0000000100004458
(class_data_bits_t *) $3 = 0x0000000100004458
(lldb) p $3->data()
(class_rw_t *) $4 = 0x000000010075e790
(lldb) p *$4
(class_rw_t) $5 = {
  flags = 2684878849
  witness = 0
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4311956033
    }
  }
  firstSubclass = nil
  nextSiblingClass = 0x00007fff8d81bcd8
}
(lldb) p $5.methods()
(const method_array_t) $6 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100004360
      }
      arrayAndFlag = 4294984544
    }
  }
}
(lldb) p $6.list
(const method_list_t_authed_ptr<method_list_t>) $7 = {
  ptr = 0x0000000100004360
}
(lldb) p $7.ptr
(method_list_t *const) $8 = 0x0000000100004360
(lldb) p *$8
(method_list_t) $9 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 1)
}
(lldb) p $9.get(0).big()
(method_t::big) $10 = {
  name = "say666"
  types = 0x0000000100003f77 "v16@0:8"
  imp = 0x0000000100003e10 (KCObjcBuild`+[LGPerson say666])
}
(lldb) 

对于class_rw_tclass_ro_t 、数据结构的变化看一下这篇文章Objective-C 运行时的改进之数据结构的变化

2.成员变量和属性以及编码

// 成员变量 vs 属性 VS 实例变量
@interface LGPerson : NSObject
{
    NSString *hobby; // 字符串
    int a;
    NSObject *objc;  // 结构体
}

@property (nonatomic, copy) NSString *nickName;
@property (atomic, copy) NSString *acnickName;
@property (nonatomic) NSString *nnickName;
@property (atomic) NSString *anickName;

@property (nonatomic, strong) NSString *name;
@property (atomic, strong) NSString *aname;
@end

@implementation LGPerson
@end

@interface LGTeacher : NSObject

@end

@implementation LGTeacher

@end
clang -rewrite-objc main.m -o main.cpp

将LGPerson转换为底层源码

extern "C" unsigned long OBJC_IVAR_$_LGPerson$_nickName;
extern "C" unsigned long OBJC_IVAR_$_LGPerson$_nnickName;
extern "C" unsigned long OBJC_IVAR_$_LGPerson$_anickName;
extern "C" unsigned long OBJC_IVAR_$_LGPerson$_name;
extern "C" unsigned long OBJC_IVAR_$_LGPerson$_aname;
struct LGPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *hobby;
    int a;
    NSObject *objc;
    NSString *_nickName;
    NSString *_acnickName;
    NSString *_nnickName;
    NSString *_anickName;
    NSString *_name;
    NSString *_aname;
};
// @property (nonatomic, copy) NSString *nickName;
// @property (atomic, copy) NSString *acnickName;
// @property (nonatomic) NSString *nnickName;
// @property (atomic) NSString *anickName;

上面可以看出:属性转化为:带下划线成员变量 + setter + getter ⽅法
其中setter与getter方法代码如下

截屏2021-06-20 下午9.42.02.png

发现属性setter方法有objc_setPropertyself + OBJC_IVAR_ 内存平移赋值两种实现方式?
后面来分析,现在先来看下苹果各种类型编码都代表什么意思

截屏2021-06-20 下午9.55.40.png
可以通过苹果的官方文档来查看:

例如:nickName", "@16@0:8"
1.@:id
2.16: 占用内存

  1. @:id
  2. 0:从0号位置开始
  3. ::SEL
  4. 8 :从8号位置开始

3.setter方法的底层原理

对objc_setProperty分析:

截屏2021-06-18 上午9.47.28.png
截屏2021-06-20 下午11.56.28.png

当我们属性为copy类型时候,会调用GetSetProperty的方法。

4.类方法存储的API介绍

未完待续...
上一篇 下一篇

猜你喜欢

热点阅读