目录
alloc
alloc的调用流程图
alloc源码分析,参考下面的文章,从源码分析解释了流程调用
init和new
看下init的源码
- (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仅仅是将alloc创建的对象返回。为什么这样设计呢?其实并不难理解,在平时的开发中,我们常常会根据业务需求重写init,进行一些自定义的配置.
NSObject的init是一种工厂设计方案,方便子类重写。
看下new的源码
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
很明显,new相当于alloc+init。
alloc与allocWithZone
给对象分配空间有两个方法:
//常用
+ (id)alloc;
//不在建议使用
+ (id)allocWithZone:(struct _NSZone *)zone;
在NSObject这个类的官方文档里面,allocWithZone方法介绍说,该方法的参数是被忽略的,正确的做法是传nil或者NULL参数给它。而这个方法之所以存在,是历史遗留原因。
在《Objective-C高级编程:iOS与OS X多线程和内存管理》这本书中14页有一个专栏是介绍Zone的,我们可以来了解下。
allocWithZone不被Apple鼓励使用,基本上多数时候程序员也不需要自己去管理自己的zone。当然多了解一些东西总是好的嘛。
但当我们创建一个单例类时,假如有人调用了allocWithZone:是否还能保证我们这个类是一个单例类呢?下面我们用代码来看下。
创建一个Person类
@interface Person : NSObject
+(instancetype)shareInstance;
@end
+(instancetype)shareInstance{
static Person * person = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
person = [[Person alloc] init];
});
return person;
}
调用,从lldb的调试结果可以看出person、person1、person2是三个不同的实例对象。只有person和person3是同一个对象。
Person * person = [Person shareInstance];
Person * person1 = [[Person allocWithZone:NULL] init];
Person * person2 = [[Person alloc] init];
Person * person3 = [Person shareInstance];
/**
lldb的调试结果:
(lldb) po person
<Person: 0x13ce9e000>
(lldb) po person1
<Person: 0x13ce9e090>
(lldb) po person2
<Person: 0x13ce9dae0>
(lldb) po person3
<Person: 0x13ce9e000>
*/
要想避免上述现象,就需要把alloc和allocWithZone这两条路堵上。
调试时发现,如果重写了allocWithZone方法,调用alloc时就会调用我们重写的allocWithZone。那么我们就可以在allocWithZone这个方法内来统一处理这个问题。
处理思路如下,这样就能保证Person是一个真正的单例类。
static Person * person = nil;
+(instancetype)shareInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
person = [[self alloc] init];
});
return person;
}
+(instancetype)allocWithZone:(struct _NSZone *)zone{
if (!person){
person = [super allocWithZone:zone];
}
return person;
}
Person * person = [Person shareInstance];
Person * person1 = [[Person allocWithZone:NULL] init];
Person * person2 = [[Person alloc] init];
Person * person3 = [Person shareInstance];
或者调换顺序
//Person * person1 = [[Person allocWithZone:NULL] init];
//Person * person = [Person shareInstance];
//Person * person2 = [[Person alloc] init];
//Person * person3 = [Person shareInstance];
/**
lldb的调试结果:
(lldb) po person
<Person: 0x126db9610>
(lldb) po person1
<Person: 0x126db9610>
(lldb) po person2
<Person: 0x126db9610>
(lldb) po person3
<Person: 0x126db9610>
*/
行者常至,为者常成!