多线程(二):GCD的基本使用,同步函数和异步函数,串行队列和并发队列,GCD队列组,栅栏函数,GCD单例

  • 什么是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

猜你喜欢

转载自blog.csdn.net/weixin_42433480/article/details/89609096