iOS开发之单例模式

在讲到GCD的其他用法的时候,其中有一个用法是让代码在程序运行期间只执行一次。这种功能可以帮助我们实现iOS开发中的一种设计模式:单例模式。

单例模式

一个类在整个程序运行过程中只有一个实例。

单例模式的作用

可以保证在程序运行过程中,一个类只有一个实例,而且该实例易于供外界访问。从而方便第控制了实例个数,并节约系统资源。

单例模式使用场合

在整个应用程序中,共享一份资源(这份资源只需要创建初始化一次)

单例模式在ARC和非ARC环境下得写法有所不同,需要编写两套不同的代码。如果将单例写成宏的形式,可以使用__has_feature(objc_arc)宏判断是否为ARC环境。

#if __has_feature(objc_arc)
// 是ARC
#else
// 非ARC
#endif

ARC中的单例实现

1.    在.m文件中保留一个全局的static的实例

static id _instance

2.    重写allocWithZone:方法(alloc方法内部会自动执行该方法,在该方法中分配唯一内存),在这里创建唯一的实例(注意线程安全)

+ (id)allocWithZone:(struct _NSZone *)zone
{
    // 里面的代码永远只执行1次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });
    
    // 返回对象
    return _instance;
}

或者:

+ (id)allocWithZone:(struct _NSZone *)zone
{
    // 里面的代码永远只执行1次
    @synchronized(self) {// 加锁,防止别的线程同时访问
	if (!_instance) {
	    _instance = [super allocWithZone:zone];
        }
    }
    // 返回对象
    return _instance;
}

3.    提供一个类方法让外界获得唯一的实例

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

或者:

+ (instancetype)sharedInstance
{
    @synchronized(self) {
	if (!_instance) {
	     _instance = [[self alloc] init];
        }
    }
    return _instance;
}

4.    再实现copy方法(copy方法内部会自动执行copyWithZone:方法,因为实例就一个只要返回自身即可)

+ (id)copy
{
    return _instance;
}

ARC中的单例实现

在非ARC中(MRC),单例模式的实现除了实现上面的代码之外,还要比ARC多出几个步骤。

在非ARC环境下,需要我们自己管理内存,需要实现以下内存管理方法:

- (id)retain {
   return self;// 或return _instance
}
- (NSUInteger)retainCount {
   return 1;
}
- (oneway void)release {
}
- (id)autorelease {
   return self;// 或return _instance
}


下面我们就用一个例子来实现一下载ARC和非ARC下的单例

场景:现在在项目中有10个控制器页面需要播放同一段音乐。要是每个控制器器里面都创建一个新的对象来播放该音乐,就显得太繁琐,内存占用也大。那么这个时候就可以使用单例模式来解决这个问题。

创建一个HXMusicTool类(整个应用程序中只有一个实例对象),该类的作用是播放一段音乐。( ARC

2.    在HXMusicTool.m文件中实现和重写以下方法

#import "HXMusicTool.h"

@implementation HXMusicTool
// 声明一个全局的实例变量
static id _instance;

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

/**
 *  重写allocWithZone:方法(alloc方法中会自动调用该方法)来分配唯一的内存空间
 */
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    
    // block中的代码只会执行一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });
    
    // 返回分配内存后的对象
    return _instance;
}

/**
 *  重写copy方法,保证copy出来的对象也是实例对象
 */
+ (id)copy
{
    return _instance;
}
@end

3.    在HXMusicTool.h文件中提供shareMusicTool外部接口,供外界访问实例对象

#import <Foundation/Foundation.h>

@interface HXMusicTool : NSObject

// 外部接口,供外界访问实例变量
+ (instancetype)shareMusicTool;

@end

4.    在ViewController中使用HXMusicTool类

#import "ViewController.h"
#import "HXMusicTool.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // tool1,tool2通过alloc创建
    HXMusicTool *tool1 = [[HXMusicTool alloc] init];
    HXMusicTool *tool2 = [[HXMusicTool alloc] init];
    
    // tool3,tool4通过提供的外部接口创建
    HXMusicTool *tool3 = [HXMusicTool shareMusicTool];
    HXMusicTool *tool4 = [HXMusicTool shareMusicTool];

    HXMusicTool *tool5 = [HXMusicTool copy];
    
    // 打印四个对象的内存地址
    NSLog(@"\n1 = %p\n2 = %p\n3 = %p\n4 = %p\n5 = %p", tool1, tool2, tool3, tool4, tool5);
}

@end

打印结果:


相同的内存地址。说明四个对象是一个实例。这就做到了在整个应用程序中,不管使用什么方法来创建对象都是同一个实例对象。

在非ARC环境下实现上面的需求,只需要在HXMusicTool.m文件中再实现以下方法即可

- (id)retain {
    return self;// 或return _instance
}
- (NSUInteger)retainCount {
    return 1;
}
- (oneway void)release {
}
- (id)autorelease {
    return self;// 或return _instance
}

这就保证了在程序运行期间,实例对象一旦创建就会一直存在,且唯一。

现在项目的开发大部分都在ARC和非ARC混合环境下进行的,那么单例模式如何同时适用ARC和非ARC环境。

下面就提供一种解决方案:在实现单例模式的代码写成宏,再使用宏判断当前是否是ARC环境,并在不同环境下实现不同的代码。

以下是同时适用ARC和非ARC的单例模式(宏)

// ## : 连接字符串和参数
#define singleton_h(name) + (instancetype)shared##name;

#if __has_feature(objc_arc) // ARC

#define singleton_m(name) \
static id _instance; \
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
        _instance = [super allocWithZone:zone]; \
    }); \
    return _instance; \
} \
 \
+ (instancetype)shared##name \
{ \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
        _instance = [[self alloc] init]; \
    })
    return _instance; \
} \
+ (id)copyWithZone:(struct _NSZone *)zone \
{ \
    return _instance; \
}

#else // 非ARC

#define singleton_m(name) \
static id _instance; \
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [super allocWithZone:zone]; \
}); \
return _instance; \
} \
\
+ (instancetype)shared##name \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [[self alloc] init]; \
}); \
return _instance; \
} \
\
- (oneway void)release \
{ \
\
} \
\
- (id)autorelease \
{ \
return _instance; \
} \
\
- (id)retain \
{ \
return _instance; \
} \
\
- (NSUInteger)retainCount \
{ \
return 1; \
} \
\
+ (id)copyWithZone:(struct _NSZone *)zone \
{ \
return _instance; \
}

#endif

注意:后面添加\是说明下一行也是宏的一部分

这就是iOS开发单例设计模式的实现,其实IOS开发中的设计模式还有很多,其他的设计模式以后会慢慢讲到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值