类的方法属性探究上

今天来探索一下类的成员方法和成员属性以及类方法都存放在哪里

Class的本质

首先先来补充一下类的本质Class,通过源码知道

typedef struct objc_class *Class;
Class 实际就是一个结构体指针

// 将结构体里面的方法,静态方法都去掉,简化如下struct objc_class
struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
}

struct objc_object {
private:
    isa_t isa
};

struct objc_class 继承了objc_object, 所以objc_class 也有isa指针,
接下来我们要研究的是bits这个属性,为什么要研究这个bits呢,还有怎么去拿到bits呢?

Bits的获取

为什么要研究这个bits呢?
还记得之前研究对象申请空间大小是看到过这样一段代码吗?这两个函数就是在struct objc_class 的成员函数,这样我们就可以顺藤摸瓜成员变量应该就在这个bits里面,方法可能也在里面

uint32_t unalignedInstanceSize() const {
        ASSERT(isRealized());
        return data()->ro()->instanceSize; // 这边返回的是对象所有成员变量的大小
}

class_rw_t *data() const {
        return bits.data();
}

怎么去拿到bits呢?
首先我们想到的是通过指针平移来获取,如果平移的话我们就要先确定需要平移多少位,在bits前面有isa,superclass,cache,isa 8字节,superclass 8字节,cache?
我们来看看cache结构体

struct cache_t {
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask;
    union {
        struct {
            explicit_atomic<mask_t>    _maybeMask;
#if __LP64__
            uint16_t                   _flags;
#endif
            uint16_t                   _occupied;
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache;
    };
}

实际的cache_t结构体非常庞大,但是呢大部分都是方法和静态方法,所以我把他去掉只剩上面,由此可以看出cache_t 的大小只有16字节也就是我们需要平移32字节
接下来我们通过lldb来研究查看

@interface HFObject : NSObject
{
    NSString *_a;
    NSString *_b;
}

@property (nonatomic,strong) NSString *name;

@property (nonatomic,strong) NSString *nickName;

- (void)hello;

+ (void)world;

@end

HFObject *p = [HFObject alloc];
NSLog(@"%@",p);

(lldb) x/4gx HFObject.class
0x100004650: 0x0000000100004628 0x0000000100369140
0x100004660: 0x00000001003603c0 0x0000803000000000
打印HFObject的类地址,我们已知需要便宜32字节也就是0x20到bits位置
(lldb) p/x 0x100004650+32
(long) $2 = 0x0000000100004670
这样似乎看不出什么来
(lldb) p (class_data_bits_t *)0x0000000100004670
(class_data_bits_t *) $3 = 0x0000000100004670
这样就得到了class_data_bits_t 指针
(lldb) p $3->data()
(class_rw_t *) $4 = 0x0000000101034140
我们进入到class_rw_t 结构体里惊奇的发现里面有几个函数
  const method_array_t methods() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
        }
    }

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

    const protocol_array_t protocols() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
        } else {
            return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
        }
    }

methods会不会是方法列表呢?
properties 会不会是属性呢?
protocols 会不会是协议呢?
一个个来探索一下
(lldb) p $4->properties()
(const property_array_t) $8 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x00000001000043c0
      }
      arrayAndFlag = 4294984640
    }
  }
}
(lldb) p $8.list.ptr
(property_list_t *const) $9 = 0x00000001000043c0
(lldb) p *$9
(property_list_t) $10 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
}
(lldb) p $10.get(0)
(property_t) $11 = (name = "name", attributes = "T@\"NSString\",&,N,V_name")
(lldb) p $10.get(1)
(property_t) $12 = (name = "nickName", attributes = "T@\"NSString\",&,N,V_nickName")
这边我们发现property存放的是成员属性,但是成员变量不再里面
继续探究methods方法
(lldb) p $4->methods()
(const method_array_t) $5 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x00000001000042b8
      }
      arrayAndFlag = 4294984376
    }
  }
}
(lldb) p $5.list.ptr
(method_list_t *const) $6 = 0x00000001000042b8
(lldb) p *$6
(method_list_t) $7 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 5)
}
(lldb) p $7.get(0).big()
(method_t::big) $14 = {
  name = "hello"
  types = 0x0000000100003f7e "v16@0:8"
  imp = 0x0000000100003c30 (KCObjcBuild`-[HFObject hello])
}
果然没错,methods存放的确实是成员方法,但是类方法却不再里面
之前我们计算对象大小时是用的data()->ro()->instanceSize;
接下来我们看看ro()
(lldb) p $4->ro()
(const class_ro_t *) $17 = 0x0000000100004270
(lldb) p $17->ivars
(const ivar_list_t *const) $18 = 0x0000000100004338
(lldb) p *$18
(const ivar_list_t) $19 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 4)
}
(lldb) p $19.get(0)
(ivar_t) $20 = {
  offset = 0x00000001000045a8
  name = 0x0000000100003f1d "_a"
  type = 0x0000000100003f6a "@\"NSString\""
  alignment_raw = 3
  size = 8
}
果然ro里面存放着所有的成员变量

还有一个问题,类方法放在那里呢?,因为我们在这边methods里面并没有看到,对象的方法属性放在类里面,那类的方法是不是放在元类里面呢?
继续lldb看看

(lldb) x/4gx HFObject.class
0x100004650: 0x0000000100004628 0x0000000100369140
0x100004660: 0x00000001003603c0 0x0000803000000000
(lldb) p (class_data_bits_t *)0x0000000100004648
(class_data_bits_t *) $25 = 0x0000000100004648
(lldb) p $25->data()
(class_rw_t *) $26 = 0x0000000101034120
(lldb) p $26->methods()
(const method_array_t) $27 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100004250
      }
      arrayAndFlag = 4294984272
    }
  }
}
(lldb) p $27.list.ptr
(method_list_t *const) $28 = 0x0000000100004250
(lldb) p *$28
(method_list_t) $29 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 1)
}
(lldb) p $29.get(0).big()
(method_t::big) $30 = {
  name = "world"
  types = 0x0000000100003f7e "v16@0:8"
  imp = 0x0000000100003c20 (KCObjcBuild`+[HFObject world])
}
果然在元类里面
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容