前言
相信每一个iOS开发人员创建对象的时候都会写这样一行代码
NSObject *obj = [[NSObject alloc] init];
接下来就让已alloc
为例来查找一下苹果的源码实现。
一、查找alloc源码实现
要想了解alloc
的源码实现,咱们首先得找到alloc
的源码在哪个库里实现,找到这个库以后先下载这个库,然后咱们就可以看他的底层源码实现了。
找到alloc
所在的库有以下三种方法:
1、直接下代码断点
先在alloc
处打一个断点
control
按钮点击下图(图二)中的红色方框内的in按钮
图二
然后会进入到调用栈会发现调用了objc_alloc
图三
继续control+in
往下走走到objc_alloc
内部就会找到alloc
所在的库了
图四
这里的
libobjc.A.dylib
这个动态链接库就是我们想要找的包含alloc
源码的库了。
2、下符号断点
图五 图六通过上面图五、图六两步打一个
alloc
的符号断点,就可以看到alloc
所在的动态链接库
图七
读取这个地址可以看到就是我们自己创建的类
图八
注意:
程序运行前请先取消alloc
符号断点、现在自己写的代码的alloc
出打一个断点,等程序断在此处后再选中alloc符号断点进行下一步就可以定位到图七中的动态库了。
3、反汇编显示
具体操作方式为打开 Debug 菜单下的 Debug Workflow
下的 Always Show Disassembly
control+in
一步步下断点走到这
图十
至此,我们得到了
alloc
实现位于 libObjc
这个动态库,而libObjc
又属于objc4
,而刚好苹果已经开源了这部分的代码,所以我们可以在苹果开源官网上下载即可。最新的
objc4
为 781。
图十一
二、alloc流程分析
alloc流程分析- 点击对象的alloc方法进入源码实现
//alloc源码分析-第一步
+ (id)alloc {
return _objc_rootAlloc(self);
}
- 进入查看
_objc_rootAlloc
源码实现
// 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*/);
}
- 进入查看
callAlloc
源码实现
// Call [cls alloc] or [cls allocWithZone:nil], with appropriate
// shortcutting optimizations.
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
if (slowpath(checkNil && !cls)) return nil;
//类是否具有默认的alloc/allocWithZone:实现
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));
}
一般情况下,在我们自定义的类中如果没有重写+(instance)alloc
或+(instance)allocWithZone:(struct _NSZone *)zone
方法,默认走_objc_rootAllocWithZone
这个方法
- 进入查看
_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);
}
- 进入查看
_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;
// 1.计算需要开辟的内存大小,传入的extraBytes 为 0
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
// 2.申请内存
obj = (id)calloc(1, size);
}
if (slowpath(!obj)) {
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}
// 3.将 cls类 与 obj指针(即isa) 关联
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);
}
这部分源码为alloc
的核心,由上面的源码可以看到主要流程分三步:
- 计算需要开辟的内存的大小
size = cls->instanceSize(extraBytes);
- 申请内存
obj = (id)calloc(1, size);
- 将cls类与obj指针(即isa)关联
obj->initInstanceIsa(cls, hasCxxDtor);
三、init源码分析
进入查看init
源码实现
// Replaced by CF (throws an NSException)
+ (id)init {
return (id)self;
}
- (id)init {
return _objc_rootInit(self);
}
id
_objc_rootInit(id obj)
{
// In practice, it will be hard to rely on this function.
// Many classes do not properly chain -init calls.
return obj;
}
由上述源码可以看出init返回的是self
本身,这样做的原因是为了用户可以重写此方法做一些初始化操作
new 源码解析
一般在开发中,初始化除了init
,还可以使用new
,两者本质上并没有什么区别,以下是objc
中new
的源码实现,通过源码可以得知,new
函数中直接调用了callAlloc
函数(即alloc
中分析的函数),且调用了init
函数,所以可以得出[NSObject new]
其实就等价于[[NSObject alloc] init]
的结论。
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
但是一般开发中并不建议使用new
,主要是因为有时会重写init
方法做一些初始化操作,用new
创建对象可能会无法做到初始化一些东西。
网友评论