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.
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打印: <LGPerson: 0x10050f1d0> -- 0x7ffeefbff438
KC打印: <LGPerson: 0x10050a9d0> -- 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 <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
?
Actually,
NSObject
is the type ofobjc_class
cause it's Class.objc_object
is the C(under the hood) of theNSObject
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<method_t, method_list_t> = {
= {
list = 0x00000001000021e0
arrayAndFlag = 4294975968
}
}
}
properties = {
list_array_tt<property_t, property_list_t> = {
= {
list = 0x0000000100002278
arrayAndFlag = 4294976120
}
}
}
protocols = {
list_array_tt<unsigned long, protocol_list_t> = {
= {
list = 0x0000000000000000
arrayAndFlag = 0
}
}
}
firstSubclass = nil
nextSiblingClass = NSUUID
demangledName = 0x0000000000000000
}
(lldb) p $4.properties
(property_array_t) $5 = {
list_array_tt<property_t, property_list_t> = {
= {
list = 0x0000000100002278
arrayAndFlag = 4294976120
}
}
}
we find the property_array_t
:
class property_array_t :
public list_array_tt<property_t, property_list_t>
{
typedef list_array_tt<property_t, property_list_t> Super;
public:
property_array_t duplicate() {
return Super::duplicate<property_array_t>();
}
};
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<property_t, property_list_t, 0> {
};
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<property_t, property_list_t, 0> = {
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<ivar_t, ivar_list_t, 0> = {
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<method_t, method_list_t, 3> = {
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<const method_t&,
const method_t&, bool>
{
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<method_t, method_list_t> = {
= {
list = 0x0000000100002240
arrayAndFlag = 4294976064
}
}
}
properties = {
list_array_tt<property_t, property_list_t> = {
= {
list = 0x00000001000022f0
arrayAndFlag = 4294976240
}
}
}
protocols = {
list_array_tt<unsigned long, protocol_list_t> = {
= {
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<method_t, method_list_t, 3> = {
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:
(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
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<method_t, method_list_t, 3> = {
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:
find class method: