美文网首页
OC底层5-类的探究分析(下)

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

作者: JEFF009 | 来源:发表于2021-06-21 11:19 被阅读0次

    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介绍

    未完待续...
    

    相关文章

      网友评论

          本文标题:OC底层5-类的探究分析(下)

          本文链接:https://www.haomeiwen.com/subject/bildyltx.html