前言
作为一门面向对象的语言,用一句万物皆对象来形容OC是非常贴切的。而NSObject 几乎是有所OC对象的老祖(为什么说几乎呢而不是全部呢?因为哈呦一个叫做NSProxy 的抽象类);但凡是使用到对象你需要先对对象进行初始化吧,那今天就来讨论一下alloc到底做了什么。
![](https://img.haomeiwen.com/i1694726/0d9d9cbecef6ffc1.png)
1、先来看一个例子:
CDSubObj *obj1 = [CDSubObj alloc];
CDSubObj *obj2 = [obj1 init];
CDSubObj *obj3 = [obj1 init];
CDSubObj *obj4 = [CDSubObj alloc];
NSLog(@"%@->%p", obj1, obj1);
NSLog(@"%@->%p", obj2, obj2);
NSLog(@"%@->%p", obj3, obj3);
NSLog(@"%@->%p", obj4, obj4);
//结果如下:
<CDSubObj: 0x6000036e42d0>->0x6000036e42d0
<CDSubObj: 0x6000036e42d0>->0x6000036e42d0
<CDSubObj: 0x6000036e42d0>->0x6000036e42d0
<CDSubObj: 0x6000036f0090>->0x6000036f0090
结论:可以明显看到obj1、2、3 的内存地址的一样的。而obj4 是一块单独的内存地址。
2、我们再来看看每个的指针地址又是如何的。
NSLog(@"%@->%p ->%p", obj1, obj1, &obj1);
NSLog(@"%@->%p ->%p", obj2, obj2, &obj2);
NSLog(@"%@->%p ->%p", obj3, obj3, &obj3);
NSLog(@"%@->%p ->%p", obj4, obj4, &obj4);
//结果:
<CDSubObj: 0x600002bf4110>->0x600002bf4110 ->0x16faa54f8
<CDSubObj: 0x600002bf4110>->0x600002bf4110 ->0x16faa54f0
<CDSubObj: 0x600002bf4110>->0x600002bf4110 ->0x16faa54e8
<CDSubObj: 0x600002be0130>->0x600002be0130 ->0x16faa54e0
结论:明显看到每个的指针地址也是不一样的。而且依次相差8字节,且是高地址(栈空间)
alloc 源码分析流程
上面看到了,alloc 确实是开辟了内存空间。而init 貌似什么都没做。
接下来从源码的角度来分析:这里需要准备一份编译好objc 源码:objc818
1、打开项目,初始化一个对象并且下一个断点:
![](https://img.haomeiwen.com/i1694726/2c9c0e11c89c1b2f.png)
2、点击alloc 方法,会跳转到NSObject.mm 里面的 +alloc 方法,里面调用了一个 _objc_rootAlloc()的方法
![](https://img.haomeiwen.com/i1694726/659a563b4cd43aeb.png)
3、继续进入:_objc_rootAlloc 方法里面调用了 callAlloc(),接下来看看callAlloc 到底干了什么事。
/// callAlloc 方法的源码:
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
if (slowpath(checkNil && !cls)) return nil;
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
#endif
// 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));
}
//(!cls->ISA()->hasCustomAWZ()) 这个第一次的时候 为false ,直接走了消息发送的流程。然后在回来执行 _objc_rootAllocWithZone()
4、_objc_rootAllocWithZone()
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
// allocWithZone under __OBJC2__ ignores the zone parameter
return _class_createInstanceFromZone(cls, 0, nil,
OBJECT_CONSTRUCT_CALL_BADALLOC);
}
// _objc_rootAllocWithZone里面只调用了 _class_createInstanceFromZone 这个方法。
_class_createInstanceFromZone 源码:
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;
//1、获取当前对象需要的内存大小
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
//2、调用calloc开辟内存
id obj;
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (slowpath(!obj)) {
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}
//3、绑定类名信息
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);
}
//4、返回对象
if (fastpath(!hasCxxCtor)) {
return obj;
}
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
return object_cxxConstructFromClass(obj, cls, construct_flags);
}
4.1: size = cls->instanceSize(extraBytes); 这个计算对象到底需要分配多少字节的内存。
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;
}
4.2 从缓存当中获取内存大小。这里有一个对其方式 为16对齐。
size_t fastInstanceSize(size_t extra) const
{
ASSERT(hasFastInstanceSize(extra));
if (__builtin_constant_p(extra) && extra == 0) {
return _flags & FAST_CACHE_ALLOC_MASK16;
} else {
size_t size = _flags & FAST_CACHE_ALLOC_MASK;
// remove the FAST_CACHE_ALLOC_DELTA16 that was added
// by setFastInstanceSize
return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
}
}
// (x + 15) & ~15
// 例如:x = 8; x + 15 = 23;
//23 : 0001 0111
//15 : 0000 1111
//~15 : 1111 0000
//23 & ~15 = 0001 0000 == 16
//这里就是16字节对齐原理
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
4.3: 处理字节对齐的情况
由于WORD_MASK 是7.
(x + WORD_MASK) & ~WORD_MASK; 这个就相当是8字节对齐;
这里的7字节对齐和15字节对齐原理是一样的。
uint32_t unalignedInstanceSize() const {
ASSERT(isRealized());
return data()->ro()->instanceSize;
}
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
define WORD_MASK 7UL
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
1)当我整个对象为空的时候:
@interface CDSubObj : NSObject
@end
//输出结果
(lldb) p cls
(Class) $0 = CDSubObj
(lldb) p size
(size_t) $1 = 16
- 当这个对象增加一些属性后
@interface CDSubObj : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@end
//输出结果
(lldb) p cls
(Class) $0 = CDSubObj
(lldb) p size
(size_t) $1 = 32
结论:可以看出内存的大小是按照16字节对齐的。
5、获取到需要开辟的内存后,调用 calloc 开辟内存
id obj;
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
6、调用 initInstanceIsa 绑定isa。然后返回对象
inline void
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
isa_t newisa(0);
if (!nonpointer) {
newisa.setClass(cls, this);
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
#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
# if ISA_HAS_CXX_DTOR_BIT
newisa.has_cxx_dtor = hasCxxDtor;
# endif
newisa.setClass(cls, this);
#endif
newisa.extra_rc = 1;
}
// 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;
}
结论:
整个alloc的流程大致就是NSObject +alloc->_objc_rootAlloc->callAlloc->hasCustomAWZ == false->objc_msgSend ;或者hasCustomAWZ == true->_objc_rootAllocWithZone->_class_createInstanceFromZone->instanceSize->calloc->initInstanceIsa
网友评论