iOS 入门 016: 单例模式及其实现

1 min read Apr 17, 2012

iOS 入门 016: 单例模式及其实现

Table of Content

单例模式及其实现

  • 步骤

    • 提供全局访问方法,公认的格式 shared+类型

    • 有一个静态全局变量,保证对象的实例保存在静态区,并且不需要关心释放 的问题

    • 重写allocWithZone方法,能够保证对象只被实例化一次!

      • 需要包含dispatch_once:保证对象只被分配一次内存空间
    • 实现shared方法

      • 需要包含dispatch_once:保证对象只被初始化一次

eg.h

#import <Foundation/Foundation.h>
@interface SoundTools : NSObject <NSCopying>
// 全局的访问方法
+ (instancetype)sharedSoundTools;
@end

eg.m

@interface SoundTools()
@property (nonatomic, assign) int count;
@end

@implementation SoundTools
static id instance;

// 所有对象的内存分配,最终都会调用到此方法
// 把这个方法搞定,就能实现单例!
+ (instancetype)allocWithZone:(struct _NSZone *)zone {

    // 问题:这种方式不是线程安全的
    // 解决方法1:增加互斥锁
//    @synchronized (self) {
//        if (instance == nil) {
//            instance = [super allocWithZone:zone];
//        }
//    }
    // 其他解决办法?dispatch_once,执行一次,线程安全!
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (instance == nil) {
            instance = [super allocWithZone:zone];
        }
    });
    return instance;
}

+ (instancetype)sharedSoundTools {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 使用shared方法只能做一次初始化!
        instance = [[self alloc] init];
    });
     instance;
}

//------------- 以上部分是面试的时候,需要手写单例的代码------
- (id)copyWithZone:(NSZone *)zone {
     instance;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        _count = 10;
        NSLog(@"初始化 %d", _count);
    }
     self;
}
@end

MRC中的单例的实现

  • 先参考ARC中单例的实现部分

eg.m

#pragma mark - MRC模式中需要增加的代码
- (oneway void)release {
    // 原本的程序是做计数-1
}

- (instancetype)retain {
    return instance;
}

- (instancetype)autorelease {
    return instance;
}

- (NSUInteger)retainCount {
	// retainCount返回最大整数,此方法可以不重写
    // 出处 limits.h ULONG_MAX 宏可以自行根据运行的CPU架构调整整数的长度
    return ULONG_MAX;
}

抽取单例宏

Singleton.h

#define singletonInterface(className) +(instancetype)shared##className;
#if __has_feature(objc_arc)
// 以下是ARC版本
#define singletonImplementation(className) \
+ (instancetype)allocWithZone:(struct _NSZone *)zone { \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
        if (instance == nil) { \
            instance = [super allocWithZone:zone]; \
        } \
    }); \
    return instance; \
} \
+ (instancetype)shared##className { \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
        instance = [[self alloc] init]; \
    }); \
    return instance; \
} \
- (id)copyWithZone:(NSZone *)zone { \
    return instance; \
}
#else
// 以下是MRC版本
#define singletonImplementation(className) \
+ (instancetype)allocWithZone:(struct _NSZone *)zone { \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
        if (instance == nil) { \
            instance = [super allocWithZone:zone]; \
        } \
    }); \
    ⏎ instance; \
} \
+ (instancetype)shared##className { \
    static dispatch_once_t onceToken; \
        dispatch_once(&onceToken, ^{ \
        instance = [[self alloc] init]; \
    }); \
    ⏎ instance; \
} \
- (id)copyWithZone:(NSZone *)zone { \
    ⏎ instance; \
} \
- (oneway void)release {} \
- (instancetype)retain {⏎ instance;} \
- (instancetype)autorelease {⏎ instance;} \
- (NSUInteger)retainCount {⏎ ULONG_MAX;}
#endif
// 提示末尾一行不要有 \

eg.h

#import <Foundation/Foundation.h>
#import "Singleton.h"
@interface SoundTools : NSObject <NSCopying>
// 全局的访问方法
//+ (instancetype)sharedSoundTools;
singletonInterface(SoundTools);
@end

eg.m

#import "SoundTools.h"
@interface SoundTools()
/** 声音的数量 */
@property (nonatomic, assign) int count;
@end

@implementation SoundTools
static id instance;
singletonImplementation(SoundTools);
#pragma mark - MRC 部分代码
- (instancetype)init
{
    self = [super init];
    if (self) {
        _count = 10;
        NSLog(@"初始化 %d", _count);
    }
    return self;
}
@end
Picsew | Ezra