黑马程序员--Objective-C——OC单例的实现

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一、单例的介绍

单例:在内存中只有唯一的实例,并且提供一个全局的访问方法。

OC中常见的单例有:

UIApplication

NSFileManager

NSUserdefaults

NotificationCenter

在IOS开发中,单例的应用非常广,甚至都有滥用的趋势。


二、单例的设计步骤

开发中有一个最常用的单例就是音乐或者音效播放,下面以音乐播放的例子介绍设计单例的思路和步骤:

1>新建一个类SoundTools继承NSObject


2>定义一个全局访问方法

<span style="background-color: rgb(255, 255, 255);">+ (instancetype)sharedSoundTools {
    return [[self alloc] init];
}</span>


如果对象遵守了NSCopying协议,还需要实现copyWithZone方法

<span style="background-color: rgb(255, 255, 255);">- (id)copyWithZone:(NSZone *)zone {
    id s = [[self.class allocWithZone:zone] init];
    return s;
}</span>



3>编写ViewController的viewDidLoad方法中编写测试代码

<span style="background-color: rgb(255, 255, 255);">- (void)viewDidLoad {
    [super viewDidLoad];
    
    SoundTools *s1 = [[SoundTools alloc] init];
    SoundTools *s2 = [[SoundTools alloc] init];
    SoundTools *s3 = [SoundTools sharedSoundTools];
    SoundTools *s4 = [s3 copy];
    NSLog(@"%@ %@ %@ %@", s1, s2, s3, s4);
}</span>



4>运行,此时当然地址会不一样

运行效果:

可以看到,上面的四个实例化方法都会调用内存分配方法alloc或者allocWithZone,由于历史原因,alloc内部会调用allocWithZone方法。因此,只要保证allocWithZone方法每次返回的都是同一个实例,就可以让以上的四个方法返回同一个对象了!


5>添加一个全局的实例对象,修改allocWithZone方法

<span style="background-color: rgb(255, 255, 255);">static SoundTools *instance;
​+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    if (instance == nil) {
        instance = [super allocWithZone:zone];
    }
    return instance;
}</span>



至此,我们已经实现了一个最简单的单例!

但是,如果涉及到多线程同时执行这段代码,可能会分配多个内存空间得到多个不同的实例!


6>修改allocWithZone方法

<span style="background-color: rgb(255, 255, 255);">+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [super allocWithZone:zone];
    });
    return instance;
}</span>


注:里面的dispatch_once方法可以保证在多线程运行的情况下,block里面的代码只会被执行一次,所以可以删除之前的判断语句。


7>修改copyWithZone方法

<span style="background-color: rgb(255, 255, 255);">- (id)copyWithZone:(NSZone *)zone {
    return instance;
}</span>


由于这是个对象方法,执行这个方法之前已经实例化了,所以可以直接返回实例化对象。

做个对比,如果我们将sharedSoundTools这个类方法也直接返回instance,会怎么样呢?

改变测试代码的执行顺序,将s3的实例化代码放在最前面,运行

发现s3和s4都是空,这是因为执行这个类方法时,还没有实例化的对象存在!


8>修改sharedSoundTools方法

<span style="background-color: rgb(255, 255, 255);">+ (instancetype)sharedSoundTools {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}</span>


注:为什么这里也要使用dispatch_once呢?如果不使用,会发生什么状况?instance可能会被多次初始化,因为前面只是保证了只alloc一次,并没有保证只init一次。这样,一个完整的单例基本上就实现了。

细心的人可能会发现,当我们不调用sharedSoundTools方法,而是调用[[SoundTools allocinit],还是可以实例化多次,如果要解决这个问题,就需要重写init方法,在init方法中再加一个dispatch_once。这样是不好的,因为静态区的资源是有限的,使用太多锁会造成资源的浪费。因此,单例规定:所有对单例的访问,都用share(或者default,strandard)方法。




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值