严谨的单例写法(转)

单例模式在iOS开发中可能算是最常用的模式之一了,但是由于OC本身的语言特性,想要写一个正确的单例模式相对来说比较麻烦. 今天就来说一说, 单例创建的方式和严谨的单例写法及可继承单例编写.

基本单例的创建方式

方式一(普通创建方式)

SingleHandle.h

@interface SingleHandle : NSObject

//单例创建方法一般以 share, stand, main 开头 + 当前类名
+(SingleHandle *)shareSingleHandle;

@end
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

SingleHandle.m

@implementation SingleHandle

//声明静态变量
static SingleHandle *singlehanle = nil;

+(SingleHandle *)shareSingleHandle
{
    //同步锁  //防止一种极限的可能,第一个对象正在创建的时候,第二个对象就开始创建了,造成两个对象
    @synchronized(self){
        if (singlehanle == nil) {
            singlehanle = [[SingleHandle alloc]init];
        }
        return singlehanle;
    }
}
@end
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

方式二(GCD 方式)

Singleton.h

@interface Singleton : NSObject

+(instancetype) shareInstance;

@end
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

Singleton.m

@implementation Singleton

static Singleton* instance = nil;

+(instancetype) shareInstance
{
    static dispatch_once_t onceToken ;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init] ;
    }) ;

    return instance ;
}

@end
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

严谨的单例写法探究

一般写法

一般情况下,可能我们写的单例模式是上面的方式, 这里不再赘述, 但这样是不严谨的. 先来看一下到底不严谨的写法缺点是什么. 
以上面的方式二举例:

    Singleton* single1 = [Singleton shareInstance] ;
    NSLog(@"single1 = %@", single1) ;

    Singleton* single2 = [Singleton shareInstance] ;
    NSLog(@"single2 = %@", single2) ;

    Singleton* single3 = [[Singleton alloc] init] ;
    NSLog(@"single3 = %@", single3) ;

    NSLog(@"single4 = %@", [single3 copy]) ;
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

打印结果:

single1 = < Singleton: 0x7ffb93743e80 > 
single2 = < Singleton: 0x7ffb93743e80 > 
single3 = < Singleton: 0x7ffb9373ecc0 > 
-[Singleton copyWithZone:]: unrecognized selector sent to instance 0x7ffb9373ecc0

可以看到,当我们调用shareInstance方法时获取到的对象是相同的,但是当我们通过alloc和init来构造对象的时候,得到的对象却是不一样的。而且在 single4 这行注释打开后, 会在此崩溃, 原因如上. 

所以,我们通过不同的途径得到不同的对象,显然是不行的。我们必须要确保对象的唯一性,所以我们就需要封锁用户通过alloc和init以及copy来构造对象这条道路。

我们知道,创建对象的步骤分为申请内存(alloc)、初始化(init)这两个步骤,我们要确保对象的唯一性,因此在第一步这个阶段我们就要拦截它。当我们调用alloc方法时,OC内部会调用allocWithZone这个方法来申请内存,我们覆写这个方法,然后在这个方法中调用shareInstance方法返回单例对象,这样就可以达到我们的目的。拷贝对象也是同样的原理,覆写copyWithZone方法,然后在这个方法中调用shareInstance方法返回单例对象。

严谨的写法

@implementation Singleton

static Singleton* instance = nil;

+(instancetype) shareInstance
{
    static dispatch_once_t onceToken ;
    dispatch_once(&onceToken, ^{
        instance = [[super allocWithZone:NULL] init];
    }) ;

    return instance ;
}
+(id) allocWithZone:(struct _NSZone *)zone
{
    return [Singleton shareInstance] ;
}
-(id) copyWithZone:(struct _NSZone *)zone
{
    return [Singleton shareInstance] ;
}
@end
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

打印结果:

single1 = < Singleton: 0x7fe2e24a2880 > 
single2 = < Singleton: 0x7fe2e24a2880 > 
single3 = < Singleton: 0x7fe2e24a2880 > 
single4 = < Singleton: 0x7fe2e24a2880 >

这样就是比较正确很严谨的写法了.

可继承单例探究

可继承单例是指父类中写下单例创建的方法, 当其本身类或其子类调用父类中的类创建的方法时, 可以各自类创建各自类的单例. 所以, 在父类中写的一个方法, 同时适用于其本身和其子类, 故称作可继承单例.

单例类 A :

@interface A : NSObject
@property (nonatomic,copy)NSString *a1;

+ (instancetype)sharedInstance;

@end


#import "A.h"
#import <objc/runtime.h>
@implementation A

+(instancetype)sharedInstance
{
    id instance = objc_getAssociatedObject(self, @"instance");

    if (!instance)
    {
        instance = [[super allocWithZone:NULL] init];
        NSLog(@"单例创建=====%@=====",instance);
        objc_setAssociatedObject(self, @"instance", instance, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return instance;
}
+(id) allocWithZone:(struct _NSZone *)zone
{
    return [self sharedInstance] ;
}
-(id) copyWithZone:(struct _NSZone *)zone
{
    Class selfClass = [self class];
    return [selfClass sharedInstance] ;
}
@end
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

单例类 B 和单例类 C 中无任何方法和属性, 只是继承于A类. 

执行下面的方法:

    A *singleA = [A sharedInstance];
    B *singleB = [B sharedInstance];
    C *singleC = [C sharedInstance];

    singleA.a1 = @"aaa";
    singleB.a1 = @"bbb";
    singleC.a1 = @"ccc";

    NSLog(@"singleA = %p",singleA);
    NSLog(@"singleB = %p",singleB);
    NSLog(@"singleC = %p",singleC);

    NSLog(@"singleA.a1 = %@",singleA.a1);
    NSLog(@"singleB.b1 = %@",singleB.a1);
    NSLog(@"singleC.c1 = %@",singleC.a1);
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

打印结果:

单例创建=====< A: 0x7fc21282fb60 >===== 
单例创建=====< B: 0x7fc21282fcb0 >===== 
单例创建=====< C: 0x7fc210427190 >===== 
singleA = 0x7fc21282fb60 
singleB = 0x7fc21282fcb0 
singleC = 0x7fc210427190 
singleA.a1 = aaa 
singleB.a1 = bbb 
singleC.a1 = ccc

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值