一、基本知识
1. 进程
- 系统中正在运行的一个程序
- 每个进程之间相互独立「运行在其专用且受保护的内存空间内」
- 进程之间可以相互通讯
2. 线程
- 1 个进程想执行任务,必须得有 1 条线程「每个进程至少有一条线程」
- 1 个进程所有的任务都在线程里执行
- 1 个线程的任务的执行都是串行「按顺序多个任务,同一时间内 1个线程只执行 1个任务」
I. 线程的状态
- 启动线程
-(void)start;
- 阻塞线程
// 让线程阻塞多久不做任务
+ (void)sleepUntilDate:(NSDate *)date;
// 示例 1:
[NSThread sleepUntilDate: [NSDate distanceFuture]]; //永远阻塞「当前语句所在的线程」
[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow:2]]; //阻塞 2秒「单位是秒,当前语句所在的线程」
+ (void)sleepForTimeInterval:(NSTimeInterval) ti;
// 示例2:
[NSThread sleepForTimeInterval:2]; //阻塞 2秒「单位是秒,当前语句所在线程」
- 强制停止线程
+(void)exit;
注意:一旦线程死亡了,不能在开启任务,只能重新创建新的线程来完成任务
II. 线程的通信
定义:
- 1个线程传递数据给另 1个线程
- 在 1个线程中执行完成特定任务后,转到 另一个线程 继续 执行任务
线程间的通信方法:
// 跳转回 主线程 的 哪个方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
// 跳转到 哪个线程的 哪个方法
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
// 利用 NSPort(端口对象) 通讯「了解」
// A线程操作 B线程,需要 B将 1个Port对象 给A,A通过 Port对象 操作 B
3. 多线程「掌握」
定义:1个进程 开启多条线程,每条线程可以 并行「同时」执行不同的任务
原理:
- 同一时间 CPU只处理 1条线程
- 多线程并发「同时」执行,其实是 CPU快速的在多条线程间调度「切换」
优点:适当提高 程序执行效率 和 资源利用率「CPU、内存利用率」
缺点:
- 创建线程有开销「创建线程大约需要90毫秒的创建时间」
iOS的主要开销- 内核数据结构「大约 1KB」
- 栈空间「子线程 521KB,主线程 1MB」
- 线程过多 会降低程序性能
- 线程过多 CPU在线程上的开销越大
- 使用多线程,程序设计更加复杂「线程通信、多线程数据共享」
- 多个线程访问同一个资源的时候会引发 数据错乱 和 数据安全 问题
I. 主线程
定义:
- 程序运行后,默认开启 1条线程叫主线程「UI线程」
- 主要作用:显示、刷新UI界面、处理UI事件
注意:耗时操作放在主线程会卡住主线程,严重影响UI流畅度
II. 互斥锁「使用线程同步技术」
线程同步 定义:多条线程在同一条线上执行「按顺序的执行任务」
互斥锁 格式:@synchronized ( 锁对象 ){ 需要锁定的代码 }
注意:
- 使用前提:多线程共同抢夺同一个线程
- 锁对象可以是任意对象「一般都是 self,即调用此方法的单例对象本身」
- 锁定 1份代码,多个线程共用 1个锁对象「多个锁对象无效」
优点:防止多线程抢夺资源造成的数据安全问题
缺点:需要消耗大量的 CPU资源「一般不提倡使用」
III. 原子和非原子属性
在 @property 的属性中有
- 原子属性
atomic
:为 setter方法加同步锁「@property 属性 默认是 加锁」
特点:线程安全,但需要消耗大量的资源 - 非原子属性
nonatomic
:不加 同步锁
特点:非线程安全,适合内存小的移动设备
注:加锁、资源抢夺一般交给服务器处理,减少客户端压力
二、iOS中多线程的实现方案
1. pthread「了解」
// 创建线程
#import<pthread.h>
pthread_t thread;
pthread_create(pthread_t *restrict, const pthread_attr_t *restrict, void *(*)(void *), void *restrict);
2. NSThread 「掌握」
I. 基本用法
-
+ (NSThread *)currentThread;
获得当前线程 -
+ (NSThread *)mainThread;
获得主线程 -
- (BOOL)isMainThread;
查看 当前方法所在的类 是否为主线程 -
+ (BOOL)isMainThread;
查看 当前方法所在的方法 是否为主线程
II. 创建和启动线程
- 1 个NSThread对象就是 1条线程
- 虽然这里的线程是局部变量,可是当线程开启后,线程会直到任务完成后销毁
- 线程任务完成后会处于消亡状态,不能再次开启。只能重新创建新的线程在执行任务。
// 方法1. 创建线程
NSThread *thread_1 = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"run方法的参数,obj类型"];
// 以下这步可以省略
thread_1.name = @"这条线程的名字";
// 启动线程「线程一启动,就会在线程thread中执行 self的 run方法」
[thread start];
// 注:以下两种方法 不返回线程对象
// 优点:简单快捷
// 缺点:无法对线程进行详细的设置
// 方法2. 创建后自动分离出线程,并启动
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"run方法的参数, obj类型"];
// 方法3. 隐式创建,并启动后台线程
[self performSelectorInBackground:@selector(run) withObject:nil];
3. GCD「Grand Central Dispatch 宏大的中枢调度器」
优势
- 纯 C 语言有许多强大的函数
- 进行了 iOS 系统级的优化
- 针对多核并行运算提出的解决方案「会自动利用更多的CPU内核」
- 自动管理线程的生命周期「创建线程、调度任务、销毁线程」
使用步骤
- 创建队列或使用系统提供的队列
- 定制任务「线程操作」
- 将任务添加到 队列「存放任务」中
1) 任务
任务的执行方式「GCD会自动从队列中取出任务,放到对应线程中执行」
I. 同步任务「synchronize」
简介
- 只能在当前线程中执行任务,不能 擅自开启新线程
- 注:当前任务中 添加了同步函数,先执行完 同步函数,后执行 同步函数后的当前任务操作
在当前队列添加 同步任务,会阻塞当前队列
执行步骤
- 阻塞当前线程,保持和当前线程的同步
- 执行同步任务中的任务
- 回到当前队列,使当前队列不在阻塞,执行当前队列的任务
示例结果:1、2、3
// block:需要执行的任务
// dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
NSLog(@"1. %@", [NSThread currentThread]);
dispatch_queue_t newQueue = dispatch_queue_create("test.testName.queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(newQueue, ^{
NSLog(@"2. %@", [NSThread currentThread]);
});
NSLog(@"3. %@", [NSThread currentThread]);
II. 异步任务「asynchronous」
简介
- 可以在新的线程执行任务,能 擅自开启新线程
- 注:当前任务中 添加异步函数,先执行完 当前任务,后 执行异步函数
执行步骤
- 执行当前线程「不会阻塞当前线程」
- 执行异步任务
示例结果:1、3、2
// dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
// 每个有block的函数都有对应的 function函数
// dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work);
NSLog(@"1. %@", [NSThread currentThread]);
dispatch_queue_t newQueue = dispatch_queue_create("test.testName.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(newQueue, ^{
NSLog(@"2. %@", [NSThread currentThread]);
});
NSLog(@"3. %@", [NSThread currentThread]);
2)队列
I. 串行队列「Serial Dispatch Queue」
- 任务一个接着一个执行
- 代表:**主线程队列 **「不受 retain 和 release 的影响」
放到主队列中的任务,会在主线程,UI界面的线程中执行
// 1. 获得主队列
dispatch_queue_t qu = dispatch_get_main_queue();
// 2. 将任务加入队列
// 2.1 主队列+异步函数:串行执行,不能开启新线程
dispatch_async(qu, ^{ /*任务*/ });
// 2.2 主队列+同步函数:串行执行,不能开启新线程「如果执行的函数也在主队列则,队列阻塞,都无法执行」
dispatch_sync(qu, ^{ /*任务*/ });
- 自己创建 1个串行队列,队列优先级默认为
DISPATCH_QUEUE_PRIORITY_DEFAULT
// 1. 创建 1个串行队列
// label: 队列名称「格式: 作用.名称.queue」,队列名称会在 CrashLog 里标明崩溃的队列名称
// dispatch_queue_attr_t: 队列类型「并发/同步」
// dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
// 属性:DISPATCH_QUEUE_SERIAL 等价于 NULL
dispatch_queue_t qu = dispatch_queue_create("down.Miao.queue", DISPATCH_QUEUE_SERIAL);
// 2. 将任务加入队列
// 2.1 串行队列+异步函数:不能并发,可以开启多线程
// 异步函数:任务执行完成后,不等待 任务执行的队列结束就返回得出的结果
dispatch_async(qu, ^{ /*任务*/ });
// 2.2 串行队列+同步函数:能并发,不会开启新线程
// 同步函数:任务执行完成后,等待 任务执行的队列结束就返回得出的结果
dispatch_sync(qu, ^{ /*任务*/ });
// 3. 自己创建的队列需要手动释放「MRC模式」
dispatch_release(qu); // 当然 也有 dispatch_retain() 方法
II. 并发队列「Concurrent Dispatch Queue」
- 让多个任务 并发(同时) 进行「自动开启多线程同时执行任务」
并发只有在 异步函数dispatch_async
下有效 - 代表:全局并发队列 「不受 retain 和 release 的影响」
- 所有应用程序都能使用并发队列
// 有 4 个执行优先级:
// - 高 :DISPATCH_QUEUE_PRIORITY_HIGH
// - 默认:DISPATCH_QUEUE_PRIORITY_DEFAULT
// - 低 :DISPATCH_QUEUE_PRIORITY_LOW
// - 后台:DISPATCH_QUEUE_PRIORITY_BACKGROUND
dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
// 不是自己创建的对象无法手动释放
- 自己创建 1个并发队列
// 1. 创建 1个并发队列
dispatch_queue_t qu = dispatch_queue_create("com.Miao.queue", DISPATCH_QUEUE_CONCURRENT);
// 2. 将任务加入并发队列
// 2.1 并行队列+异步函数:可以并发,可以开启新线程
dispatch_async(qu, ^{ /*任务*/ });
// 2.2 并行队列+同步函数:不能并发,不会开启新线程
dispatch_sync(qu, ^{ /*任务*/ });
III. 变更线程的优先级
- 使用
dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);
多个串行队列的优先级都变更到同一队列的优先级,会导致
原本互相可以并行的多个串行队列,在目标队列上只能同时执行一个处理
dispatch_set_target_queue(/*要设置优先级的对列*/, /*与期望的优先级相同的队列*/);
IV. 线程通讯
示例:下载图片
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 1. 耗时的异步操作
// 获取图片网络路径
NSURL *url = [NSURL URLWithString:@"图片网络路径/图片名.图片格式"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL: url];
// 生成图片
UIImage *image = [UIImage imageWithData:data];
// 2. 回到主线程「最好用异步函数」
dispatch_async( dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
3)信号量
信号:多线程中的计数器,计数为 0 时线程等待,计数为 1 或者大于 1 时,减去 1 而不等待
示例:由于信号量为 10 在这个并行队列里,最多会有 10 个任务被执行
// 创建信号量,其值设为 10
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
for (int i = 0; i < 100; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 超时后,信号量会失效
// 信号量为 0,线程被阻塞;信号量 不为 0,信号总量 -1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"__%d__",i);
// 休眠
[NSThread sleepForTimeInterval:3];
// 信号量 +1
dispatch_semaphore_signal(semaphore);
});
}
4) GCD 其他方法
I. 障碍函数
使用限制
// 此方法阻塞的是 传入的 queue「不是当前线程」
// 参数 queue 不能是 全局的并发队列
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
执行步骤
- 阻塞传入的线程 queue
- queue 前面的任务执行完毕,执行 障碍函数
- 取消对线程 queue 的阻塞
dispatch_async(queue, ^{ /* 读 */ });
dispatch_async(queue, ^{ /* 读 */ });
dispatch_barrier_async(queue, ^{ /* 并行读入完成,开始并行写入 */ });
dispatch_async(queue, ^{ /* 写 */ });
dispatch_async(queue, ^{ /* 写 */ });
II. 延时函数「不影响现有的线程继续执行」
- NSObject 方法:
[self performSelector:@selector(函数1:) withObject:函数1的参数 afterDelay: 2.0];
延迟 2秒 - NSTimer 方法:
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(函数1:) userInfo:nil repeats:NO];
- GCD 函数「非循环」:
dispatch_after( dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)),
// 2秒后到主线程执行任务
dispatch_get_main_queue(), ^{
// 2秒后异步执行这里的代码...
});
- GCD 定时器「循环」:GCD 的定时器不受 RunLoop 的 Mode影响
int count = 0;
// 1. 获得队列
// dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_queue_t queue = dispatch_get_main_queue();
// 2. 创建一个定时器(dispatch_source_t本质还是个OC对象)
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 3. 设置定时器的各种属性(几时开始任务,每隔多长时间执行一次)
// GCD的时间参数,一般是纳秒(1秒 == 10的9次方纳秒)
// 何时开始执行第一个任务
// dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC) 比当前时间晚3秒
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC);
dispatch_source_set_timer(self.timer, start, interval, 0);
// 4. 设置回调
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"------------%@", [NSThread currentThread]);
count++;
if (count == 4) {
// 6. 取消定时器
dispatch_cancel(self.timer);
self.timer = nil;
}
});
// 5. 启动定时器
dispatch_resume(self.timer);
III. 一次性代码「保证代码在程序运行中,只执行一次」
static dispatch_one_t onceToken; // 标记是否执行的过
dispatch_once(&onceToken, ^{ /*要只执行一次的代码块,线程安全的已经加锁了*/ });
IV. 快读迭代「可以多线程同时遍历」
dispatch_queue_t qu = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 10为遍历的数组长度
dispatch_apply(10, qu,^(size_t index){
// 遍历需要的操作
});
VI. 队列组
作用
- 首先,异步执行多个耗时操作
- 其次,等之前的操作全部完毕后,再回到主线程执行操作
示例:
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的异步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行2个耗时的异步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步操作都执行完毕后,回到主线程...
});
4. NSOperation「底层是GCD,做了面向对象的封装」
作用
- 配合使用 NSOperation 和 NSOperationQueue 也能实现多线程编程
- NSOperation 是抽象类,并不具备封装操作的能力,必须使用他的子类
1)NSOperation 的子类「了解」
I. NSInovcationOperation
注意:
- 默认,调用 start方法后 不会开启一条新线程执行,会在当前线程同步执行操作
- 只有将 NSOperation 放到 NSOperationQueue 里,才会执行异步操作
// 创建NSInvocationOperation对象
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
// 调用start方法开始执行操作
// 一旦执行操作,就会调用target的sel方法
- (void)start;
II. NSBlockOperation
注意:只要NSBlockOperation封装的操作数 > 1,就会异步执行操作
// 创建NSBlockOperation对象
// 默认在当前线程执行
+ (id)blockOperationWithBlock:(void (^)(void))block;
// 通过addExecutionBlock:方法添加更多的操作
// 添加额外的任务会开启新的线程异步执行
- (void)addExecutionBlock:(void (^)(void))block;
III. 自定义类「继承自NSOperation」
- 要实现 NSOperation中的
-(void)main
将自定义类添加到 NSOperationQueue 中时会自动调用 实现的main函数
其中,要自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池) -
-(BOOL)isConcurrent
是否能够并发执行 -
-(BOOL)isCancelled
检测操作是否被取消,对取消做出响应
还有很多等等
2)NSOperationQueue「掌握」
作用:将 NSOperation 添加到 NSOperationQueue 中,系统会自动异步执行 NSOperation 中的操作
I. NSOperationQueue的队列类型
主队列
[NSOperationQueue mainQueue]
凡是添加到主队列中的任务,都会放到主线程中执行非主队列
[[NSOperationQueue alloc] init]
同时包含了:串行、并发功能
添加到这种队列,就会自动放到子线程中执行
// 1. 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 设置最大并发数,如果为 1 则为串行
queue.maxConcurrentOperationCount = 1;
// 2. 创建操作(任务)
// 2.1 创建NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
// 2.2 创建NSBlockOperation
// 快速创建并添加方式
[queue addOperationWithBlock:^{
NSLog(@"download5 --- %@", [NSThread currentThread]);
}];
// 普通方式
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}];
[op2 addExecutionBlock:^{
NSLog(@"download4 --- %@", [NSThread currentThread]);
}];
[op2 addExecutionBlock:^{
NSLog(@"download5 --- %@", [NSThread currentThread]);
}];
// 2.3 创建自定义任务
Person *op3 = [[Person alloc] init];
// 3. 添加任务到队列中
[queue addOperation:op1]; // 不用调用start方法,自动开启线程
[queue addOperation:op2];
[queue addOperation:op3];
II. NSOperationQueue 的方法
-
- (void)cancelAllOperations;
取消所有队列的操作,会移除 所有队列的操作 -
- (void)cancel;
取消单个队列操作 -
@property (getter=isSuspended) BOOL suspended;
暂停或恢复队列,不会移除队列
III. 线程的依赖和监听
- 依赖的作用:保证执行顺序
可以在 同一 / 不同队列中 创建依赖关系
// 线程操作B 会在 线程操作A执行完后 在执行
[operationB addDependency:operationA]; // 操作B依赖于操作A
- 线程操作的监听
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}];
op2.completionBlock = ^{
NSLog(@"监听线程操作 op2执行完毕后要执行的操作");
};
[queue addOperation:op2];
IV. 线程间的通讯
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"图片路径/图片名.格式"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
UIImage *image = [UIImage imageWithData:data];
// 回到主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
5. 多线程的应用:单例设计模式「Singleton」
定义:
- 类的对象成为系统中唯一的实例,提供一个访问点,供客户类 共享资源
使用情景:「操作频繁的时候,提升效率」
- 类只能有一个实例,必需从一个为人熟知的访问点访问,比如工厂方法
- 整个应用程序中,共享一份资源
注意:
- 某个类只能有一个实例
- 为保证实例唯一性,这个方法必须是静态类方法
- 单例方法名称一般以 share 或 default 开头
- 这个类必须自行创建这个对象
- 必须自行向整个系统提供这个实例
示例代码一、使用GCD
// 实现之外,先定义一个实例,为了防止野指针,赋空值
// 实现之外的静态变量,一个类只有一个「不能用继承来实现不同类的单例」
static Sample *_instance = nil;
@implement Sample
// alloc、allocWithZone方法都会调用 allocWithZone方法,所以只重写 allocWithZone方法
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
// 确保多线程的线程安全
static dispach_one_t onceToken;
dispach_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
// 直接返回单例对象
+ (instancetype)shareInstance{
// 确保多线程的线程安全
static dispatch_one_t onceToken;
dispach_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
// 防止 Copy方法的误用
-(id)copyWithZone:(NSZone *)zone{
return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone{
return _instance;
}
// 若是MRC模式下,需要添加一下代码
- (oneway void)release{
// 为保证单例,只重写父类方法,什么都不需要做
}
- (instancetype)retain{
return _instance;
}
- (NSInteger)retainCount{
return MAXFLOAT; //为了方便程序员交流,这里返回一个很大的数
}
@end
示例代码二、不使用GCD
static Sample _instance = nil;
@implement Sample
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
// 添加同步锁,注意加锁的位置
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
+ (instancetype)sharedInstance {
// 添加同步锁
@synchronized(self) {
if (_instance == nil) {
_instance = [[self alloc] init];
}
}
return _instance;
}
// 以下代码与使用GCD时重复,略
@end
将单例作为宏定义使用
单例抽取为宏
优点:每次定义单例的方式都是一样的,为了提高编程效率
缺点:因为单例定义中含有全局变量static
,在程序运行的整个过程中只创建一份,所以在 使用宏时不能继承单例宏方法定义
// 声明方法,
// #define a(b) is_##b 的作用是 将 a(hehe) 自动替换为 is_hehe
#define interfaceSingleton(name) +(instancetype)share##name;
// 定义方法
// 系统自带 宏定义 判断是否为 arc模式
#if !__has_feature(obj_arc)
#define implememtationSingleton(name) \
static id _instance = nil;\
+ (instancetype)allocWithZone:(struct _NSZone *)zone{\
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
return _instance;\
}\
+ (instancetype)share##name{\
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{\
_instance = [[self alloc] init];\
});\
return _instance;\
}\
- (id)copyWithZone:(NSZone *)zone{ return _instance; }\
- (id)mutableCopyWithZone:(NSZone *)zone{ return _instance; }\
- (oneway void)release{}\
- (instancetype)retain{ return _instance; }\
- (NSInteger)retainCount{ return (unsigned long)MAXFLOAT}
#else
#define implememtationSingleton(name) \
static id _instance = nil;\
+ (instancetype)allocWithZone:(struct _NSZone *)zone{\
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
return _instance;\
}\
+ (instancetype)share##name{\
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{\
_instance = [[self alloc] init];\
});\
return _instance;\
}\
- (id)copyWithZone:(NSZone *)zone{ return _instance; }\
- (id)mutableCopyWithZone:(NSZone *)zone{ return _instance; }
#endif
- 宏方法使用
interfaceSingleton(className) // 这里没有「;」因为是宏定义的使用
implementationSingleton(className)