类的结构分析

同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 存在于元类中

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,104评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,816评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,697评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,836评论 1 298
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,851评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,441评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,992评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,899评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,457评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,529评论 3 341
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,664评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,346评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,025评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,511评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,611评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,081评论 3 377
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,675评论 2 359