class structure analysis

The objc source code in this article is objc4-838, and different source codes may vary from Apple's official website.


foreword

本文主要探究类的结构并分析类方法及ivar

One, isa points to

First customize a class SLPerson.

@interface SLPerson : NSObject
{
    NSString * subject;
}
@property(copy,nonatomic)NSString * name;
@property(copy,nonatomic)NSString * hobby;

-(void)sayNB;
+(void)say666;

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        SLPerson * p = [[SLPerson alloc];
        NSObject * obj = [NSObject alloc];
    }
    return 0;
}

 Use lldb to view the memory address and analyze it as shown in the figure below

 The isa of the SLPerson instance object p points to: p—>SLPerson class object—>SLPerson metaclass object—>NSObject metaclass object—>NSObject metaclass object (pointing to itself).

2. Summary of superclass inheritance relationship

1. There is no inheritance relationship between instance objects , only classes or metaclasses have inheritance relationships.

2. The NSObject metaclass inherits the NSObjct class , and the NSObjct class inherits from nil

3. isa pointing graph and superclass graph

4. Class Structure Analysis and Exploration

1.objc_object&objc_class source code

typedef struct objc_class *Class;
typedef struct objc_object *id;
struct objc_class : objc_object {
    .....

    // Class ISA;              //8字节 从objc_object继承而来
    Class superclass;          //8字节
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    ........

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

The class information is in class_data_bits_t , and the member memory in struct objc_class is continuous, so you can view the source code by adding the class address plus specific pointer memory translation . isa and superclass occupy 8 bytes, let's analyze the size of cache_t.

cache_t size

First look at the source code. Since the static variable does not occupy the storage space of this structure, we analyze the key code as follows:

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

    ......此处省略好多代码
}

It can be concluded that the size of the union is 8 bytes, plus the 8 bytes of _bucketsAndMaybeMask , the cache_t size is 16 bytes. Then we can get the first address of class_data_bits_t bits by adding 8+8+16=32 bytes (0x20) offset to the first address of the class.

2.class_data_bits_t

a.  Getthe first address of class_data_bits_t bits

(lldb) x/4gx SLPerson.class
0x100008388: 0x0000000100008360 0x000000010080e140
0x100008398: 0x0000000100806230 0x0000802800000000
(lldb) p (class_data_bits_t *)(0x100008388+0x20)
(class_data_bits_t *) $1 = 0x00000001000083a8

b. Get the address  class_rw_tof the type data

(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000100f047e0
(lldb) p *$2
(class_rw_t) $3 = {
  flags = 2148007936
  witness = 0
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4295000488
    }
  }
  firstSubclass = nil
  nextSiblingClass = 0x00007ff8480b19c8
}

c. View property listproperties

(lldb) p $3.properties()
(const property_array_t) $4 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008180
      }
      arrayAndFlag = 4295000448
    }
  }
}
(lldb) p $4.list
(const RawPtr<property_list_t>) $5 = {
  ptr = 0x0000000100008180
}
(lldb) p $5.ptr
(property_list_t *const) $6 = 0x0000000100008180
(lldb) p *$6
(property_list_t) $7 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
}
(lldb) p $7.get(0)
(property_t) $8 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb) p $7.get(1)
(property_t) $9 = (name = "hobby", attributes = "T@\"NSString\",C,N,V_hobby")

Summary: You can find the attributes name and hobby by calling properties, but you can't find the member variable subject.

d. View methods ()

(lldb) p $10.list.ptr
(method_list_t *const) $11 = 0x0000000100008098
(lldb) p *$11
(method_list_t) $12 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 5)
}
(lldb) p $12.get(0).big()
(method_t::big) $14 = {
  name = "sayNB"
  types = 0x0000000100003ede "v16@0:8"
  imp = 0x0000000100003be0 (KCObjcBuild`-[SLPerson sayNB])
}
(lldb) p $12.get(1).big()
(method_t::big) $15 = {
  name = "hobby"
  types = 0x0000000100003ee6 "@16@0:8"
  imp = 0x0000000100003c40 (KCObjcBuild`-[SLPerson hobby])
}
(lldb) p $12.get(2).big()
(method_t::big) $16 = {
  name = "setHobby:"
  types = 0x0000000100003eee "v24@0:8@16"
  imp = 0x0000000100003c60 (KCObjcBuild`-[SLPerson setHobby:])
}
(lldb) p $12.get(3).big()
(method_t::big) $17 = {
  name = "name"
  types = 0x0000000100003ee6 "@16@0:8"
  imp = 0x0000000100003bf0 (KCObjcBuild`-[SLPerson name])
}
(lldb) p $12.get(4).big()
(method_t::big) $18 = {
  name = "setName:"
  types = 0x0000000100003eee "v24@0:8@16"
  imp = 0x0000000100003c10 (KCObjcBuild`-[SLPerson setName:])
}

Summary: Calling methods()the printed method has no class methods, only instance methods .

3. View ivars

(lldb) x/4gx SLPerson.class
0x100008388: 0x0000000100008360 0x000000010080e140
0x100008398: 0x0000000100806230 0x0000802800000000
(lldb) p (class_data_bits_t *)(0x100008388+0x20)
(class_data_bits_t *) $1 = 0x00000001000083a8
(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000100f12330
(lldb) p *$2
(class_rw_t) $3 = {
  flags = 2148007936
  witness = 0
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4295000488
    }
  }
  firstSubclass = nil
  nextSiblingClass = 0x00007ff8480b19c8
}
(lldb) p $3.ro()
(const class_ro_t *) $4 = 0x00000001000081a8
(lldb) p *$4
(const class_ro_t) $5 = {
  flags = 0
  instanceStart = 8
  instanceSize = 32
  reserved = 0
   = {
    ivarLayout = 0x0000000000000000
    nonMetaclass = nil
  }
  name = {
    std::__1::atomic<const char *> = "SLPerson" {
      Value = 0x0000000100003e08 "SLPerson"
    }
  }
  baseMethods = {
    ptr = 0x0000000100008098
  }
  baseProtocols = nil
  ivars = 0x0000000100008118
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000100008180
  _swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $5.ivars
(const ivar_list_t *const) $6 = 0x0000000100008118
(lldb) p *$6
(const ivar_list_t) $7 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 3)
}
(lldb) p $7.get(0)
(ivar_t) $8 = {
  offset = 0x0000000100008348
  name = 0x0000000100003e45 "subject"
  type = 0x0000000100003ef9 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $7.get(1)
(ivar_t) $9 = {
  offset = 0x0000000100008350
  name = 0x0000000100003e4d "_name"
  type = 0x0000000100003ef9 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $7.get(2)
(ivar_t) $10 = {
  offset = 0x0000000100008358
  name = 0x0000000100003e53 "_hobby"
  type = 0x0000000100003ef9 "@\"NSString\""
  alignment_raw = 3
  size = 8
}

Summary: In ivar_list_t, attributes (name, hobby) will be underlined, and member variables (subject) will not be underlined.

4. Class method 

Summary: Class methods exist in metaclass objects

5. isKindOfClass and isMemberOfClass

1.isKindOfClass:

The source code is as follows:

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}

Test code:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];       //
        BOOL re2 = [(id)[SLPerson class] isKindOfClass:[SLPerson class]];       //
        BOOL re3 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];       //
        BOOL re4 = [(id)[SLPerson alloc] isKindOfClass:[SLPerson class]];       //
        NSLog(@"\n re1:%hhd re2:%hhd re3:%hhd re4:%hhd",re1,re2,re3,re4);
    }
    return 0;
}

 result:

  • re1——1. tcls = self->ISA() is the NSObject metaclass (that is, the root metaclass ), cls is [NSObject class] is the class ; 2. tcls is tcls->superclass, and the parent class of the root metaclass is the NSObject class . Equal to cls, returns YES.
  • re2——1. tcls = self->ISA() is the SLPerson metaclass , cls is [SLPerson class] is a class ; 2. tcls is tcls->superclass, and the parent class of the SLPerson metaclass is the root metaclass , which is not equal to cls; 3. The parent class of the root metaclass is the NSObject class , which is not equal to cls; 4. The parent class of the NSObject class is nil, the for loop terminates, and returns NO.
  • re3 - tcls = [self class] is the NSObject class , equal to cls, returns YES.
  • re4——tcls = [self class] is the SLPerson ​​​​​​​​class , equal to cls, returns YES.

2.isMemberOfClass

The source code is as follows:

+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

Test code:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        BOOL re5 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];     //
        BOOL re6 = [(id)[SLPerson class] isMemberOfClass:[SLPerson class]];     //
        BOOL re7 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];     //
        BOOL re8 = [(id)[SLPerson alloc] isMemberOfClass:[SLPerson class]];     //
        NSLog(@"\n re5:%hhd re6:%hhd re7:%hhd re8:%hhd",re5,re6,re7,re8);
    }
    return 0;
}

result

  • re5 - self->ISA()yes根元类,cls是NSObject,不相等返回NO。
  • re6——self->ISA()is the SLPerson metaclass , cls is the SLPerson class , and returns NO if they are not equal.
  • re7——self->ISA()是NSObject,等于cls,返回YES。
  • re8——self->ISA()是SLPerson,等于cls,返回YES。

Guess you like

Origin blog.csdn.net/weixin_38016552/article/details/125742455