JHHK

欢迎来到我的个人网站
行者常至 为者常成

NSObject的本质(五) 创建对象

目录

alloc

alloc的调用流程图

img

alloc源码分析,参考下面的文章,从源码分析解释了流程调用

OC源码分析之对象的创建

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的,我们可以来了解下。

img

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>
*/

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.