Class

Theory of Class

Three points

  • canAllocFast()
  • nonpointer isa
  • what time does the system create the class or meta class

When explore the alloc, we can seecanAllocFast() here, but why each time it escape it:

static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
    if (slowpath(checkNil && !cls)) return nil;

#if __OBJC2__
    if (fastpath(!cls->ISA()->hasCustomAWZ())) {
        // No alloc/allocWithZone implementation. Go straight to the allocator.
        // fixme store hasCustomAWZ in the non-meta class and 
        // add it to canAllocFast's summary
        if (fastpath(cls->canAllocFast())) {
            // No ctors, raw isa, etc. Go straight to the metal.
            bool dtor = cls->hasCxxDtor();
            id obj = (id)calloc(1, cls->bits.fastInstanceSize());
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            obj->initInstanceIsa(cls, dtor);
            return obj;
        }
        else {
            // Has ctor or raw isa or something. Use the slower path.
            id obj = class_createInstance(cls, 0);
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            return obj;
        }
    }
#endif

    // No shortcuts available.
    if (allocWithZone) return [cls allocWithZone:nil];
    return [cls alloc];
}

So let's see what it looks like:

bool canAllocFast() {
    assert(!isFuture());
    return bits.canAllocFast();
}

and

#if FAST_ALLOC
    ...
    bool canAllocFast() {
        return bits & FAST_ALLOC;
    }
#else
    ...
    bool canAllocFast() {
        return false;
    }
#endif

but how to make sure it is not a FAST_ALLOC, we can find it's defined here:

#if !__LP64__

...

#elif 1

...

#else

...
  
// summary bit for fast alloc path: !hasCxxCtor and 
//   !instancesRequireRawIsa and instanceSize fits into shiftedSize
#define FAST_ALLOC              (1UL<<2)
  
...

#endif

this code #elif 1 means it will always get into here. So the FAST_ALLOC will never be defined which leads to the result is we will never get into the if (fastpath(cls->canAllocFast())) braces.

Why we can set class to isa or can set different bitfield to it?

if (!nonpointer) {
    isa.cls = cls;
} else {
    assert(!DisableNonpointerIsa);
    assert(!cls->instancesRequireRawIsa());

    isa_t newisa(0);

#if SUPPORT_INDEXED_ISA
    assert(cls->classArrayIndex() > 0);
    newisa.bits = ISA_INDEX_MAGIC_VALUE;
    // isa.magic is part of ISA_MAGIC_VALUE
    // isa.nonpointer is part of ISA_MAGIC_VALUE
    newisa.has_cxx_dtor = hasCxxDtor;
    newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
    newisa.bits = ISA_MAGIC_VALUE;
    // isa.magic is part of ISA_MAGIC_VALUE
    // isa.nonpointer is part of ISA_MAGIC_VALUE
    newisa.has_cxx_dtor = hasCxxDtor;
    newisa.shiftcls = (uintptr_t)cls >> 3;
#endif

    // This write must be performed in a single store in some cases
    // (for example when realizing a class because other threads
    // may simultaneously try to use the class).
    // fixme use atomics here to guarantee single-store and to
    // guarantee memory order w.r.t. the class index table
    // ...but not too atomic because we don't want to hurt instantiation
    isa = newisa;
}

let's check the isa and see it is an union which means it can either be cls or bits but not set them at the same time.

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

what time does system create the class or meta-class?

The answer is they are created during the compile time. Let's prove it. First breakpoint at line 1 - main:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
            LGPerson *object = [LGPerson alloc];
    }
    return 0;
}

we can check the class:

(lldb) p/x LGPerson.class
(Class) $0 = 0x00000001000014c0 LGPerson

(lldb) x/4xg 0x00000001000014c0
0x1000014c0: 0x001d800100001499 0x0000000100b38140
0x1000014d0: 0x00000001003db240 0x0000000000000000

(lldb) p/x 0x001d800100001499 & 0x00007ffffffffff8
(long) $1 = 0x0000000100001498 // meta class
(lldb) po 0x0000000100001498
LGPerson

(lldb) x/4xg 0x0000000100001498
0x100001498: 0x001d800100b380f1 0x0000000100b380f0
0x1000014a8: 0x0000000101e03120 0x0000000200000003

We can see thay have been created. Another way we can improve it is check it in +load method of NSObject category.

There's also a third way, just compile the code and put the Macho to 'MachO View' app.

Screen Shot 2019-12-21 at 4.18.47 PM.png

We can prove that, too.

Pointer and its offset

let's see an example

    int a = 10; //
    int b = 10; //
    LGNSLog(@"%d -- %p", a, &a);
    LGNSLog(@"%d -- %p", b, &b);

check the result:

KC打印: 10 -- 0x7ffeefbff43c
KC打印: 10 -- 0x7ffeefbff438

a and b are different pointer. So we call this value copy. see another example:

    LGPerson *p1 = [LGPerson alloc];
    LGPerson *p2 = [LGPerson alloc];
    LGNSLog(@"%@ -- %p", p1, &p1);
    LGNSLog(@"%@ -- %p", p2, &p2);

we will find the result:

KC打印:  -- 0x7ffeefbff438
KC打印:  -- 0x7ffeefbff430

They are different pointers, and &p1 is the pointer point to the value which is p1's address. the same as &p2. Here is the 3rd example:

    int c[4] = { 1, 2, 3, 4 };
    NSLog(@"%p - %p - %p", &c, &c[0], &c[1]);

result:

: 0x7ffeefbff430 - 0x7ffeefbff430 - 0x7ffeefbff434

we find &c and &c[0] are the same because &c point to the first element's address. the same as &c[0]. &c[0] and &c[1] are 4 bytes away which is exactly the size of the int. let's try this:

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

see result:

: 0x7ffeefbff430 - 0x7ffeefbff430 - 0x7ffeefbff434
: 0x7ffeefbff430 - 0x7ffeefbff434 - 0x7ffeefbff438

d is the first element's address. d + 1 is the second element's address. d + 2 is the third element's address. We can play like this:

    int c[4] = { 1, 2, 3, 4 };
    int *d   = c;    
        for (int i = 0; i<4; i++) {
        // int value = c[i];
        int value = *(d+i);
        LGNSLog(@"%d",value);
    }

Result will be:

KC打印: 1
KC打印: 2
KC打印: 3
KC打印: 4

quite right. we can also check using lldb :

(lldb) x/4xg 0x7ffeefbff430
0x7ffeefbff430: 0x0000000200000001 0x0000000400000003
0x7ffeefbff440: 0x00007ffeefbff468 0xdf46a8e7bfc600ba

The struct of class

open the objc4 source code, let's do some test:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson *person = [LGPerson alloc];
        Class pClass     = object_getClass(person);
        NSLog(@"%@ - %p",person,pClass);
    }
    return 0;
}

breakpoint at line 5, then:

(lldb) x/4xg pClass
0x1000023b0: 0x001d800100002389 0x0000000100b37140
0x1000023c0: 0x00000001003da260 0x0000000000000000
(lldb) po 0x0000000100b37140
NSObject

(lldb) po 0x00000001003da260
4299006560

(lldb) p 0x00000001003da260
(long) $3 = 4299006560

We find the first part is isa, the second one is superclass, but what is the third one? Hard to check, use clang to have a try.

clang -rewrite-objc main.m

we can see

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        LGPerson *person = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc"));
        Class pClass = object_getClass(person);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_1x_5pdrpqcj35d9dkj3hc50wm_00000gn_T_main_3b1a26_mi_9,person,pClass);
    }
    return 0;
}

We take a look at the Class

typedef struct objc_class *Class;

its type is objc_class * , carry on exploring:

struct objc_class {
    Class _Nonnull isa __attribute__((deprecated));
} __attribute__((unavailable));

There's an isa in this struct , but here's no any info . back to our source code:

struct objc_class : objc_object {
    // Class ISA; // 8
    Class superclass; // 8
    cache_t cache;    // 16 不是8         // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    ...
}

Here we find it. objc_class is the subclass of objc_object, that's why we call it class object. Class is also an Object. So the ISA is it's superclass's property.

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

So NSObject is kind of objc_object. you can consider they are the same.

@interface NSObject  {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}

So where is the property or method in the Class?

two questions

Why isa was qualified by Class :

Class isa  OBJC_ISA_AVAILABILITY;

cause in early time, we use isa to just return the Class but now it has been optimized as nonpointer. we can also find here:

inline Class 
objc_object::ISA() 
{
    assert(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK);
#endif
}

the return type is Class, in the else part it is forced transformed to Class.

What is the relationship with objc_class and NSObject | objc_object and NSObject ?

  1. Actually, NSObject is the type of objc_class cause it's Class.

  2. objc_object is the C(under the hood) of the NSObject in Objective-C

Properties stored in Class

we put some properties and variables in class:

@interface LGPerson : NSObject{
    NSString *hobby;
}

@property (nonatomic, copy) NSString *nickName;

@end

@implementation LGPerson

@end

still breakpoint at main.m

(lldb) x/4xg pClass
0x100002338: 0x001d800100002311 0x0000000100b37140
0x100002348: 0x00000001003da260 0x0000000000000000

we need to use the pointer offset to get the property. So back to check the objc_class:

struct objc_class : objc_object {
    // Class ISA; // 8
    Class superclass; // 8
    cache_t cache;    // 16 不是8         // formerly cache pointer and vtable
    class_data_bits_t bits; 
    ...
}

here we can see ISA is a class, so it use 8 bytes. the same as superclass. the cache_t souds like caching some info, so we guess the property in stored in bits:

class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

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

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;

   ...
}

but what's the size of the cache_t?

struct cache_t {
    struct bucket_t *_buckets; // 0 ~ 7
    mask_t _mask;  // 4 | 8 % 4 == 0 so | 8 9 10 11
    mask_t _occupied; // 4 | 12 % 4 == 0 so | 12 13 14 15

...
};// total is 16

typedef uint32_t mask_t; 

here the _buckets is the pointer to the struct which take 8 bytes. so mask_t is take 4 bytes. We calculate it the struct is take 16 bytes.

So back to the objc_class:

struct objc_class : objc_object {
    // Class ISA; // 8
    Class superclass; // 8
    cache_t cache;    // 16 不是8         // formerly cache pointer and vtable
    class_data_bits_t bits; 
    ...
}

and with:

(lldb) x/4xg pClass
0x100002338: 0x001d800100002311 0x0000000100b37140
0x100002348: 0x00000001003da260 0x0000000000000000

we can see the pointer to the bits is 0x100002338 + 32 = 0x100002358

(lldb) p (class_data_bits_t *)0x100002358
(class_data_bits_t *) $1 = 0x0000000100002358
(lldb) po $1->data()
0x0000000100f6ee10

(lldb) p $1->data()
(class_rw_t *) $3 = 0x0000000100f6ee10

(lldb) p *$3
(class_rw_t) $4 = {
  flags = 2148139008
  version = 0
  ro = 0x0000000100002290
  methods = {
    list_array_tt = {
       = {
        list = 0x00000001000021e0
        arrayAndFlag = 4294975968
      }
    }
  }
  properties = {
    list_array_tt = {
       = {
        list = 0x0000000100002278
        arrayAndFlag = 4294976120
      }
    }
  }
  protocols = {
    list_array_tt = {
       = {
        list = 0x0000000000000000
        arrayAndFlag = 0
      }
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
  demangledName = 0x0000000000000000
}

(lldb) p $4.properties
(property_array_t) $5 = {
  list_array_tt = {
     = {
      list = 0x0000000100002278
      arrayAndFlag = 4294976120
    }
  }
}

we find the property_array_t:

class property_array_t : 
    public list_array_tt 
{
    typedef list_array_tt Super;

 public:
    property_array_t duplicate() {
        return Super::duplicate();
    }
};

class list_array_tt {
    struct array_t {
        uint32_t count;
        List* lists[0];
      ...
    }
      
      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;
        }
...
}

It looks like an array, let's try:

(lldb) p $5.list
(property_list_t *) $6 = 0x0000000100002278

check the property_list_t:

struct property_list_t : entsize_list_tt {
};

struct entsize_list_tt {
    uint32_t entsizeAndFlags;
    uint32_t count;
    Element first;

    Element& get(uint32_t i) const { 
        assert(i < count);
        return getOrEnd(i);
    }
    ...
}

we see the first here, so try:

(lldb) p $6->first
(property_t) $8 = (name = "nickName", attributes = "T@\"NSString\",C,N,V_nickName")

We find the name. But where is the hobby? in ro

(lldb) p $4.ro
(const class_ro_t *) $9 = 0x0000000100002290

(lldb) p *$9
(const class_ro_t) $10 = {
  flags = 388
  instanceStart = 8
  instanceSize = 24
  reserved = 0
  ivarLayout = 0x0000000100001f89 "\x02"
  name = 0x0000000100001f80 "LGPerson"
  baseMethodList = 0x00000001000021e0
  baseProtocols = 0x0000000000000000
  ivars = 0x0000000100002230
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000100002278
}

(lldb) p $10.baseProperties
(property_list_t *const) $11 = 0x0000000100002278

(lldb) p *$11
(property_list_t) $12 = {
  entsize_list_tt = {
    entsizeAndFlags = 16
    count = 1
    first = (name = "nickName", attributes = "T@\"NSString\",C,N,V_nickName")
  }
}

but still no hobby? where is it? let's check the ivas

(lldb) p $10.ivars
(const ivar_list_t *const) $13 = 0x0000000100002230
(lldb) p *$13
(const ivar_list_t) $14 = {
  entsize_list_tt = {
    entsizeAndFlags = 32
    count = 2
    first = {
      offset = 0x0000000100002300
      name = 0x0000000100001e94 "hobby"
      type = 0x0000000100001fa6 "@\"NSString\""
      alignment_raw = 3
      size = 8
    }
  }
}

(lldb) p $14.get(1)
(ivar_t) $15 = {
  offset = 0x0000000100002308
  name = 0x0000000100001e9a "_nickName"
  type = 0x0000000100001fa6 "@\"NSString\""
  alignment_raw = 3
  size = 8
}

here the hobby is.

Instance Methods stored in Class

Where is the methods? We have noticed that baseMethodList, check it:

(lldb) p $10.baseMethodList
(method_list_t *const) $16 = 0x00000001000021e0
(lldb) p *$16
(method_list_t) $17 = {
  entsize_list_tt = {
    entsizeAndFlags = 26
    count = 3
    first = {
      name = ".cxx_destruct"
      types = 0x0000000100001f8b "v16@0:8"
      imp = 0x0000000100001c90 (LGTest`-[LGPerson .cxx_destruct] at LGPerson.m:11)
    }
  }
}

Cool, got a c plus plus method. try to see method_t:

struct method_t {
    SEL name;
    const char *types;
    MethodListIMP imp;

    struct SortBySELAddress :
        public std::binary_function
    {
        bool operator() (const method_t& lhs,
                         const method_t& rhs)
        { return lhs.name < rhs.name; }
    };
};

they both have name, types and imp. the count is 3 means the setter and getter :

(lldb) p $17.get(1)
(method_t) $18 = {
  name = "setNickName:"
  types = 0x0000000100001f9b "v24@0:8@16"
  imp = 0x0000000100001c50 (LGTest`-[LGPerson setNickName:] at LGPerson.h:17)
}
(lldb) p $17.get(2)
(method_t) $19 = {
  name = "nickName"
  types = 0x0000000100001f93 "@16@0:8"
  imp = 0x0000000100001c20 (LGTest`-[LGPerson nickName] at LGPerson.h:17)
}

Let's add some methods by ourselves:

@interface LGPerson : NSObject{
    NSString *hobby;
}

@property (nonatomic, copy) NSString *nickName;

- (void)sayHello;
+ (void)sayHappy;

@end

@implementation LGPerson

- (void)sayHello{
    NSLog(@"LGPerson say : Hello!!!");
}

+ (void)sayHappy{
    NSLog(@"LGPerson say : Happy!!!");
}


@end

The same process:

(lldb) x/4xg pClass
0x1000023b0: 0x001d800100002389 0x0000000100b37140
0x1000023c0: 0x00000001003da260 0x0000000000000000

(lldb) p (class_data_bits_t *)0x1000023d0
(class_data_bits_t *) $3 = 0x00000001000023d0
(lldb) p $3->data()
(class_rw_t *) $4 = 0x0000000100f50de0

(lldb) p *$4
(class_rw_t) $5 = {
  flags = 2148139008
  version = 0
  ro = 0x0000000100002308
  methods = {
    list_array_tt = {
       = {
        list = 0x0000000100002240
        arrayAndFlag = 4294976064
      }
    }
  }
  properties = {
    list_array_tt = {
       = {
        list = 0x00000001000022f0
        arrayAndFlag = 4294976240
      }
    }
  }
  protocols = {
    list_array_tt = {
       = {
        list = 0x0000000000000000
        arrayAndFlag = 0
      }
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
  demangledName = 0x0000000000000000
}

(lldb) p *$6
(const class_ro_t) $7 = {
  flags = 388
  instanceStart = 8
  instanceSize = 24
  reserved = 0
  ivarLayout = 0x0000000100001f89 "\x02"
  name = 0x0000000100001f80 "LGPerson"
  baseMethodList = 0x0000000100002240
  baseProtocols = 0x0000000000000000
  ivars = 0x00000001000022a8
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x00000001000022f0
}

(lldb) p $7.baseMethodList
(method_list_t *const) $8 = 0x0000000100002240
(lldb) p *$8
(method_list_t) $9 = {
  entsize_list_tt = {
    entsizeAndFlags = 26
    count = 4
    first = {
      name = "sayHello"
      types = 0x0000000100001f8b "v16@0:8"
      imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)
    }
  }
}
(lldb) p $9.get(1)
(method_t) $10 = {
  name = ".cxx_destruct"
  types = 0x0000000100001f8b "v16@0:8"
  imp = 0x0000000100001c60 (LGTest`-[LGPerson .cxx_destruct] at LGPerson.m:11)
}
(lldb) p $9.get(2)
(method_t) $11 = {
  name = "setNickName:"
  types = 0x0000000100001f9b "v24@0:8@16"
  imp = 0x0000000100001c20 (LGTest`-[LGPerson setNickName:] at LGPerson.h:17)
}
(lldb) p $9.get(3)
(method_t) $12 = {
  name = "nickName"
  types = 0x0000000100001f93 "@16@0:8"
  imp = 0x0000000100001bf0 (LGTest`-[LGPerson nickName] at LGPerson.h:17)
}

Found the sayHello, where's sayHappy?

Class Methods stored in Class

Yeah, the sayHappy is a class method, so, use some api to check it:

void testObjc_copyIvar_copyProperies(Class pClass){
    
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList(pClass, &count);
    for (unsigned int i=0; i < count; i++) {
        Ivar const ivar = ivars[i];
        //获取实例变量名
        const char*cName = ivar_getName(ivar);
        NSString *ivarName = [NSString stringWithUTF8String:cName];
        NSLog(@"class_copyIvarList:%@",ivarName);
    }
    free(ivars);

    unsigned int pCount = 0;
    objc_property_t *properties = class_copyPropertyList(pClass, &pCount);
    for (unsigned int i=0; i < pCount; i++) {
        objc_property_t const property = properties[i];
        //获取属性名
        NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
        //获取属性值
        NSLog(@"class_copyProperiesList:%@",propertyName);
    }
    free(properties);
}

void testObjc_copyMethodList(Class pClass){
    unsigned int count = 0;
    Method *methods = class_copyMethodList(pClass, &count);
    for (unsigned int i=0; i < count; i++) {
        Method const method = methods[i];
        //获取方法名
        NSString *key = NSStringFromSelector(method_getName(method));
        
        NSLog(@"Method, name: %@", key);
    }
    free(methods);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson *person = [LGPerson alloc];
        Class pClass     = object_getClass(person);
        
        testObjc_copyIvar_copyProperies(pClass);
        testObjc_copyMethodList(pClass);
        
        NSLog(@"%@ - %p",person,pClass);
    }
    return 0;
}

we can see the result:

2019-12-21 22:23:56.225863+0800 LGTest[4745:249221] class_copyIvarList:hobby
2019-12-21 22:23:56.226531+0800 LGTest[4745:249221] class_copyIvarList:_nickName
2019-12-21 22:23:56.226771+0800 LGTest[4745:249221] class_copyProperiesList:nickName

2019-12-21 22:23:56.226892+0800 LGTest[4745:249221] Method, name: sayHello
2019-12-21 22:23:56.227135+0800 LGTest[4745:249221] Method, name: .cxx_destruct
2019-12-21 22:23:56.227311+0800 LGTest[4745:249221] Method, name: setNickName:
2019-12-21 22:23:56.227416+0800 LGTest[4745:249221] Method, name: nickName

Still no sayhappy, carry on:

void testInstanceMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));

    Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
    
    NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
    NSLog(@"%s",__func__);
}

void testClassMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getClassMethod(pClass, @selector(sayHello));
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));

    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy)); // ?
    
    NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
    NSLog(@"%s",__func__);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson *person = [LGPerson alloc];
        Class pClass     = object_getClass(person);
        
        testObjc_copyIvar_copyProperies(pClass);
        testObjc_copyMethodList(pClass);

        testInstanceMethod_classToMetaclass(pClass);
        testClassMethod_classToMetaclass(pClass);
        
        NSLog(@"%@ - %p",person,pClass);
    }
    return 0;
}

the result:

2019-12-21 22:26:35.495128+0800 LGTest[4762:251068] 0x100002248-0x0-0x0-0x1000021e0
2019-12-21 22:26:35.495172+0800 LGTest[4762:251068] testInstanceMethod_classToMetaclass
2019-12-21 22:26:35.495225+0800 LGTest[4762:251068] 0x0-0x0-0x1000021e0-0x1000021e0
2019-12-21 22:26:35.495267+0800 LGTest[4762:251068] testClassMethod_classToMetaclass

testInstanceMethod_classToMetaclass:

0x100002248: means pClass has instance method sayHello

0x0: means pClass' meta Class has no instance method sayhello

0x0: means pClass has no instance method sayHappy

0x1000021e0: means pClass' meta Class has instance method sayHappy

testClassMethod_classToMetaclass:

0x0: means pClass has no class method sayHello

0x0: means pClass' meta Class has no class method sayhello

0x1000021e0: means pClass has class method sayHappy

0x1000021e0: means pClass' meta Class has class method sayHappy

Why meta class has a class method which is the class method of it's instance? (Lator)

Use 'Macho view' to check:

Screen Shot 2019-12-21 at 10.36.10 PM.png
(lldb) x/4xg pClass
0x1000023b0: 0x001d800100002389 0x0000000100b37140
0x1000023c0: 0x000000010194dfa0 0x0000000200000003
image list
[  0] 42E95CCE-60B3-3684-A003-6FA73B5AB4B5 0x0000000100000000 /Users/justin/Library/Developer/Xcode/DerivedData/objc-ewpbpbvubdzujbdyksqawblkbzsx/Build/Products/Debug/LGTest 
(lldb) p/x 0x001d800100002389 & 0x00007ffffffffff8
(long) $1 = 0x0000000100002388
(lldb) po 0x0000000100002388
Screen Shot 2019-12-21 at 10.37.13 PM.png
Screen Shot 2019-12-21 at 10.39.52 PM.png

lator on 'MachO View'

turn back to llbd:

(lldb) x/4xg pClass
0x1000023b0: 0x001d800100002389 0x0000000100b37140
0x1000023c0: 0x000000010194dfa0 0x0000000200000003
image list
[  0] 42E95CCE-60B3-3684-A003-6FA73B5AB4B5 0x0000000100000000 /Users/justin/Library/Developer/Xcode/DerivedData/objc-ewpbpbvubdzujbdyksqawblkbzsx/Build/Products/Debug/LGTest 
(lldb) p/x 0x001d800100002389 & 0x00007ffffffffff8
(long) $1 = 0x0000000100002388
(lldb) po 0x0000000100002388

(lldb) x/4xg 0x0000000100002388
0x100002388: 0x001d800100b370f1 0x0000000100b370f0
0x100002398: 0x000000010194dff0 0x0000000400000007

(lldb) p (class_data_bits_t *)0x1000023a8
(class_data_bits_t *) $3 = 0x00000001000023a8
(lldb) p $3->data()
(class_rw_t *) $4 = 0x000000010194d490
(lldb) p *$4->ro
(const class_ro_t) $5 = {
  flags = 389
  instanceStart = 40
  instanceSize = 40
  reserved = 0
  ivarLayout = 0x0000000000000000
  name = 0x0000000100001f80 "LGPerson"
  baseMethodList = 0x00000001000021d8
  baseProtocols = 0x0000000000000000
  ivars = 0x0000000000000000
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000000000000
}
(lldb) p $5.baseMethodList
(method_list_t *const) $6 = 0x00000001000021d8
(lldb) p *$6
(method_list_t) $7 = {
  entsize_list_tt = {
    entsizeAndFlags = 26
    count = 1
    first = {
      name = "sayHappy"
      types = 0x0000000100001f8b "v16@0:8"
      imp = 0x0000000100001bc0 (LGTest`+[LGPerson sayHappy] at LGPerson.m:17)
    }
  }
}

found it, Let's make a conclusion:

  • member variable: ivar
  • properties: property and ivar(_property)
  • instance methods: Class
  • class methods : meta Class

find instance method:

isa流程图_1.png

find class method:

isa流程图_2.png

你可能感兴趣的:(Class)