NSObject
Review
toggle the button to check the whole call heap stack.
When run the code ABCRobot *r = [ABCRobot alloc];
You will find before it calls the
// Base class implementation of +alloc. cls is not nil.
// Calls [cls allocWithZone:nil].
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
It call the method below instead:
// Calls [cls alloc].
id
objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
then it will come to here:
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];
}
then it calls [cls alloc]
which is the code below:
(id)alloc {
return _objc_rootAlloc(self);
}
// Base class implementation of +alloc. cls is not nil.
// Calls [cls allocWithZone:nil].
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
Guess before we call the method, the system bind the symbol table. When initialize macho, bind the symbol (alloc - objc_alloc) , only once.
in objc-runtime-new.mm
, check the code in line 13:
/***********************************************************************
* fixupMessageRef
* Repairs an old vtable dispatch call site.
* vtable dispatch itself is not supported.
**********************************************************************/
static void
fixupMessageRef(message_ref_t *msg)
{
msg->sel = sel_registerName((const char *)msg->sel);
if (msg->imp == &objc_msgSend_fixup) {
if (msg->sel == SEL_alloc) {
msg->imp = (IMP)&objc_alloc;
} else if (msg->sel == SEL_allocWithZone) {
msg->imp = (IMP)&objc_allocWithZone;
} else if (msg->sel == SEL_retain) {
msg->imp = (IMP)&objc_retain;
} else if (msg->sel == SEL_release) {
msg->imp = (IMP)&objc_release;
} else if (msg->sel == SEL_autorelease) {
msg->imp = (IMP)&objc_autorelease;
} else {
msg->imp = &objc_msgSend_fixedup;
}
}
else if (msg->imp == &objc_msgSendSuper2_fixup) {
msg->imp = &objc_msgSendSuper2_fixedup;
}
else if (msg->imp == &objc_msgSend_stret_fixup) {
msg->imp = &objc_msgSend_stret_fixedup;
}
else if (msg->imp == &objc_msgSendSuper2_stret_fixup) {
msg->imp = &objc_msgSendSuper2_stret_fixedup;
}
#if defined(__i386__) || defined(__x86_64__)
else if (msg->imp == &objc_msgSend_fpret_fixup) {
msg->imp = &objc_msgSend_fpret_fixedup;
}
#endif
#if defined(__x86_64__)
else if (msg->imp == &objc_msgSend_fp2ret_fixup) {
msg->imp = &objc_msgSend_fp2ret_fixedup;
}
#endif
}
this method can be found called in _read_images
.
#if SUPPORT_FIXUP
// Fix up old objc_msgSend_fixup call sites
for (EACH_HEADER) {
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
if (count == 0) continue;
if (PrintVtables) {
_objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
"call sites in %s", count, hi->fname());
}
for (i = 0; i < count; i++) {
fixupMessageRef(refs+i);
}
}
ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
#endif
looks like it is fix up.
Compare with these two methods:
// Calls [cls alloc].
id
objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
// Base class implementation of +alloc. cls is not nil.
// Calls [cls allocWithZone:nil].
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
both of them call the callAlloc
method, but different parameters for zone
.
isa connect the object and class
let's check the code:
LGPerson *object = [LGPerson alloc];
NSLog(@"Hello, World! %@",object);
break point at line 2, then:
(lldb) x/4xg object
0x101824790: 0x001d8001000014b9 0x0000000000000000
0x1018247a0: 0x0000000000000002 0x00007fff99649588
So how to improve the first place is isa
?
let's try:
(lldb) p/t 0x001d8001000014b9
(long) $1 = 0b0000000000011101100000000000000100000000000000000001010010111001
p/t 0x.... // make 16 to 2
p/o ... // make 16 to 8
p/d ... // make 16 to 10
let's see the class:
(lldb) p/x LGPerson.class
(Class) $2 = 0x00000001000014b8 LGPerson
Then:
(lldb) p/t (uintptr_t)LGPerson.class
(uintptr_t) $3 = 0b0000000000000000000000000000000100000000000000000001010010111000
Then check the isa
:
(lldb) p/t $1>>3
(long) $8 = 0b0000000000000011101100000000000000100000000000000000001010010111
(lldb) p/t $8<<3
(long) $9 = 0b0000000000011101100000000000000100000000000000000001010010111000
(lldb) p/t $9<<17
(long) $10 = 0b0000000000000010000000000000000000101001011100000000000000000000
(lldb) p/t $10>>17
(long) $11 = 0b0000000000000000000000000000000100000000000000000001010010111000
$3 = 0b0000000000000000000000000000000100000000000000000001010010111000
$11 = 0b0000000000000000000000000000000100000000000000000001010010111000
They are the same. So we can iimprove that the first place is isa
This way is a little bit hard, let's try another one:
(lldb) x/4xg object
0x101824790: 0x001d8001000014b9 0x0000000000000000
0x1018247a0: 0x0000000000000002 0x00007fff99649588
we can use :
object_getClass(object);
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
inline Class
objc_object::getIsa()
{
if (!isTaggedPointer()) return ISA();
uintptr_t ptr = (uintptr_t)this;
if (isExtTaggedPointer()) {
uintptr_t slot =
(ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
return objc_tag_ext_classes[slot];
} else {
uintptr_t slot =
(ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
return objc_tag_classes[slot];
}
}
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
}
# define ISA_MASK 0x00007ffffffffff8ULL
Got the ISA_MASK!!
(lldb) x/4xg object
0x101824790: 0x001d8001000014b9 0x0000000000000000
0x1018247a0: 0x0000000000000002 0x00007fff99649588
(lldb) p/x LGPerson.class
(Class) $13 = 0x00000001000014b8 LGPerson
(lldb) p/x 0x001d8001000014b9 & 0x00007ffffffffff8
(long) $14 = 0x00000001000014b8
(lldb) po 0x00000001000014b8
LGPerson
$13 == $14 !!!!
analysis isa
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
ISA_MASK has different value in different architecture.
Q2: We can create seveal objects, how about class?
class has only one.
//MARK: - 分析类对象内存存在个数
void lgTestClassNum(){
Class class1 = [LGPerson class];
Class class2 = [LGPerson alloc].class;
Class class3 = object_getClass([LGPerson alloc]);
Class class4 = [LGPerson alloc].class;
NSLog(@"\n%p-\n%p-\n%p-\n%p",class1,class2,class3,class4);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *p = [LGPerson alloc];
NSLog(@"%@",p);
lgTestClassNum();
NSLog(@"isa 我来了!");
}
return 0;
}
Result:
2019-12-19 22:21:18.856127+0800 001-对象isa[1969:194885] <LGPerson: 0x1006ba910>
2019-12-19 22:21:18.856628+0800 001-对象isa[1969:194885]
0x100003108-
0x100003108-
0x100003108-
0x100003108
2019-12-19 22:21:18.856730+0800 001-对象isa[1969:194885] isa 我来了!
We proved the class has only one in memory.
Let's see the memory structure of the class:
(lldb) x/4xg LGPerson.class
0x1000014c0: 0x001d800100001499 0x0000000100b38140
0x1000014d0: 0x00000001003db240 0x0000000000000000
(lldb) po 0x1000014c0
LGPerson
So the first pointer of the class is point to itself?
Actually, it's meta class. So let's prove that the isa of class is point to tht meta class.
(lldb) p/x 0x001d800100001499 & 0x00007ffffffffff8
(long) $2 = 0x0000000100001498
(lldb) po 0x0000000100001498
LGPerson
(lldb) x/4xg 0x0000000100001498
0x100001498: 0x001d800100b380f1 0x0000000100b380f0
0x1000014a8: 0x0000000100f755a0 0x0000000100000007
(lldb) p/x 0x001d800100b380f1 & 0x00007ffffffffff8
(long) $4 = 0x0000000100b380f0
(lldb) po 0x0000000100b380f0
NSObject
(lldb) p/x NSObject.class
(Class) $6 = 0x0000000100b38140 NSObject
(lldb) x/4xg NSObject.class
0x100b38140: 0x001d800100b380f1 0x0000000000000000
0x100b38150: 0x0000000101f09db0 0x0000000200000003
(lldb) p/x 0x001d800100b380f1 & 0x00007ffffffffff8
(long) $8 = 0x0000000100b380f0
(lldb) po 0x0000000100b380f0
NSObject
we have found:
SubInstance -> subclass -> meta Class -> root meta Class -> root meta Class(itself)
NSObject Instance -> root NSObject Class -> root meta Class
(lldb) x/4xg 0x0000000100b380f0 // (root meta class)
0x100b380f0: 0x001d800100b380f1 0x0000000100b38140
0x100b38100: 0x0000000102d696e0 0x0000000500000007
(lldb) p/x 0x0000000100b380f0 & 0x00007ffffffffff8
(long) $10 = 0x0000000100b380f0
(lldb) po 0x0000000100b380f0
NSObject
we can also prove in this way:
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *p = [LGPerson alloc];
NSLog(@"%@",p);
lgTestNSObject();
// lgTypes();
NSLog(@"isa 我来了!");
}
return 0;
}
void lgTestNSObject(){
// 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);
}
result is:
2019-12-19 22:46:18.845295+0800 001-对象isa[2008:209154]
0x1005a2500 实例对象
0x7fff9b38a118 类
0x7fff9b38a0f0 元类
0x7fff9b38a0f0 根元类
0x7fff9b38a0f0 根根元类
2019-12-19 22:46:18.845361+0800 001-对象isa[2008:209154] isa 我来了!
- root class 's superclass is NSObject class
- NSObject's superclass is nil
Class's struct
struct objc_class : objc_object {
// 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
....
}
we find the 1st variable is ISA, the 2nd is superclass.
So we can prove the root class's super class
(lldb) x/4xg NSObject.class
[0x100b38140]: 0x001d800100b380f1 0x0000000000000000
0x100b38150: 0x0000000101e13810 0x0000000200000003
(lldb) p/x 0x001d800100b380f1 & 0x00007ffffffffff8
(long) $1 = 0x0000000100b380f0
(lldb) po 0x0000000100b380f0
NSObject
(lldb) x/4xg 0x0000000100b380f0
0x100b380f0: 0x001d800100b380f1 [0x0000000100b38140]
0x100b38100: 0x0000000100f5a6d0 0x0000000400000007
(lldb) po 0x0000000100b38140
NSObject
Object
@interface LGPerson : NSObject
@end
@implementation LGPerson
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"123");
}
return 0;
}
Clang -rewrite-objc main.m -o main.cpp
#ifndef _REWRITER_typedef_LGPerson
#define _REWRITER_typedef_LGPerson
typedef struct objc_object LGPerson;
typedef struct {} _objc_exc_LGPerson;
#endif
struct LGPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
};
struct NSObject_IMPL {
Class isa;
};
we find object is the struct and inherit the property from the super class. What about the property in itself?
// 对象 -编译 --> 结构体 父类的属性继承过来
// 下节课开始 - 展开分析 类的结构 (对象 - isa)
// 细节分析 -
@interface LGPerson : NSObject
@property (nonatomic, copy) NSString *name; // 属性的区别
@end
@implementation LGPerson
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"123");
}
return 0;
}
#ifndef _REWRITER_typedef_LGPerson
#define _REWRITER_typedef_LGPerson
typedef struct objc_object LGPerson;
typedef struct {} _objc_exc_LGPerson;
#endif
extern "C" unsigned long OBJC_IVAR_$_LGPerson$_name;
struct LGPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_name;
};
// @property (nonatomic, copy) NSString *name;
/* @end */
// @implementation LGPerson
static NSString * _I_LGPerson_name(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_LGPerson_setName_(LGPerson * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _name), (id)name, 0, 1); }
// @end
...
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[2];
} _OBJC_$_INSTANCE_METHODS_LGPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
// @ return value
// @ -> id 0 - 7
// : -> sel 8 - 15
{{(struct objc_selector *)"name", "@16@0:8", (void *)_I_LGPerson_name},
{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_LGPerson_setName_}}
};
check code here:
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *p = [LGPerson alloc];
NSLog(@"%@",p);
lgTypes();
NSLog(@"isa 我来了!");
}
return 0;
}
void lgTypes(){
NSLog(@"char --> %s",@encode(char));
NSLog(@"int --> %s",@encode(int));
NSLog(@"short --> %s",@encode(short));
NSLog(@"long --> %s",@encode(long));
NSLog(@"long long --> %s",@encode(long long));
NSLog(@"unsigned char --> %s",@encode(unsigned char));
NSLog(@"unsigned int --> %s",@encode(unsigned int));
NSLog(@"unsigned short --> %s",@encode(unsigned short));
NSLog(@"unsigned long --> %s",@encode(unsigned long long));
NSLog(@"float --> %s",@encode(float));
NSLog(@"bool --> %s",@encode(bool));
NSLog(@"void --> %s",@encode(void));
NSLog(@"char * --> %s",@encode(char *));
NSLog(@"id --> %s",@encode(id));
NSLog(@"Class --> %s",@encode(Class));
NSLog(@"SEL --> %s",@encode(SEL));
int array[] = {1,2,3};
NSLog(@"int[] --> %s",@encode(typeof(array)));
typedef struct person{
char *name;
int age;
}Person;
NSLog(@"struct --> %s",@encode(Person));
typedef union union_type{
char *name;
int a;
}Union;
NSLog(@"union --> %s",@encode(Union));
int a = 2;
int *b = {&a};
NSLog(@"int[] --> %s",@encode(typeof(b)));
}
Result:
2019-12-19 23:19:32.412972+0800 001-对象isa[2099:227343] <LGPerson: 0x1007100b0>
2019-12-19 23:19:32.413433+0800 001-对象isa[2099:227343] char --> c
2019-12-19 23:19:32.413491+0800 001-对象isa[2099:227343] int --> i
2019-12-19 23:19:32.413534+0800 001-对象isa[2099:227343] short --> s
2019-12-19 23:19:32.413573+0800 001-对象isa[2099:227343] long --> q
2019-12-19 23:19:32.413641+0800 001-对象isa[2099:227343] long long --> q
2019-12-19 23:19:32.413692+0800 001-对象isa[2099:227343] unsigned char --> C
2019-12-19 23:19:32.413738+0800 001-对象isa[2099:227343] unsigned int --> I
2019-12-19 23:19:32.413782+0800 001-对象isa[2099:227343] unsigned short --> S
2019-12-19 23:19:32.413825+0800 001-对象isa[2099:227343] unsigned long --> Q
2019-12-19 23:19:32.413867+0800 001-对象isa[2099:227343] float --> f
2019-12-19 23:19:32.413909+0800 001-对象isa[2099:227343] bool --> B
2019-12-19 23:19:32.413953+0800 001-对象isa[2099:227343] void --> v
2019-12-19 23:19:32.413995+0800 001-对象isa[2099:227343] char * --> *
2019-12-19 23:19:32.414036+0800 001-对象isa[2099:227343] id --> @
2019-12-19 23:19:32.414080+0800 001-对象isa[2099:227343] Class --> #
2019-12-19 23:19:32.414122+0800 001-对象isa[2099:227343] SEL --> :
2019-12-19 23:19:32.414171+0800 001-对象isa[2099:227343] int[] --> [3i]
2019-12-19 23:19:32.414654+0800 001-对象isa[2099:227343] struct --> {person=*i}
2019-12-19 23:19:32.414687+0800 001-对象isa[2099:227343] union --> (union_type=*i)
2019-12-19 23:19:32.414712+0800 001-对象isa[2099:227343] int[] --> ^i
2019-12-19 23:19:32.414764+0800 001-对象isa[2099:227343] isa 我来了!
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk ViewController.m
xcrun -sdk iphonesimulator clang -rewrite-objc ViewController.m
xcrun -sdk iphoneos clang -rewrite-objc ViewController.m
// 对象 -编译 --> 结构体 父类的属性继承过来
@interface LGPerson : NSObject{
NSString *name; // 成员变量 : 底层编译不会生成相应的 setter getter
// lgTeacher *t; // 实例变量 : 是一种特殊的成员变量 (类声明而来 实例 - id (void *))
}
@property (nonatomic, copy) NSString *name; // 属性的区别
@end
@implementation LGPerson
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"123");
}
return 0;
}
The difference between member variable and property:
#ifndef _REWRITER_typedef_LGPerson
#define _REWRITER_typedef_LGPerson
typedef struct objc_object LGPerson;
typedef struct {} _objc_exc_LGPerson;
#endif
extern "C" unsigned long OBJC_IVAR_$_LGPerson$_name;
struct LGPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *name;
NSString *_name;
};
// @property (nonatomic, copy) NSString *name;
/* @end */
// @implementation LGPerson
static NSString * _I_LGPerson_name(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_LGPerson_setName_(LGPerson * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _name), (id)name, 0, 1); }
// @end
Q&A
NSProxy class's isa is not point to NSObject. It point to NSProxy meta class. And NSProxy meta class's isa point to itself. Also NSProxy's super class is not NSObject, it's nil.