OC – 方法的本质(上)

前言

通过之前的文章我们对 isa 已经有了初步的了解和认知。如果还有不了解的同学可以通过 这篇文章 了解一下。接下来我们进入今天的正文。

一、类的分析 — ISA

首页我们创建下面这份代码,然后对 TPerson 进行分析。

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
        // 0x00007ffffffffff8
        TPerson * tp = [TPerson alloc];
        NSLog(@" -- %@ -- ", tp);
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
我们通过控制台输出:
(lldb) p/x tp   // 对 tp 进行探索
(TPerson *) $0 = 0x00006000029440a0   // 得到当前 tp 的指针地址
(lldb) x/4gx 0x00006000029440a0
0x6000029440a0: 0x0000000102ec4570 0x0000000000000000
0x6000029440b0: 0x0000006568636163 0x000000000000000d
// 这里就能得到当前对象的 isa :0x000000010aba3570
(lldb) p/x 0x0000000102ec4570 & 0x00007ffffffffff8   // 通过 isa 获取当前的类
(long) $1 = 0x0000000102ec4570
(lldb) po 0x0000000102ec4570
TPerson
// 输出 0x0000000102ec4570 就得到了 TPerson

// 我们继续探索 TPerson 类的地址,发现它也有和 TPerson 一样的内存结构
(lldb) x/4gx 0x0000000102ec4570
0x102ec4570: 0x0000000102ec4548 0x00007fff80030660
0x102ec4580: 0x00007fff20195cd0 0x0000801000000000
(lldb) p/x 0x0000000102ec4548 & 0x00007ffffffffff8
(long) $4 = 0x0000000102ec4548
(lldb) po 0x0000000102ec4548
TPerson
// 输出 0x0000000102ec4548 ,发现也得到了 TPerson
(lldb) 
复制代码

通过上面这段代码和输出,发现 0x0000000102ec45700x0000000102ec4548 都输出了 TPerson 。难道 对象 一样,可以 无限开辟 在内存中 不止一个类
我们通过下面这段代码进行验证 类对象

Class class1 = [TPerson class];
Class class2 = [TPerson alloc].class;
Class class3 = object_getClass([TPerson alloc]);
Class class4 = [TPerson alloc].class;
NSLog(@"\n-- class1: %p\n-- class2: %p\n-- class3: %p\n-- clsss4: %p",class1,class2,class3,class4);

输出如下:
-- class1: 0x102ec4570
-- class2: 0x102ec4570
-- class3: 0x102ec4570
-- clsss4: 0x102ec4570
复制代码

根据打印结果可以得知,只有 0x0000000102ec4570 才是我们的类 TPerson0x0000000102ec4548 不是我们的类,那么它是什么呢?会不会是 NSObject 呢?通过控制台输出验证。

(lldb) p/x NSObject.class
(Class) $5 = 0x00007fff80030660 NSObject
复制代码

发现它也不是我们的 NSObject 类,那么它到底是什么呢?我们通过对程序的二进制文件进行分析,这里需要一个分析工具 烂苹果。[下载地址](链接: pan.baidu.com/s/1nRvNHX-D… 密码: 8pes)

image.png

可以看到在整个编译的可以执行文件中多了有个 _OBJC_METCLASS_$_TPerson 叫做 元类 ,是 系统或者 编译器 帮我们生成好的。可以得出 对象 isa -> 类 isa -> 元类

二、isa 走位图和继承链

1. 走位图

上面我已经知道了 元类。而且还得出了 对象 isa -> 类 isa -> 元类 那么我们可不可以类推一下呢?

对象 isa -> 类 isa -> 元类 isa -> 根元类

根类 isa -> 根元类 isa -> 根元类
就得到了这张 isa 走位图

image.png

2. 继承链

那么什么是 继承链 呢?通过下面代码来一探究竟。

// NSObject 元类链
// NSObject 实例对象
NSObject *object1 = [NSObject alloc];
// NSObject 类
Class class = object_getClass(object1);
// NSObject 元类
Class metaClass = object_getClass(class);
// NSObject 根元类
Class rootMetaClass = object_getClass(metaClass);
// NSObject 根根元类
Class rootRootMetaClass = object_getClass(rootMetaClass);
NSLog(@"\n%p 实例对象\n%p 类\n%p 元类\n%p 根元类\n%p 根根元类",object1,class,metaClass,rootMetaClass,rootRootMetaClass);

输出:
0x600000378060 实例对象
0x7fff80030660 类
0x7fff80030638 元类
0x7fff80030638 根元类
0x7fff80030638 根根元类

        
// TPerson元类
Class pMetaClass = object_getClass(TPerson.class);
Class psuperClass = class_getSuperclass(pMetaClass);
NSLog(@"\n%@ - %p",psuperClass,psuperClass);

输出:
NSObject - 0x7fff80030638

// TTeacher -> TPerson -> NSObject
// 元类也有一条继承链
Class tMetaClass = object_getClass(TTeacher.class);
Class tsuperClass = class_getSuperclass(tMetaClass);
NSLog(@"%@ - %p",tsuperClass,tsuperClass);

输出:
TPerson - 0x1005a35f0
        
// NSObject 根类特殊情况
Class nsuperClass = class_getSuperclass(NSObject.class);
NSLog(@"%@ - %p",nsuperClass,nsuperClass);

输出: 
(null) - 0x0

// 根元类 -> NSObject
Class rnsuperClass = class_getSuperclass(metaClass);
NSLog(@"%@ - %p",rnsuperClass,rnsuperClass);

输出:
NSObject - 0x7fff80030660

复制代码

根据上述代码,我们可以得出下图:

image.png

三、类的结构

根据上文可以知道 也有 内存,也能存储值。 的本质是 objc_class,接下来我们一起探索一下 objc 的源码。 苹果objc源码objc源码编译

1. objc_class 声明

在源码中搜索 objc_class ,发现它有两个声明。

声明一
struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

声明二
struct objc_class : objc_object {
  objc_class(const objc_class&) = delete;
  objc_class(objc_class&&) = delete;
  void operator=(const objc_class&) = delete;
  void operator=(objc_class&&) = delete;
    // 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

    Class getSuperclass() const {
#if __has_feature(ptrauth_calls)
#   if ISA_SIGNING_AUTH_MODE == ISA_SIGNING_AUTH
        if (superclass == Nil)
            return Nil;

#if SUPERCLASS_SIGNING_TREAT_UNSIGNED_AS_NIL
        void *stripped = ptrauth_strip((void *)superclass, ISA_SIGNING_KEY);
        if ((void *)superclass == stripped) {
            void *resigned = ptrauth_sign_unauthenticated(stripped, ISA_SIGNING_KEY, ptrauth_blend_discriminator(&superclass, ISA_SIGNING_DISCRIMINATOR_CLASS_SUPERCLASS));
            if ((void *)superclass != resigned)
                return Nil;
        }
#endif
            
        void *result = ptrauth_auth_data((void *)superclass, ISA_SIGNING_KEY, ptrauth_blend_discriminator(&superclass, ISA_SIGNING_DISCRIMINATOR_CLASS_SUPERCLASS));
        return (Class)result;

#   else
        return (Class)ptrauth_strip((void *)superclass, ISA_SIGNING_KEY);
#   endif
#else
        return superclass;
#endif
    }

    void setSuperclass(Class newSuperclass) {
#if ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_ALL
        superclass = (Class)ptrauth_sign_unauthenticated((void *)newSuperclass, ISA_SIGNING_KEY, ptrauth_blend_discriminator(&superclass, ISA_SIGNING_DISCRIMINATOR_CLASS_SUPERCLASS));
#else
        superclass = newSuperclass;
#endif
    }
    ... 由于代码过多截取部分,如需了解移步源码中查看。
}
复制代码

发现 声明一OBJC2_UNAVAILABLE ,无法使用的。所以我们就探索 声明二objc_class 是一个继承objc_object结构体 的结构是 Class ISAClass superclasscache_t cacheclass_data_bits_t bits 这些。我们现在已知 objc_class 这个类,但是我们要如何去获取 内存数据 (cachebits) 呢?

2. 如何获取 bits 的数据

我们先来探究如何获取 内存数据 bits

@interface TPerson : NSObject {
    NSString * desc;
}
@property (nonatomic, copy) NSString * name;
@property (nonatomic, copy) NSString * nickName;

- (void)formatPerson;

+ (void)personNickName;

@end
复制代码
#import "TPerson.h"

@implementation TPerson

- (instancetype)init {
    self = [super init];
    if (self) {
        self.name = @"小名";
    }
    return self;
}
- (void)formatPerson {
}
+ (void)personNickName {   
}
@end
复制代码
(lldb) x/4gx TPerson.class
0x100008600: 0x0000000100008628 0x000000010036a140
0x100008610: 0x00000001007519a0 0x0002802c00000003
(lldb)  po 0x000000010036a140
NSObject

// 我们对 `TPerson.class` 进行探究,得到 '0x0000000100008628' -> isa , '0x000000010036a140' -> superclass ,即为 'NSObject'。
// 验证一下 superclass 是否就是 'NSObject' ?  
(lldb) p/x NSObject.class
(Class) $2 = 0x000000010036a140 NSObject
(lldb) 
复制代码

我们对 TPerson.class 进行探究得到 0x0000000100008628 : isa0x000000010036a140 : superclass,以此类推 0x00000001007519a0 : cache0x0002802c00000003 : bits

3. cache_t 探索

接下来我们看 objc_class声明二 源码。

image.png
从首地址平移到 bits 需要平移多少个字节呢?我们知道 isa 8字节、superclass 8字节,那么 cache 多少字节呢?那么我们接下来就一起探索 cache_t 的源码,看看 cache_t 占多少个字节。

struct cache_t {
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask;    // 8
    union {
        struct {
            explicit_atomic<mask_t>    _maybeMask;      // 4
#if __LP64__
            uint16_t                   _flags;          // 2
#endif
            uint16_t                   _occupied;       // 2
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache;  // 8
    };
    ... 由于 【方法、函数、全局区】 都不在结构体存储区域得到了如上代码,如需了解移步源码中查看。
}
复制代码

得到 cache_t 的大小就等于 uintptr_t + union 的大小。uintptr_t8字节,union8 字节,所以 cache_t16 字节。

这里我们就得到了平移大小 : isa8) + superclass8) + cache_t16)= 32

(lldb) x/4gx TPerson.class
0x100008600: 0x0000000100008628 0x000000010036a140
0x100008610: 0x00000001007519a0 0x0002802c00000003

(lldb) p/x 0x100008600+0x20     // 0x100008600 平移 32 位
(long) $9 = 0x0000000100008620  
(lldb) p (class_data_bits_t *)0x0000000100008620  // 还原得到了 class_data_bits_t
(class_data_bits_t *) $10 = 0x0000000100008620
(lldb) p $10->data()    // 调用 data() 就得到了 class_rw_t
(class_rw_t *) $11 = 0x0000000101525af0
(lldb) p *$11
(class_rw_t) $12 = {
  flags = 2148007936
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4295000856
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
}
(lldb) 
复制代码

接下来我们继续查看 class_rw_t 源码。

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint16_t witness;
#if SUPPORT_INDEXED_ISA
    uint16_t index;
#endif

    explicit_atomic<uintptr_t> ro_or_rw_ext;

    Class firstSubclass;
    Class nextSiblingClass;

private:
    using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t, class_rw_ext_t, PTRAUTH_STR("class_ro_t"), PTRAUTH_STR("class_rw_ext_t")>;

    const ro_or_rw_ext_t get_ro_or_rwe() const {
        return ro_or_rw_ext_t{ro_or_rw_ext};
    }

    void set_ro_or_rwe(const class_ro_t *ro) {
        ro_or_rw_ext_t{ro, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_relaxed);
    }

    void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) {
        // the release barrier is so that the class_rw_ext_t::ro initialization
        // is visible to lockless readers
        rwe->ro = ro;
        ro_or_rw_ext_t{rwe, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_release);
    }

    class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deep = false);

public:
    void setFlags(uint32_t set)
    {
        __c11_atomic_fetch_or((_Atomic(uint32_t) *)&flags, set, __ATOMIC_RELAXED);
    }

    void clearFlags(uint32_t clear) 
    {
        __c11_atomic_fetch_and((_Atomic(uint32_t) *)&flags, ~clear, __ATOMIC_RELAXED);
    }

    // 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));
    }

    class_rw_ext_t *ext() const {
        return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
    }

    class_rw_ext_t *extAllocIfNeeded() {
        auto v = get_ro_or_rwe();
        if (fastpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
        } else {
            return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
        }
    }

    class_rw_ext_t *deepCopy(const class_ro_t *ro) {
        return extAlloc(ro, true);
    }

    const class_ro_t *ro() const {
        auto v = get_ro_or_rwe();
        if (slowpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
        }
        return v.get<const class_ro_t *>(&ro_or_rw_ext);
    }

    void set_ro(const class_ro_t *ro) {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro = ro;
        } else {
            set_ro_or_rwe(ro);
        }
    }

    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};
        }
    }
}
复制代码

class_rw_t 源码中我们知道了分别存储 properties()methods()protocols() 等。

4. properties() 探索

我们先来探索 properties()

(class_rw_t *) $11 = 0x0000000101525af0
(lldb) p *$11
(class_rw_t) $12 = {
  flags = 2148007936
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4295000856
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
}
(lldb) p $11->properties()
(const property_array_t) $13 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008478
      }
      arrayAndFlag = 4295001208
    }
  }
}
复制代码

这里我们就得到了 property_array_t,我们继续查看 property_array_t 的源码。

class property_array_t : 
    public list_array_tt<property_t, property_list_t, RawPtr>
{
    typedef list_array_tt<property_t, property_list_t, RawPtr> Super;

 public:
    property_array_t() : Super() { }
    property_array_t(property_list_t *l) : Super(l) { }
};
复制代码

继续查看 list_array_tt

class list_array_tt {
    struct array_t {
        uint32_t count;
        Ptr<List> lists[0];

        static size_t byteSize(uint32_t count) {
            return sizeof(array_t) + count*sizeof(lists[0]);
        }
        size_t byteSize() {
            return byteSize(count);
        }
    };

 protected:
    class iterator {
        const Ptr<List> *lists;
        const Ptr<List> *listsEnd;
        typename List::iterator m, mEnd;

     public:
        iterator(const Ptr<List> *begin, const Ptr<List> *end)
            : lists(begin), listsEnd(end)
        {
            if (begin != end) {
                m = (*begin)->begin();
                mEnd = (*begin)->end();
            }
        }

        const Element& operator * () const {
            return *m;
        }
        Element& operator * () {
            return *m;
        }

        bool operator != (const iterator& rhs) const {
            if (lists != rhs.lists) return true;
            if (lists == listsEnd) return false;  // m is undefined
            if (m != rhs.m) return true;
            return false;
        }

        const iterator& operator ++ () {
            ASSERT(m != mEnd);
            m++;
            if (m == mEnd) {
                ASSERT(lists != listsEnd);
                lists++;
                if (lists != listsEnd) {
                    m = (*lists)->begin();
                    mEnd = (*lists)->end();
                }
            }
            return *this;
        }
    };
    // ....
}
复制代码

发现这里有一个 iterator 迭代器方法,所以的 property 都是存放在这里,并且可以取出响应的元素。

(lldb) p $11->properties()
(const property_array_t) $14 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008478
      }
      arrayAndFlag = 4295001208
    }
  }
}
(lldb) p $14.list
(const RawPtr<property_list_t>) $15 = {
  ptr = 0x0000000100008478
}
(lldb) p $15.ptr   // 获取 ptr
(property_list_t *const) $16 = 0x0000000100008478
(lldb) p *$16   // 还原数据
(property_list_t) $17 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
}
(lldb) p $17.get(0)
(property_t) $18 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb) p $17.get(1)
(property_t) $19 = (name = "nickName", attributes = "T@\"NSString\",C,N,V_nickName")
(lldb) p $17.get(2)
Assertion failed: (i < count), function get, file ***/objc4_debug-master/objc4-818.2/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.
复制代码

通过还原的 property_list_t $17 调用 get(0)get(1) 方法就得到了 TPerson 中定义的属性。

image.png

调用 get(2) 时,出现了如下错误,数组越界了!

Assertion failed: (i < count), function get, file ***/objc4_debug-master/objc4-818.2/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.
复制代码

那么我们在 TPerson 中定义的方法怎么获取呢?我们已经知道了如果获取 property,那么我们就按照获取 property 的方法,继续探索 methods()

5. methods() 探索

(lldb) p $11->methods()
(const method_array_t) $28 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100008360
      }
      arrayAndFlag = 4295000928
    }
  }
}
(lldb) p $28.list
(const method_list_t_authed_ptr<method_list_t>) $29 = {
  ptr = 0x0000000100008360
}
(lldb) p $29.ptr
(method_list_t *const) $30 = 0x0000000100008360
(lldb) p *$30
(method_list_t) $31 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 7)
}
(lldb) p $31.get(0)
(method_t) $32 = {}
(lldb) 
复制代码

这里我们就得到了 method_list_t,但是我们这里通过 get(0)get(1)get(2)get(3) 都无法取到值呢?难道 method 不能像 property 一样,通过 get() 取值么?我们一起来看一下 method_array_t 的源码。

class method_array_t : 
    public list_array_tt<method_t, method_list_t, method_list_t_authed_ptr>
{
    typedef list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> Super;

 public:
    method_array_t() : Super() { }
    method_array_t(method_list_t *l) : Super(l) { }

    const method_list_t_authed_ptr<method_list_t> *beginCategoryMethodLists() const {
        return beginLists();
    }
    
    const method_list_t_authed_ptr<method_list_t> *endCategoryMethodLists(Class cls) const;
};
复制代码

6. methods() 与 properties() 的区别

通过对比 method_array_tproperty_array_t ,发现它们的代码都是一样的。那么为什么不行呢?

通过对代码进行对比,发现 property_list_t 方法没有实现,而 method_list_t 方法有实现!
property_list_t 方法:

struct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {
};
复制代码

method_list_t 方法:

struct method_list_t : entsize_list_tt<method_t, method_list_t, 0xffff0003, method_t::pointer_modifier> {
    bool isUniqued() const;
    bool isFixedUp() const;
    void setFixedUp();

    uint32_t indexOfMethod(const method_t *meth) const {
        uint32_t i = 
            (uint32_t)(((uintptr_t)meth - (uintptr_t)this) / entsize());
        ASSERT(i < count);
        return i;
    }

    bool isSmallList() const {
        return flags() & method_t::smallMethodListFlag;
    }

    bool isExpectedSize() const {
        if (isSmallList())
            return entsize() == method_t::smallSize;
        else
            return entsize() == method_t::bigSize;
    }

    method_list_t *duplicate() const {
        method_list_t *dup;
        if (isSmallList()) {
            dup = (method_list_t *)calloc(byteSize(method_t::bigSize, count), 1);
            dup->entsizeAndFlags = method_t::bigSize;
        } else {
            dup = (method_list_t *)calloc(this->byteSize(), 1);
            dup->entsizeAndFlags = this->entsizeAndFlags;
        }
        dup->count = this->count;
        std::copy(begin(), end(), dup->begin());
        return dup;
    }
}
复制代码

这里有一个点: 我们获取 property_list_t 的时候获取的是 property_t 结构体直接输出的是 nameattributes

struct property_t {
    const char *name;
    const char *attributes;
};
复制代码

而再看 method_t 结构体它里面没有成员变量,所以输出的是空{}

struct method_t {
    static const uint32_t smallMethodListFlag = 0x80000000;

    method_t(const method_t &other) = delete;

    // The representation of a "big" method. This is the traditional
    // representation of three pointers storing the selector, types
    // and implementation.
    struct big {
        SEL name;
        const char *types;
        MethodListIMP imp;
    };
    // ....
}
复制代码

7. methods()结构体的结构体中取值

这里我们发现在 method_t 中有一个 big 结构体,它里面有 nametypesimp,那么我们就可以先获取 method_t 再获取 big。那么 结构体-> 结构体 怎么获取呢?method_t 中提供了方法

big &big() const {
    ASSERT(!isSmall());
    return *(struct big *)this;
}
复制代码

这样我们就能获取到 TPerson 中的 对象方法。那么 TPerson 中的 类方法 存储在哪里呢?

(lldb) p $31.get(0).big()
(method_t::big) $33 = {
  name = "formatPerson"
  types = 0x0000000100003f84 "v16@0:8"
  imp = 0x0000000100003c50 (KCObjcBuild`-[TPerson formatPerson])
}
(lldb) p $31.get(1).big()
(method_t::big) $34 = {
  name = "init"
  types = 0x0000000100003f5e "@16@0:8"
  imp = 0x0000000100003bc0 (KCObjcBuild`-[TPerson init])
}
(lldb) p $31.get(2).big()
(method_t::big) $35 = {
  name = "name"
  types = 0x0000000100003f5e "@16@0:8"
  imp = 0x0000000100003c60 (KCObjcBuild`-[TPerson name])
}
(lldb) p $31.get(3).big()
(method_t::big) $36 = {
  name = ".cxx_destruct"
  types = 0x0000000100003f84 "v16@0:8"
  imp = 0x0000000100003d20 (KCObjcBuild`-[TPerson .cxx_destruct])
}
(lldb) p $31.get(4).big()
(method_t::big) $37 = {
  name = "setName:"
  types = 0x0000000100003f66 "v24@0:8@16"
  imp = 0x0000000100003c90 (KCObjcBuild`-[TPerson setName:])
}
(lldb) p $31.get(5).big()
(method_t::big) $38 = {
  name = "nickName"
  types = 0x0000000100003f5e "@16@0:8"
  imp = 0x0000000100003cc0 (KCObjcBuild`-[TPerson nickName])
}
(lldb) p $31.get(6).big()
(method_t::big) $39 = {
  name = "setNickName:"
  types = 0x0000000100003f66 "v24@0:8@16"
  imp = 0x0000000100003cf0 (KCObjcBuild`-[TPerson setNickName:])
}
复制代码

这篇文章我们已经探索了 properties()methods()

这里留了两个问题:
问题一:TPerson 中的 ivar 存储在哪里呢?
问题二:TPerson 中的 类方法 存储在哪里呢?
下篇文章我们继续补充。

补充: 内存偏移

我们通过下面这几段代码,对 内存偏移 进行了解。

// 普通指针
int a = 10;
int b = 10;
NSLog(@"-- %d -- %p", a, &a);
NSLog(@"-- %d -- %p", b, &b);

输出:
-- 10 -- 0x7ffeefbff430
-- 10 -- 0x7ffeefbff434
复制代码

在内存中某一个区域里,有指针a指针b,它们都同时指向一个数据 值 = 10,意味着这个数据可以被任何一个指针所使用。按照这个理论就得出: 普通指针是值拷贝

image.png

// 对象指针
TPerson * tp1 = [TPerson alloc];
TPerson * tp2 = [TPerson alloc];
NSLog(@"-- %@ -- %p",tp1, &tp1);
NSLog(@"-- %@ -- %p",tp2, &tp2);

输出:
-- <TPerson: 0x100735110> -- 0x7ffeefbff420
-- <TPerson: 0x100736060> -- 0x7ffeefbff428
复制代码

tp1tp2 指向的地址不同,空间也不同。&tp -> tp -> [TPerson alloc] 就得出一个结论:对象指针是指针拷贝

image.png

// 数组指针
int c[4] = {1, 2, 3, 4};
int *d = c;
NSLog(@"-- %p-- %p-- %p", &c, &c[0], &c[1]);
NSLog(@"-- %p-- %p-- %p", d, d+1, d+2);

输出:
-- 0x7ffeefbff420-- 0x7ffeefbff420-- 0x7ffeefbff424
-- 0x7ffeefbff420-- 0x7ffeefbff424-- 0x7ffeefbff428
复制代码

发现 &c&c[0] 地址相同,是因为 数组的首地址就等于第一个元素的地址0x7ffeefbff4200x7ffeefbff424 相差 4个字节( int 占用 4个字节 )。d 就是当前 c 的指针,d+1 就是将当前 c 的指针 平移 1个

我们通过下面这段代码进行验证:

// 数组指针
int c[4] = {1, 2, 3, 4};
int *d = c;
        
for (int i = 0; i < 4; i++) {
    int num = *(d+i);
    NSLog(@"-- num : %d", num);
}

输出:
-- num : 1
-- num : 2
-- num : 3
-- num : 4
复制代码

得出一个结论:内存偏移可以根据 首地址 + 偏移值,得到相应变量的地址。
image.png

后续继续补充。。。

Tips: 如若有理解错误或者不对的地方欢迎一起探索和交流!!!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享