美文网首页
类的结构分析

类的结构分析

作者: 愚十二 | 来源:发表于2019-12-22 16:53 被阅读0次

同isa一样从源码入手,首先从源码中找到类Class的定义和结构

// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

// A pointer to an instance of a class.
typedef struct objc_object *id;

继续找objc_class

struct objc_class : objc_object {
    // Class ISA;    //隐性存在(继承而来)的isa 占8个字节
    Class superclass; //父类对象地址 占8个字节
    cache_t cache;      //16个字节       //  pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }
    //省略其他
 }    

cache_t

struct cache_t {
    //typedef uint32_t mask_t;
    struct bucket_t *_buckets;  //指针8个字节
    mask_t _mask;   //uint32_t  4个字节
    mask_t _occupied; //uint32_t  4个字节

public:
    struct bucket_t *buckets();
    mask_t mask();
    mask_t occupied();
    void incrementOccupied();
    void setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask);
    void initializeToEmpty();

    mask_t capacity();
    bool isConstantEmptyCache();
    bool canBeFreed();

    static size_t bytesForCapacity(uint32_t cap);
    static struct bucket_t * endMarker(struct bucket_t *b, uint32_t cap);

    void expand();
    void reallocate(mask_t oldCapacity, mask_t newCapacity);
    struct bucket_t * find(SEL sel, id receiver);

    static void bad_cache(id receiver, SEL sel, Class isa) __attribute__((noreturn));
};

class_rw_t

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;

#if SUPPORT_INDEXED_ISA
    uint32_t index;
#endif

    void setFlags(uint32_t set) 
    {
        OSAtomicOr32Barrier(set, &flags);
    }

    void clearFlags(uint32_t clear) 
    {
        OSAtomicXor32Barrier(clear, &flags);
    }

    // set and clear must not overlap
    void changeFlags(uint32_t set, uint32_t clear) 
    {
        assert((set & clear) == 0);

        uint32_t oldf, newf;
        do {
            oldf = flags;
            newf = (oldf | set) & ~clear;
        } while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
    }
};

找const class_ro_t *ro;

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    // This field exists only when RO_HAS_SWIFT_INITIALIZER is set.
    _objc_swiftMetadataInitializer __ptrauth_objc_method_list_imp _swiftMetadataInitializer_NEVER_USE[0];

    _objc_swiftMetadataInitializer swiftMetadataInitializer() const {
        if (flags & RO_HAS_SWIFT_INITIALIZER) {
            return _swiftMetadataInitializer_NEVER_USE[0];
        } else {
            return nil;
        }
    }

    method_list_t *baseMethods() const {
        return baseMethodList;
    }

    class_ro_t *duplicate() const {
        if (flags & RO_HAS_SWIFT_INITIALIZER) {
            size_t size = sizeof(*this) + sizeof(_swiftMetadataInitializer_NEVER_USE[0]);
            class_ro_t *ro = (class_ro_t *)memdup(this, size);
            ro->_swiftMetadataInitializer_NEVER_USE[0] = this->_swiftMetadataInitializer_NEVER_USE[0];
            return ro;
        } else {
            size_t size = sizeof(*this);
            class_ro_t *ro = (class_ro_t *)memdup(this, size);
            return ro;
        }
    }
};

通过lldb开始探索
首先定义Person如下

@interface Person : NSObject
{
    NSString *secondName;
}

@property (nonatomic, copy) NSString *firstName;

+ (void)walk;

- (void)fly;

1.查看Person类的内存

(lldb) x/4gx p.class
0x1000012d8: 0x001d8001000012b1 0x0000000100b36140
0x1000012e8: 0x0000000102d52a70 0x0000000300000003

2.通过内存偏移找到bits的内存地址,需要偏移的字节数有ISA 8个字节,superclass 8个字节, cache_t 16个字节,0x1000012d8 + 32 = 0x1000012f8

(lldb) p (class_data_bits_t *)0x1000012f8
(class_data_bits_t *) $1 = 0x00000001000012f8

3.找到class_rw_t

(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000102d52370
(lldb) p *$2
(class_rw_t) $3 = {
  flags = 2148139008
  version = 0
  ro = 0x00000001000011b0
  methods = {
    list_array_tt<method_t, method_list_t> = {
       = {
        list = 0x00000001000010e8
        arrayAndFlag = 4294971624
      }
    }
  }
  properties = {
    list_array_tt<property_t, property_list_t> = {
       = {
        list = 0x0000000100001198
        arrayAndFlag = 4294971800
      }
    }
  }
  protocols = {
    list_array_tt<unsigned long, protocol_list_t> = {
       = {
        list = 0x0000000000000000
        arrayAndFlag = 0
      }
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
  demangledName = 0x0000000000000000 <no value available>
}

5.找其中的ro

(lldb) p $3.ro
(const class_ro_t *) $4 = 0x00000001000011b0
(lldb) p *$4
(const class_ro_t) $5 = {
  flags = 388
  instanceStart = 8
  instanceSize = 24
  reserved = 0
  ivarLayout = 0x0000000100000efb "\x02"
  name = 0x0000000100000ef4 "Person"
  baseMethodList = 0x00000001000010e8
  baseProtocols = 0x0000000000000000
  ivars = 0x0000000100001150
  weakIvarLayout = 0x0000000000000000 <no value available>
  baseProperties = 0x0000000100001198
  _swiftMetadataInitializer_NEVER_USE = {}
}

6.找其中的baseProperties

(lldb) p $5.baseProperties
(property_list_t *const) $6 = 0x0000000100001198
(lldb) p *$6
(property_list_t) $7 = {
  entsize_list_tt<property_t, property_list_t, 0> = {
    entsizeAndFlags = 16
    count = 1
    first = (name = "firstName", attributes = "T@"NSString",C,N,V_firstName")
  }
}

7.查看成员变量ivars

(lldb) p $5.ivars
(const ivar_list_t *const) $8 = 0x0000000100001150
(lldb) p *$8
(const ivar_list_t) $9 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0> = {
    entsizeAndFlags = 32
    count = 2
    first = {
      offset = 0x00000001000012a8
      name = 0x0000000100000f35 "secondName"
      type = 0x0000000100000f71 "@"NSString""
      alignment_raw = 3
      size = 8
    }
  }
}

8.查看方法列表baseMethodList

(lldb) p $5.baseMethodList
(method_list_t *const) $10 = 0x00000001000010e8
(lldb) p *$10
(method_list_t) $11 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 4
    first = {
      name = "fly"
      types = 0x0000000100000f56 "v16@0:8"
      imp = 0x0000000100000d20 (objc-debug`-[Person fly] at Person.m:17)
    }
  }
}
(lldb) p $10.get(0)
(method_t) $12 = {
  name = "fly"
  types = 0x0000000100000f56 "v16@0:8"
  imp = 0x0000000100000d20 (objc-debug`-[Person fly] at Person.m:17)
}
  Fix-it applied, fixed expression was: 
    $10->get(0)
(lldb) p $10.get(1)
(method_t) $13 = {
  name = ".cxx_destruct"
  types = 0x0000000100000f56 "v16@0:8"
  imp = 0x0000000100000da0 (objc-debug`-[Person .cxx_destruct] at Person.m:11)
}
  Fix-it applied, fixed expression was: 
    $10->get(1)
(lldb) p $10.get(2)
(method_t) $14 = {
  name = "firstName"
  types = 0x0000000100000f5e "@16@0:8"
  imp = 0x0000000100000d30 (objc-debug`-[Person firstName] at Person.h:18)
}
  Fix-it applied, fixed expression was: 
    $10->get(2)
(lldb) p $10.get(3)
(method_t) $15 = {
  name = "setFirstName:"
  types = 0x0000000100000f66 "v24@0:8@16"
  imp = 0x0000000100000d60 (objc-debug`-[Person setFirstName:] at Person.h:18)
}
  Fix-it applied, fixed expression was: 
    $10->get(3)
(lldb) p $10.get(4)
Assertion failed: (i < count), function get, file /Users/tiltwang/Documents/runtime/objc-runtime-new.h, line 140.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.

并没有找到类方法walk,那么去元类中查找

(lldb) x/4gx p
0x102f70200: 0x001d8001000012fd 0x0000000000000000
0x102f70210: 0x0000000100001048 0x0000000000000000
(lldb) p/x 0x001d8001000012fd & 0x00007ffffffffff8ULL
(unsigned long long) $1 = 0x00000001000012f8
(lldb) x/4gx $1
0x1000012f8: 0x001d8001000012d1 0x0000000100b36140
0x100001308: 0x0000000102f748b0 0x0000000100000003
(lldb) p/x 0x001d8001000012d1 & 0x00007ffffffffff8ULL
(unsigned long long) $2 = 0x00000001000012d0
(lldb) x/4gx $2
0x1000012d0: 0x001d800100b360f1 0x0000000100b360f0
0x1000012e0: 0x0000000102f741f0 0x0000000100000003
(lldb) p/x 0x001d800100b360f1 & 0x00007ffffffffff8ULL
(unsigned long long) $3 = 0x0000000100b360f0
(lldb) po $3
NSObject

(lldb) p (class_data_bits_t *)0x1000012f0
(class_data_bits_t *) $4 = 0x00000001000012f0
(lldb) p $4->data()
(class_rw_t *) $5 = 0x0000000102f74150
(lldb) p *$5
(class_rw_t) $6 = {
  flags = 2685075456
  version = 7
  ro = 0x00000001000010c0
  methods = {
    list_array_tt<method_t, method_list_t> = {
       = {
        list = 0x00000001000010a0
        arrayAndFlag = 4294971552
      }
    }
  }
  properties = {
    list_array_tt<property_t, property_list_t> = {
       = {
        list = 0x0000000000000000
        arrayAndFlag = 0
      }
    }
  }
  protocols = {
    list_array_tt<unsigned long, protocol_list_t> = {
       = {
        list = 0x0000000000000000
        arrayAndFlag = 0
      }
    }
  }
  firstSubclass = nil
  nextSiblingClass = 0x00007fff917f99b0
  demangledName = 0x0000000000000000 <no value available>
}
(lldb) p $6.ro
(const class_ro_t *) $7 = 0x00000001000010c0
(lldb) p *$7
(const class_ro_t) $8 = {
  flags = 389
  instanceStart = 40
  instanceSize = 40
  reserved = 0
  ivarLayout = 0x0000000000000000 <no value available>
  name = 0x0000000100000ef4 "Person"
  baseMethodList = 0x00000001000010a0
  baseProtocols = 0x0000000000000000
  ivars = 0x0000000000000000
  weakIvarLayout = 0x0000000000000000 <no value available>
  baseProperties = 0x0000000000000000
  _swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $8.baseMethodList
(method_list_t *const) $9 = 0x00000001000010a0
(lldb) p *$9
(method_list_t) $10 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 1
    first = {
      name = "walk"
      types = 0x0000000100000f51 "v16@0:8"
      imp = 0x0000000100000d00 (objc-debug`+[Person walk] at Person.m:13)
    }
  }
}

至此找到类方法walk 存在于元类中

相关文章

  • iOS 类原理探索:类的结构分析

    OC 类原理探索 系列文章 OC 类原理探索:类的结构分析 OC 类原理探索:类结构分析补充[https://ju...

  • 多线程基础(十三):java中的FutureTask

    [toc] FutureTask源码分析 1.类结构及常量、变量 1.1 类结构 FutureTask类结构如下:...

  • iOS类结构:cache_t分析

    一、cache_t 内部结构分析 1.1 在iOS类的结构分析中,我们已经分析过类(Class)的本质是一个结构体...

  • 类,类结构分析

    忙不是不学习的借口 在isa和类的关联[https://www.jianshu.com/p/079a6ad90f1...

  • iOS-OC底层04:类结构分析

    类结构分析 通过lldb来分析类结构 查看objc2的内存情况 类对象只有一份,isa对象-> 类(LGPerso...

  • iOS-底层分析之类的结构分析

    类的结构分析 本文主要分析iOS中的类以及类的结构,下面我们通过一个例子来探索类的结构 我们定义一个WPerson...

  • 类结构分析

    这片文章主要分析的是类的结构以及对象-类-元类-根元类之间的走位. 一. isa的指向以及类之间的关系 准备工作定...

  • 类结构分析

    类结构分析 回顾 前面我们讲了alloc 流程中对象的创建过程,下面我们来探索一下类的结构,废话不多说,开始~ 类...

  • 类结构分析

    开发中经常创建一个 TestClass.h 和 TestClass.m 文件,而这个 TestClass 就是我们...

  • 类的结构分析

    神图镇楼,相信做过iOS开发的同学一定非常熟悉这张经典图,每次看这张图都有不一样的体会,今天我们就借这张图,引出我...

网友评论

      本文标题:类的结构分析

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