- 什么是GCD
1)全称是Grand Central Dispatch,可译为“牛逼的中枢调度器”
2)纯C语言,提供了非常多强大的函数
- GCD的优势
1)GCD是苹果为多核的并行运算提出的解决方案。
2)GCD会自动利用更多的CPU内核(比如双核,四核)。
3)GCD会自动管理线程的生命周期(创建线程,调度任务)。
- 任务和队列
1)任务:执行什么操作
2)队列:用来存放任务
- GCD的使用就两个步骤
1)定制任务(确定想做的事)
2)将任务添加到队列(GCD会自动将队列中的任务取出,放到对应的线程中去执行; 任务的取出遵循队列的FIFO原则:先进先出,后进后出(栈是先进后出))
- 执行任务
GCD中有2个用来执行任务的常用函数
1) 用同步的方式执行任务
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block): queue: 队列 block: 任务
2)用异步的方式执行任务
dispatch_async(dispatch_queue_t queue, dispatch_block_t block)
- 同步和异步的区别
1)同步:只能在当前的线程中执行任务,不具备开启新线程的能力
2)异步:可以在新的线程中执行任务,具备开启新线程的能力
- 队列的类型
GCD的队列可以分为两大类型
1. 并发队列
1) 可以让多个任务并发同时执行(自动开启多个线程自动执行任务)
2)并发功能只有在异步(dispatch_async)函数下才有效
2. 串行队列
1)让任务一个接一个的执行(一个任务执行完毕后,再执行下一个任务)
- 容易混淆的术语
有四个术语比较容易混淆:同步,异步,并发,串行
1. 同步和异步的主要区别:能不能开启新的线程
1)同步:只是在当前线程中执行任务,不具备开启新线程的能力
2)异步:可以在新的线程中执行,具备开启新线程的能力
2. 并发和串行的主要区别:任务的执行方式
1)并发:允许多个任务并发(同时)执行
2)串行:一个任务执行完毕后,再执行下一个任务
GCD的基本使用
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
#pragma mark ----------------------
#pragma Events
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// [NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];
[self asyncConcurrent];
}
#pragma mark ----------------------
#pragma Methods
//异步函数+并发队列:会开启多条线程,队列中的任务是并发执行
//注意:并不是有多少个任务就开启多少个线程,是由系统内部决定的
-(void)asyncConcurrent
{
//1.创建队列
/*
第一个参数:C语言的字符串,标签
第二个参数:队列的类型
DISPATCH_QUEUE_CONCURRENT:并发
DISPATCH_QUEUE_SERIAL:串行
*/
//dispatch_queue_t queue = dispatch_queue_create("com.www.download", DISPATCH_QUEUE_CONCURRENT);
//获得全局并发队列
/*
第一个参数:优先级
第二个参数:
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"---satrt----");
//2.1>封装任务2>添加任务到队列中
/*
第一个参数:队列
第二个参数:要执行的任务
*/
dispatch_async(queue, ^{
NSLog(@"download1----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download2----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download3----%@",[NSThread currentThread]);
});
NSLog(@"---end----");
}
//异步函数+串行队列:会开线程,开一条线程,队列中的任务是串行执行的
-(void)asyncSerial
{
//1.创建队列
dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_SERIAL);
//2.封装操作
dispatch_async(queue, ^{
NSLog(@"download1----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download2----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download3----%@",[NSThread currentThread]);
});
}
//同步函数+并发队列:不会开线程,任务是串行执行的
-(void)syncConcurrent
{
//1.创建队列
dispatch_queue_t queue = dispatch_queue_create("com.www.download", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"---start---");
//2.封装任务
dispatch_sync(queue, ^{
NSLog(@"download1----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download2----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download3----%@",[NSThread currentThread]);
});
NSLog(@"---end---");
}
//同步函数+串行队列:不会开线程,任务是串行执行的
-(void)syncSerial
{
//1.创建队列
dispatch_queue_t queue = dispatch_queue_create("com.www.download", DISPATCH_QUEUE_SERIAL);
//2.封装任务
dispatch_sync(queue, ^{
NSLog(@"download1----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download2----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download3----%@",[NSThread currentThread]);
});
}
//异步函数+主队列:所有任务都在主线程中执行,不会开线程
-(void)asyncMain
{
//1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
//2.异步函数
dispatch_async(queue, ^{
NSLog(@"download1----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download2----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download3----%@",[NSThread currentThread]);
});
}
//同步函数+主队列:死锁
//注意:如果该方法在子线程中执行,那么所有的任务在主线程中执行,不会死锁
-(void)syncMain
{
//1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"start----");
//2.同步函数
//同步函数:立刻马上执行,如果我没有执行完毕,那么后面的也别想执行
//异步函数:如果我没有执行完毕,那么后面的也可以执行
dispatch_sync(queue, ^{
NSLog(@"download1----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download2----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"download3----%@",[NSThread currentThread]);
});
NSLog(@"end---");
}
@end
各种队列的执行效果
注意:使用sync(同步)函数往当前串行队列中添加任务,会卡住当前的串行队列,造成死锁
GCD实现线程间通信(子线程耗时下载,主线程更新UI)
- 从子线程回到主线程
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行耗时的异步操作...
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主线程,执行UI刷新操作
});
});
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation ViewController
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//1.创建子线程下载图片
//DISPATCH_QUEUE_PRIORITY_DEFAULT 0
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//1.1 确定url
NSURL *url = [NSURL URLWithString:@"http://a.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=da0ec79c738da9774e7a8e2f8561d42f/c83d70cf3bc79f3d6842e09fbaa1cd11738b29f9.jpg"];
//1.2 下载二进制数据到本地
NSData *imageData = [NSData dataWithContentsOfURL:url];
//1.3 转换图片
UIImage *image = [UIImage imageWithData:imageData];
NSLog(@"download----%@",[NSThread currentThread]);
//更新UI
// dispatch_async(dispatch_get_main_queue(), ^{
dispatch_sync(dispatch_get_main_queue(), ^{
self.imageView.image = image;
NSLog(@"UI----%@",[NSThread currentThread]);
});
});
}
@end
GCD常用函数
- 延时执行
1)调用NSObject的方法
-
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
// 2秒后再调run方法
2)使用GCD函数
-
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后执行这里的代码...
});
3)使用NSTimer
-
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:NO];
-
4)一次性代码
-
//使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // 只执行1次的代码(这里面默认是线程安全的) });
5)代码示例
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self once];
}
//延迟执行
-(void)delay
{
NSLog(@"start-----");
//1. 延迟执行的第一种方法
//[self performSelector:@selector(task) withObject:nil afterDelay:2.0];
//2.延迟执行的第二种方法
//[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(task) userInfo:nil repeats:YES];
//3.GCD
// dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
/*
第一个参数:DISPATCH_TIME_NOW 从现在开始计算时间
第二个参数:延迟的时间 2.0 GCD时间单位:纳秒
第三个参数:队列
*/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
NSLog(@"GCD----%@",[NSThread currentThread]);
});
}
//一次性代码
//不能放在懒加载中的,应用场景:单例模式
-(void)once
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"---once----");
});
}
-(void)task
{
NSLog(@"task----%@",[NSThread currentThread]);
}
@end
GCD栅栏函数
- GCD中还有个用来执行任务的函数:
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
- 这个queue不能是全局的并发队列
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//0.获得全局并发队列
//栅栏函数不能使用全局并发队列,只能使用自己创建的并发队列
//dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT);
//1.异步函数
dispatch_async(queue, ^{
for (NSInteger i = 0; i<100; i++) {
NSLog(@"download1-%zd-%@",i,[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<100; i++) {
NSLog(@"download2-%zd-%@",i,[NSThread currentThread]);
}
});
//栅栏函数
dispatch_barrier_async(queue, ^{
NSLog(@"+++++++++++++++++++++++++++++");
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<100; i++) {
NSLog(@"download3-%zd-%@",i,[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<100; i++) {
NSLog(@"download4-%zd-%@",i,[NSThread currentThread]);
}
});
}
@end
GCD快速迭代
- 使用dispatch_apply函数能进行快速迭代遍历
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){
// 执行10次代码,index顺序不确定
});
剪切文件代码示例:
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self moveFileWithGCD];
}
-(void)forDemo
{
//同步,在主线程串行
for (NSInteger i = 0; i<10; i++) {
NSLog(@"%zd---%@",i,[NSThread currentThread]);
}
}
//开子线程和主线程一起完成遍历任务,任务的执行时并发的
-(void)applyDemo
{
/*
第一个参数:遍历的次数
第二个参数:队列(并发队列)
第三个参数:index 索引
*/
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
NSLog(@"%zd---%@",index,[NSThread currentThread]);
});
}
//使用for循环
-(void)moveFile
{
//1.拿到文件路径
NSString *from = @"/Users/Alan/Desktop/from";
//2.获得目标文件路径
NSString *to = @"/Users/Alan/Desktop/to";
//3.得到目录下面的所有文件
NSArray *subPaths = [[NSFileManager defaultManager] subpathsAtPath:from];
NSLog(@"%@",subPaths);
//4.遍历所有文件,然后执行剪切操作
NSInteger count = subPaths.count;
for (NSInteger i = 0; i< count; i++) {
//4.1 拼接文件的全路径
// NSString *fullPath = [from stringByAppendingString:subPaths[i]];
//在拼接的时候会自动添加/
NSString *fullPath = [from stringByAppendingPathComponent:subPaths[i]];
NSString *toFullPath = [to stringByAppendingPathComponent:subPaths[i]];
NSLog(@"%@",fullPath);
//4.2 执行剪切操作
/*
第一个参数:要剪切的文件在哪里
第二个参数:文件应该被存到哪个位置
*/
[[NSFileManager defaultManager]moveItemAtPath:fullPath toPath:toFullPath error:nil];
NSLog(@"%@---%@--%@",fullPath,toFullPath,[NSThread currentThread]);
}
}
-(void)moveFileWithGCD
{
//1.拿到文件路径
NSString *from = @"/Users/Alan/Desktop/from";
//2.获得目标文件路径
NSString *to = @"/Users/Alan/Desktop/to";
//3.得到目录下面的所有文件
NSArray *subPaths = [[NSFileManager defaultManager] subpathsAtPath:from];
NSLog(@"%@",subPaths);
//4.遍历所有文件,然后执行剪切操作
NSInteger count = subPaths.count;
dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t i) {
//4.1 拼接文件的全路径
// NSString *fullPath = [from stringByAppendingString:subPaths[i]];
//在拼接的时候会自动添加/
NSString *fullPath = [from stringByAppendingPathComponent:subPaths[i]];
NSString *toFullPath = [to stringByAppendingPathComponent:subPaths[i]];
NSLog(@"%@",fullPath);
//4.2 执行剪切操作
/*
第一个参数:要剪切的文件在哪里
第二个参数:文件应该被存到哪个位置
*/
[[NSFileManager defaultManager]moveItemAtPath:fullPath toPath:toFullPath error:nil];
NSLog(@"%@---%@--%@",fullPath,toFullPath,[NSThread currentThread]);
});
}
@end
GCD队列组
- 队列组使用情况
1)分别异步执行多个耗时的操作
2)等多个异步操作都执行完毕后,再回到主线程执行操作
3)拦截通知,当队列组中所有的任务都执行完毕的时候回进入到下面的方法,异步的,不会阻塞线程
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), ^{
// 执行1个耗时的异步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步操作都执行完毕后,回到主线程...
});
下载两张图片合并成一张代码示例:
#import "ViewController.h"
@interface ViewController ()
/** 图片1 */
@property (nonatomic, strong) UIImage *image1;
/** 图2 */
@property (nonatomic, strong) UIImage *image2;
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation ViewController
#pragma mark ----------------------
#pragma mark Events
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self test];
}
#pragma mark ----------------------
#pragma mark Methods
-(void)group1
{
//1.创建队列
dispatch_queue_t queue =dispatch_get_global_queue(0, 0);
//2.创建队列组
dispatch_group_t group = dispatch_group_create();
//3.异步函数
/*
1)封装任务
2)把任务添加到队列中
dispatch_async(queue, ^{
NSLog(@"1----%@",[NSThread currentThread]);
});
*/
/*
1)封装任务
2)把任务添加到队列中
3)会监听任务的执行情况,通知group
*/
dispatch_group_async(group, queue, ^{
NSLog(@"1----%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"2----%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"3----%@",[NSThread currentThread]);
});
//拦截通知,当队列组中所有的任务都执行完毕的时候回进入到下面的方法,异步的,不会阻塞线程
dispatch_group_notify(group, queue, ^{
NSLog(@"-------dispatch_group_notify-------");
});
NSLog(@"----end----");
}
-(void)group2
{
//1.创建队列
dispatch_queue_t queue =dispatch_get_global_queue(0, 0);
//2.创建队列组
dispatch_group_t group = dispatch_group_create();
//3.在该方法后面的异步任务会被纳入到队列组的监听范围,进入群组
//dispatch_group_enter|dispatch_group_leave 必须要配对使用
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"1----%@",[NSThread currentThread]);
//离开群组
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"2----%@",[NSThread currentThread]);
//离开群组
dispatch_group_leave(group);
});
//拦截通知
//问题?该方法是阻塞的吗? 内部本身是异步的
// dispatch_group_notify(group, queue, ^{
// NSLog(@"-------dispatch_group_notify-------");
// });
//等待.死等. 直到队列组中所有的任务都执行完毕之后才能执行
//阻塞的,会阻塞线程
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"----end----");
}
-(void)group3
{
/*
1.下载图片1 开子线程
2.下载图片2 开子线程
3.合成图片并显示图片 开子线程
*/
//-1.获得队列组
dispatch_group_t group = dispatch_group_create();
//0.获得并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 1.下载图片1 开子线程
dispatch_group_async(group, queue,^{
NSLog(@"download1---%@",[NSThread currentThread]);
//1.1 确定url
NSURL *url = [NSURL URLWithString:@"http://www.qbaobei.com/tuku/images/13.jpg"];
//1.2 下载二进制数据
NSData *imageData = [NSData dataWithContentsOfURL:url];
//1.3 转换图片
self.image1 = [UIImage imageWithData:imageData];
});
// 2.下载图片2 开子线程
dispatch_group_async(group, queue,^{
NSLog(@"download2---%@",[NSThread currentThread]);
//2.1 确定url
NSURL *url = [NSURL URLWithString:@"http://pic1a.nipic.com/2008-09-19/2008919134941443_2.jpg"];
//2.2 下载二进制数据
NSData *imageData = [NSData dataWithContentsOfURL:url];
//2.3 转换图片
self.image2 = [UIImage imageWithData:imageData];
});
//3.合并图片
//主线程中执行
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"combie---%@",[NSThread currentThread]);
//3.1 创建图形上下文
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
//3.2 画图1
[self.image1 drawInRect:CGRectMake(0, 0, 200, 100)];
self.image1 = nil;
//3.3 画图2
[self.image2 drawInRect:CGRectMake(0, 100, 200, 100)];
self.image2 = nil;
//3.4 根据上下文得到一张图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
//3.5 关闭上下文
UIGraphicsEndImageContext();
//3.6 更新UI
// dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"UI----%@",[NSThread currentThread]);
self.imageView.image = image;
// });
});
// dispatch_release(group)
}
-(void)test
{
// dispatch_async(<#dispatch_queue_t queue#>, <#^(void)block#>)
//区别:封装任务的方法(block--函数)
/*
第一个参数:队列
第二个参数:参数
第三个参数:要调用的函数的名称
*/
dispatch_async_f(dispatch_get_global_queue(0, 0), NULL, task);
dispatch_async_f(dispatch_get_global_queue(0, 0), NULL, task);
dispatch_async_f(dispatch_get_global_queue(0, 0), NULL, task);
}
void task(void *param)
{
NSLog(@"%s---%@",__func__,[NSThread currentThread]);
}
@end
GCD单例模式
- 单例模式的作用
1)可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问
2)从而方便地控制了实例个数,并节约系统资源
- 单例模式的使用场合
- 在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次)
- ARC中,单例模式的实现
1)在.m中保留一个全局的static的实例
static id _instance;
2)重写allocWithZone:方法,在这里创建唯一的实例(注意线程安全)
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
- 提供1个类方法让外界访问唯一的实例
+ (instancetype)sharedInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
- 实现copyWithZone:方法
- (id)copyWithZone:(struct _NSZone *)zone
{
return _instance;
}
具体单例代码:
AlanTool.h
#import <Foundation/Foundation.h>
@interface AlanTool : NSObject<NSCopying, NSMutableCopying>
//类方法
//1.方便访问
//2.标明身份
//3.注意:share+类名|default + 类名 | share | default | 类名
+(instancetype)shareTool;
@end
AlanTool.m
#import "AlanTool.m"
@implementation AlanTool
//0.提供全局变量
static AlanTool *_instance;
//1.alloc-->allocWithZone
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
//加互斥锁解决多线程访问安全问题
// @synchronized(self) {
// if (_instance == nil) {
// _instance = [super allocWithZone:zone];
// }
// }
//本身就是线程安全的
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
//2.提供类方法
+(instancetype)shareTool
{
//1
//return [[self alloc]init];
//2
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
//3.严谨
-(id)copyWithZone:(NSZone *)zone
{
return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone
{
return _instance;
}
#if __has_feature(objc_arc)
//条件满足 ARC
#else
// MRC
-(void)release
{
}
-(instancetype)retain
{
return _instance;
}
//习惯
-(NSUInteger)retainCount
{
return MAXFLOAT;
}
#endif
@end
通过宏实现GCD单例模式
#define SingleH(name) +(instancetype)share##name;
#if __has_feature(objc_arc)
//条件满足 ARC
#define SingleM(name) static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
\
return _instance;\
}\
\
+(instancetype)share##name\
{\
return [[self alloc]init];\
}\
\
-(id)copyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
return _instance;\
}
#else
//MRC
#define SingleM(name) static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
\
return _instance;\
}\
\
+(instancetype)share##name\
{\
return [[self alloc]init];\
}\
\
-(id)copyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
-(oneway void)release\
{\
}\
\
-(instancetype)retain\
{\
return _instance;\
}\
\
-(NSUInteger)retainCount\
{\
return MAXFLOAT;\
}
#endif