The objc source code in this article is objc4-838, and different source codes may vary from Apple's official website.
Article directory
Table of contents
2. Summary of
3. isa pointing graph and superclass graph
4. Class Structure Analysis and Exploration
1.objc_object&objc_class source code
a. Get the first address of class_data_bits_t bits
b. Get the data address of class_rw_t type
c. View the property list properties
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_t
of 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。