在开发过程中 我们一般使用 [[NSObject alloc] init]
的方式来创建一个对象,那么在 调用 alloc
方法是,系统都是做了什么呢?接下来我们来一探究竟。
此处可以获得objc源码 打开源码,我们使用源码进行分析。
这里我自定义一个Person
类,继承自NSObject
。
#import <Foundation/Foundation.h>
#import "Person.h"
#import <objc/runtime.h>
#import <malloc/malloc.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person* person1 = [Person alloc];
Person* person2 = [Person alloc];
NSLog(@"%@-%lu-%lu-%lu",
person1,
class_getInstanceSize([person1 class]) ,
sizeof(&person1),
malloc_size((__bridge const void *)(person1)) );
}
return 0;
}
打印内容
<Person: 0x1007164a0>-8-8-16
最开始,调用 alloc方法 断点进去 会调用到
+ (id)alloc {
return _objc_rootAlloc(self);
}
再接着是
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
接着断点来到了
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));
}
这里,之后是
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);
}
方法内部是调用 _class_createInstanceFromZone(cls, 0, nil, OBJECT_CONSTRUCT_CALL_BADALLOC)
方法。 我们来到此方法的内部这就是重点了;
方法具体的实现如下:
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); // 1
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size); // 2
}
if (slowpath(!obj)) {
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}
if (!zone && fast) {
obj->initInstanceIsa(cls, hasCxxDtor); // 3
} 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);
}
方法内部 最重要的是以下三部分内容:
- 首先,会通过
cls->instanceSize(extraBytes)
来计算出需要的内存空间大小; - 接着,使用
(id)calloc(1, size)
来向系统申请开辟内存空间,返回地址指针; - 最后,使用
obj->initInstanceIsa(cls, hasCxxDtor)
来将类和指针做绑定。
其中在计算内存空间大小时,会调用 cache.fastInstanceSize(extraBytes)
方法,
最终会调用 align16(size + extra - FAST_CACHE_ALLOC_DELTA16)
方法
align16
的实现如下:
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
此方法其实是 系统进行 16字节 的对齐操作, 也就是说, 一个对象所占用的内存大小至少是16字节。
网友评论