OC对象的指针与内存地址关系
新建一个OC的工程,再新建一个NSObject
类PCJPerson
;打印一下这个三个对象的对象地址
,指针地址
,指针变量地址
。
PCJPerson *p1 = [PCJPerson alloc];
PCJPerson *p2 = [p1 init];
PCJPerson *p3 = [p1 init];
PCJPerson *p4 = [[PCJPerson alloc]init];
NSLog(@"%@-%p-%p",p1,p1,&p1);
NSLog(@"%@-%p-%p",p2,p2,&p2);
NSLog(@"%@-%p-%p",p3,p3,&p3);
NSLog(@"%@-%p-%p",p4,p4,&p4);
由于p1、p2、p3的对象地址、指针地址一致,证明这个是指向同一个对象(堆内存);但是指针变量不同那是因为他们是不同的局部变量,指向不同的地址(栈内存)。p4的dui x
补充点
- (
对象地址
、指针地址
在堆
内存16byte
)、(指针变量
在栈
内存8byte
)。 栈
:栈是向低地址
扩展的数据结构,堆
:堆是向高地址
扩展的数据结构,是不连续的内存区域。
结论
-
1、
alloc
让对象有了内存空间,有了指针指向。 -
2、
init
后内存没有变化,证明init
没有对指针做什么操作,并不具备开辟堆内存空间。
三种底层探索分析方式
断点进入alloc方法
在需要调试的alloc
地方断点,当代码执行到断点处,按住control + step into
(单步)进入即可进入到汇编代码部分,如图:
符号断点
从上一步断点alloc
进入得知之后进入objc_alloc
方法,把objc_alloc
添加符号断点如下:
汇编方式
当断点停留在alloc
时,选择Xcode -> Debug -> Debug Workflow -> Always Show Disassembly进入汇编找到objc_alloc方法,操作如下图:
源码调试探索
根据objc4源码编译工程,断点进入alloc
调试,流程如下:
- 1.
[Person alloc]
通过消息发送调用obc_alloc
,cls
终端打印自定义类Person
。
// Calls [cls alloc].
id objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
-
- 第一次进入
callAlloc
方法,走objc_msgSend
消息发送方法alloc
。
- 第一次进入
// Call [cls alloc] or [cls allocWithZone:nil], with appropriate
// shortcutting optimizations.
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone = false)
{
if (slowpath(checkNil && !cls)) return nil;
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
// No shortcuts available.
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
// 第一次进入消息发送
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}
- 3.由于Person没有
alloc
类方法,慢速查找
到NSObject
的alloc
类方法,self
终端打印的是我们自定义的类Person
。
@implementation NSObject
···
+ (id)alloc {
return _objc_rootAlloc(self);
// [CJPerson alloc]终端 po self => Person
}
···
@end
- 4.进入
_objc_rootAlloc
方法,id
就是objc_object
的结构指针
// 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*/);
}
- 5.接着进入
callAlloc
方法,
// 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*/);
}
- 6.进入
callAlloc
方法,通过hasCusomAWZ()
判断class
或superclass
是否有自定义的alloc/allocWithZone
方法,有走消息转发objc_msgSend
;无走方法_objc_rootAllocWithZone
。
// Call [cls alloc] or [cls allocWithZone:nil], with appropriate
// shortcutting optimizations.
static ALWAYS_INLINE id callAlloc(Class cls, bool checkNil, bool allocWithZone = false) {
if (slowpath(checkNil && !cls)) return nil;
// class or superclass has default alloc/allocWithZone: implementation
// Note this is is stored in the metaclass.
// 通过FAST_CACHE_HAS_DEFAULT_AWZ标识位判断是类或者父类否有自定义的alloc方法
// alloc/allocWithZone的类方法是存储在metaClass元类
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// 无自定义的alloc
return _objc_rootAllocWithZone(cls, nil);
}
// No shortcuts available.
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}
- 7.进入
_objc_rootAllocWithZone
方法。
NEVER_INLINE id _objc_rootAllocWithZone(Class cls, objc_zone_t zone __unused) {
// allocWithZone under __OBJC2__ ignores the zone parameter
return _class_createInstanceFromZone(cls, 0, nil,
OBJECT_CONSTRUCT_CALL_BADALLOC);
}
- 8.进入
_class_createInstanceFromZone
方法。
/***********************************************************************
* class_createInstance
* fixme
* Locking: none
*
* Note: this function has been carefully written so that the fastpath
* takes no branch.
************************************************************************ ** **/**
static ALWAYS_INLINE id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
int construct_flags = OBJECT_CONSTRUCT_NONE,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil) {
ASSERT(cls->isRealized());
// Read class's info bits all at once for performance
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size;
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
#if SUPPORT_ZONES
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
#endif
obj = (id)calloc(1, size);
#if SUPPORT_ZONES
}
#endif
if (slowpath(!obj)) {
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}
if (!zone && fast) {
obj->initInstanceIsa(cls, hasCxxDtor);
} else {
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
if (fastpath(!hasCxxCtor)) {
return obj;
}
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
return object_cxxConstructFromClass(obj, cls, construct_flags);
}
- 9.进入
alloc
核心三步1-instanceSize
,计算开辟内存空间所需的大小
struct objc_class: objc_object {
...
inline size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
...
}
- 10.进入
alloc
核心三步2-calloc
,开辟内存,返回地址指针
// C 开辟内存方法
obj = (id)calloc(1, size);
- 11 .进入
alloc
核心三步3-initInstanceIsa
,初始化指针,和类关联起来
inline void objc_object::initInstanceIsa(Class cls, bool hasCxxDtor) {
ASSERT(!cls->instancesRequireRawIsa());
ASSERT(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
附上调用流程图如下:
总结
alloc
主要作用就是向系统申请开辟内存,通过isa
指针与类进行关联。