• ios开发之多线程---GCD


    一:基本概念

    1:进程:正在运行的程序为进程。

    2:线程:每个进程要想执行任务必须得有线程,进程中任务的执行都是在线程中。

    3:线程的串行:一条线程里任务的执行都是串行的,假如有一个进程开辟了一条线程,此条线程里有ABC三个任务,则ABC三个任务的执行是串行的,ABC三个任务按顺序一个个执行

    4:多线程:一个进程可以开辟多条线程去并行(同时)去执行任务,假如有ABC三个任务,则此进程会开辟多条线程去并发执行ABC三个任务,可以提高运行效率

    5:线程的原理:同一时间,cpu只能处理一条线程,而且也只有一条线程在工作,多线程的并发执行,也就是cpu在多条线程间爱会切换调度。进程一启动,就默认创建了一条线程为主线程大约为1M,所创建的子线程大约为512KB。

    6:多线程的缺点:1:大量开线程,会大量占用cpu资源,cpu在调度线程上的开销也就越大 2:大量开线程,会使每条线程的执行效率降低,程序的性能降低,对于移动开发来说,适当开辟线程,大量开线程会使程序的想能降低

    7:ios开发中多线程的应用:程序一启动就默认会开辟一条线程,成为主线程,也就是UI线程,主线程主要负责,刷新和显示UI,处理按钮的点击事件,表格的滚动拖拽事件,尽量避免把耗时的操作放在主线程。凡在主线程执行的任务都是串行的,按照顺序一个个执行,若把耗时的操作放在主线程,则会卡主主线程,影响主线程的其他操作,造成很差的用户体验。正确的做法是:将比较耗时的操作放在子线程中去执行,不要影响主线程的其他操作。

    8:获得当前线程:[NSThread currentThread]; 获得主线程:[NSThread mainThread];

    9:ios中的多线程方案:1:Pthread:跨平台,适用于各个系统,线程的生命周期由程序员去管理。 2:NSThread 3:GCD:由系统自动去管理线程的声明周期   4:NSOperation:基于GCD封装,也是右系统去管理线程的生命周期。

    二:线程的安全:

    1:线程安全:当同一块资源被多条线程同时访问的时候,会涉及到线程安全的问题,例如卖票,银行取钱问题。解决方法:加互斥锁

     @synchronized(self) {被锁住的代码 }(加锁是很耗性能的,当只有多条线程访问统一资源的时候,才考虑去加互斥锁)。加锁的工作原理是:加互斥锁后,同一时间只许可一条线程访问该资源,其他线程无法访问。当某条线程访问该资源时,会加一把锁,被锁住的代码执行完毕后,锁会被打开,下个线程此时才可以访问该资源,注意:锁对象必须是唯一的,否则多条线程依然可以访问该资源。锁对象一般用self,因为在控制器中,控制器对象就是唯一的,保证了锁对象的唯一性。

    2:线程同步:加锁用的就是线程同步技术,多条线程同一条线程上按顺序依次执行 线程异步:多条线程异步去执行

    3:原子与非原子属性:atomic是原子属性,默认是线程安全的,在执行setter方法赋值的时候,会加一把互斥锁,等待赋值结束后,所才被打开,防止了多条线程同时访问造成的线程安全问题。虽然是线程安全的,但是加锁消耗了大量的资源 。nonatomic:非原子属性,线程不安全的,没有加锁,因为在实际移动开发中,调用setter方法大部分都是在主线程,所以不涉及多条线程同时访问的问题。若涉及了多线程同时访问该资源,可以加一把互斥锁,还可以用atomic原子属性去修饰

    三:GDC

    1:GCD有两个核心的概念:1:队列 2:任务,其中队列是用来存放任务,GCD的使用:创建任务直接将任务放到队列里,GCD会自动将任务从队列里取出,按照FIFO原则(先进先出,后进后出,栈是现进后出)取出任务,放到相应的线程中去执行任务。

    2:GCD有两个常用的函数用来执行任务:1:同步函数: dispatch_sync

    dispatch_sync(queue, ^{

        });

     2:异步函数:dispatch_async  

      dispatch_async(queue, ^{

        });

     其中queue为队列,block为任务。其中同步函数:在当前线程中按顺序串行执行任务,不具备开启线程的能力 异步函数:在新的线程中执行,具备开启新线程的能力

    3:GCD两大队列:1:串行队列:队列中的任务按顺序执行,一个任务执行完毕才会去执行下一个任务  2:并发队列:可以并发同时执行多个任务,并发队列只有在异步函数 dispatch_async中才能起到并发执行的作用

    总结:1:同步和异步主要影响能不能开启新的线程 2:串行队列和并发队列主要影响任务执行的方式:串行:按顺序依次执行,并发队列:多个任务同时执行 3:看一条线程,先看最外层是同步还是异步,若是同步,不能开启新的线程,只在当前的线程中执行,若是异步,则具备开启新线程的能力,可以在新县城中执行 再看内层:若是串行队列:则任务按顺序执行 若是并发队列:则可以并发同时还行任务,但只有在异步函数中才会并发执行任务。

    4:队列的创建:

    1:dispatch_queue_t queue = dispatch_queue_create(a,b);a参数为队列名称,C语言字符串,用"",b参数为队列,串行队列:DISPATCH_QUEUE_SERIAL,也可以这么创建串行队列

     dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", NULL);

    并发队列:DISPATCH_QUEUE_SERIAL NULL  注意:1:异步并发队列有三个任务,则并不会一定开启三条线程,开启线程的个数是由GCD内部去决定的。2:任务添加到队列后,会按FIFO原则将任务取出,并发执行,也就是三个任务的执行先后顺序是不确定的

    2:并发队列的创建:1:可以获取到全局并发队列:

     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);第一个参数为队列优先级,默认为DEFAULT,也可以传0,第二个参数暂时用不到传0就可以,当涉及多条线程需要设定优先级时,可以设置第一个参数 2:也可以手动去创建并发队列dispatch_queue_create(a,b)

    5:GCD的各种队列:

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
    }
    
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
        [self syncMain];
    }
    
    /**
     * 同步函数 + 主队列:只打印begin,是因为同步函数放在串行队列中会卡主当前线程,造成死锁。同步函数不开启线程在当前线程中执行,且立即执行,而主线程的任务end要先执行完再去执行同步函数中任务,而同步函数又要立即执行,所以会造成死锁
     */
    - (void)syncMain
    {
        NSLog(@"syncMain ----- begin");
        
        // 1.获得主队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        
        // 2.将任务加入队列
        dispatch_sync(queue, ^{
            NSLog(@"1-----%@", [NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"2-----%@", [NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"3-----%@", [NSThread currentThread]);
        });
        
        NSLog(@"syncMain ----- end");
    }
    
    /**
     * 异步函数 + 主队列:只在主线程中执行任务。无论同步还是异步,只要将任务放到追队列里,任务就会在主线程中执行,且是串行执行任务,主队列是一种特殊的串行队列。因为123任务是后添加到主队列里的任务,所以先执行完 start 和 end 在执行 1,2,3
     */
    - (void)asyncMain
    {   NSLog(@"syncConcurrent--------start");
        // 1.获得主队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        
        // 2.将任务加入队列
        dispatch_async(queue, ^{
            NSLog(@"1-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"2-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"3-----%@", [NSThread currentThread]);
        });
         NSLog(@"syncConcurrent--------end");
    }
    
    /**
     * 同步函数 + 串行队列:同步不会开启新的线程,在当前线程串行执行任务,且立即执行任务。任务是串行的,执行完一个任务,再执行下一个任务。打印顺序:start 1,2,3 end
     */
    - (void)syncSerial
    {
        
        NSLog(@"syncConcurrent--------start");
        // 1.创建串行队列
        dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
        
        // 2.将任务加入队列
        dispatch_sync(queue, ^{
            NSLog(@"1-----%@", [NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"2-----%@", [NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"3-----%@", [NSThread currentThread]);
        });
        
       NSLog(@"syncConcurrent--------end");
    }
    
    /**
     * 异步函数 + 串行队列:异步函数会开启新的线程,但是任务是串行的,任务按顺序执行执行完一个任务,再执行下一个任务。所以只开启一条线程(在一条线程中任务是按照顺序一个接一个执行)。打印顺序:statrt end 1 2 3。相当于把asyncSerial任务放到了主队列中执行,任务则在主线程中执行,又将任务123放到了串行队列中执行,所以先执行完主线程的任务,在执行串行队列的任务,所以先打印start end 再按照顺序依次执行任务1,2,3
     */
    - (void)asyncSerial
    
    {    NSLog(@"syncConcurrent--------start");
        // 1.创建串行队列
        dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
    //    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", NULL);
        
        // 2.将任务加入队列
        dispatch_async(queue, ^{
            NSLog(@"1-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"2-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"3-----%@", [NSThread currentThread]);
        });
        
          NSLog(@"syncConcurrent--------end");
    }
    
    /**
     * 同步函数 + 并发队列:不会开启新的线程,在当前线程中串行执行任务且立即执行任务,因为并发队列只有在异步函数中才会起作用,打印顺序:start  1,2,3,end
     */
    - (void)syncConcurrent
    {
        NSLog(@"syncConcurrent--------start");
        // 1.获得全局的并发队列
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        // 2.将任务加入队列
        dispatch_sync(queue, ^{
            NSLog(@"1-----%@", [NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"2-----%@", [NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"3-----%@", [NSThread currentThread]);
        });
        
        
        NSLog(@"syncConcurrent--------end");
    }
    
    /**
     * 异步函数 + 并发队列:1:可以同时开启多条线程并发同时执行任务,开启线程的个数不确定,并且并发队列只有在异步函数下才有效 2:代码执行的顺序是先打印 satrt 在打印 end 最后再打印子线程中的内容,先执行主线程中的任务,在执行异步线程的任务,123任务执行完成的先后顺序是不确定的。原因是:相当于把asyncConcurrent任务放到了主队列里,在主线程执行,任务是按照FIFO原则从队列中取出,所以先执行完start后,分别将任务123添加到了全局并发队列里,123任务是后添加到队列中的,所以先执行完主队列任务,再异步并发执行子线程中的任务。
     */
    - (void)asyncConcurrent
    {
        // 1.创建一个并发队列
        // label : 相当于队列的名字
    //    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_CONCURRENT);
        
        NSLog(@"asyncConcurrent--------start");
        
        // 1.获得全局的并发队列
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        // 2.将任务加入队列
        dispatch_async(queue, ^{
            for (NSInteger i = 0; i<10; i++) {
                NSLog(@"1-----%@", [NSThread currentThread]);
            }
        });
        dispatch_async(queue, ^{
            for (NSInteger i = 0; i<10; i++) {
                NSLog(@"2-----%@", [NSThread currentThread]);
            }
        });
        dispatch_async(queue, ^{
            for (NSInteger i = 0; i<10; i++) {
                NSLog(@"3-----%@", [NSThread currentThread]);
            }
        });
        
        NSLog(@"asyncConcurrent--------end");
    
    }
    
    @end

    6:GCD中的线程间通信

        

    #import "ViewController.h"
    
    @interface ViewController ()
    @property (weak, nonatomic) IBOutlet UIImageView *imageView;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
    }
    
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
        
        NSLog(@"---------------------------------------1");
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"---------------------------------------3");
            // 图片的网络路径
            NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
            
            // 加载图片
            NSData *data = [NSData dataWithContentsOfURL:url];
            
            // 生成图片
            UIImage *image = [UIImage imageWithData:data];
            
            // 回到主线程
            dispatch_async(dispatch_get_main_queue(), ^{
                self.imageView.image = image;
            NSLog(@"-------------------------------------5");
            });
            
           NSLog(@"---------------------------------------4");
            
            
        });
        
       NSLog(@"---------------------------------------2");
    }
    
    @end

    打印顺序:1,2,3,4,5,先执行主线程中的任务,1,2 主线程中的任务执行完毕后再去异步执行子线程中的任务,先执行完子线程中的任务3,4 后,再回到主线程执行任务5。原因:相当于把touchBegan任务放到了主队列里,任务在主线程中执行,子线程中的任务是后面添加到并发队列里的,所以先回执行完主线程中的任务1,2 当执行完毕后再去执行子线程中的任务,在子线程中的任务5是后面加入到主队列的,所以先回执行完子线程中的任务3,4,再回到主线程里执行任务5

    6:GCD的其他函数

    #import "ViewController.h"
    #import "XMGPerson.h"
    
    @interface ViewController ()
    @property (weak, nonatomic) IBOutlet UIImageView *imageView;
    /** 图片1 */
    @property (nonatomic, strong) UIImage *image1;
    /** 图片2 */
    @property (nonatomic, strong) UIImage *image2;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        XMGPerson *person1 = [[XMGPerson alloc] init];
        XMGPerson *person2 = [[XMGPerson alloc] init];
        XMGPerson *person3 = [[XMGPerson alloc] init];
        XMGPerson *person4 = [[XMGPerson alloc] init];
        
    //    XMGPerson *p1 = [[XMGPerson alloc] init];
    //    NSLog(@"%@", p1.books);
    //    
    //    XMGPerson *p2 = [[XMGPerson alloc] init];
    //    NSLog(@"%@", p2.books);
        
    }
    
    void download(void * data)
    {
    
    }
    
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    //    dispatch_async(<#dispatch_queue_t queue#>, <#^(void)block#>);
    //    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //    dispatch_async_f(queue, NULL, download);
        
    //    [self group];
        [self barrier];
    }
    /**
     *    群组:1:群组中的任务是并发执行的,会等群组中的任务都执行完毕后,会调用 dispatch_group_notify,在并发队列中完成绘图操作,在主线程中去显示图片。1:首先创建群组: dispatch_group_t group = dispatch_group_create(); 2:将任务放到并发队列中,将队列放到群组中,12任务执行完不确定 3:都执行完调用dispatch_group_async,将耗时的绘图操作还放在异步线程,绘图完毕后,回到主线程显示UI
     */
    - (void)group
    {
        
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        // 创建一个队列组
        dispatch_group_t group = dispatch_group_create();
        
        // 1.下载图片1
        dispatch_group_async(group, queue, ^{
            // 图片的网络路径
            NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
            
            // 加载图片
            NSData *data = [NSData dataWithContentsOfURL:url];
            
            // 生成图片
            self.image1 = [UIImage imageWithData:data];
        });
        
        // 2.下载图片2
        dispatch_group_async(group, queue, ^{
            // 图片的网络路径
            NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
            
            // 加载图片
            NSData *data = [NSData dataWithContentsOfURL:url];
            
            // 生成图片
            self.image2 = [UIImage imageWithData:data];
        });
        
        // 3.将图片1、图片2合成一张新的图片
        dispatch_group_notify(group, queue, ^{
            // 开启新的图形上下文
            UIGraphicsBeginImageContext(CGSizeMake(100, 100));
            
            // 绘制图片
            [self.image1 drawInRect:CGRectMake(0, 0, 50, 100)];
            [self.image2 drawInRect:CGRectMake(50, 0, 50, 100)];
            
            // 取得上下文中的图片
            UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
            
            // 结束上下文
            UIGraphicsEndImageContext();
            
            // 回到主线程显示图片
            dispatch_async(dispatch_get_main_queue(), ^{
                // 4.将新图片显示出来 
                self.imageView.image = image;
            });
        });
    }
    
    /**
     * 快速迭代:dispatch_apply(subpaths.count, queue, ^(size_t index):快速迭代函数:第一个参数为迭代对象的个数,第二个参数为队列,index为返回的索引,快速迭代,并发执行,提高迭代效率
     */
    - (void)apply
    {
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        NSString *from = @"/Users/xiaomage/Desktop/From";
        NSString *to = @"/Users/xiaomage/Desktop/To";
        
        NSFileManager *mgr = [NSFileManager defaultManager];
        NSArray *subpaths = [mgr subpathsAtPath:from];
        
        dispatch_apply(subpaths.count, queue, ^(size_t index) {
            NSString *subpath = subpaths[index];
            NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
            NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
            // 剪切
            [mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
            
            NSLog(@"%@---%@", [NSThread currentThread], subpath);
        });
    }
    
    /**
     * 传统文件剪切
     */
    - (void)moveFile
    {
        NSString *from = @"/Users/xiaomage/Desktop/From";
        NSString *to = @"/Users/xiaomage/Desktop/To";
    
        NSFileManager *mgr = [NSFileManager defaultManager];
        //此方法获得的是from路径下的所有文件路径,包括子文件夹下的文件路径
        NSArray *subpaths = [mgr subpathsAtPath:from];
    
        for (NSString *subpath in subpaths) {
            NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
            NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                // 将文件从一个文件夹移动到另一个文件夹:是个耗时的操作,座椅在子线程中执行
                [mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
            });
        }
    }
    /**
     *    一次性函数:在整个项目中只执行一次
     */
    - (void)once
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSLog(@"------run");
        });
    }
    
    /**
     * 延迟执行:GCD延迟函数:dispatch_after所传的队列不同,就在不同的队列中执行延迟函数,延迟的方法有以下三种
     */
    - (void)delay
    {
        NSLog(@"touchesBegan-----");
        //    [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
        
        //    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        //        NSLog(@"run-----");
        //    });
        
        [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
    }
    
    - (void)run
    {
        NSLog(@"run-----");
    }
    
    
    /**
     *    dispatch_barrier_async函数:能保证先12任务先执行完,在执行dispatch_barrier_async中的任务,最后在执行34任务,要想此函数起作用,queue不能是全局并发队列,因为queue是并发队列,所以12,34执行完的先后顺序不确定,但是能保证先执行12,在执行dispatch_barrier_async函数任务,最后执行34
     */
    - (void)barrier
    {
        dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
       
        
        dispatch_async(queue, ^{
            NSLog(@"----1-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"----2-----%@", [NSThread currentThread]);
        });
        
        dispatch_barrier_async(queue, ^{
            NSLog(@"----barrier-----%@", [NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"----3-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"----4-----%@", [NSThread currentThread]);
        });
    }
    
    @end

    7:GCD下完整的单例模式:GCD模式下的单例保证了线程的安全,dispatch_once是线程安全的,防止同一时间多条线程初始化同一个变量

    #import "XMGPerson.h"
    
    @interface XMGPerson() <NSCopying>
    
    @end
    
    @implementation XMGPerson
    
    /**
     *    模拟三种情况得到单例对象:
        1:alloc创建对象 2:通过对象copy得到一个新对象 3:直接调用sharedPerson得到一个新对象
        2:1:先用static定义一个下划线的成员变量 2:alloc方法内部会调用allocWithZone方法,用dispatch_once函数调用父类[super allocWithZone:zone]去开辟空间 3:当在类中实现copyWithZone方法时,需要类去遵守NSCopying协议,直接将成员变量返回,是因为调用copy之前该对象已经被初始化了
     */
    static XMGPerson *_person;
    
    + (instancetype)allocWithZone:(struct _NSZone *)zone
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _person = [super allocWithZone:zone];
        });
        return _person;
    }
    
    + (instancetype)sharedPerson
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _person = [[self alloc] init];
        });
        return _person;
    }
    
    - (id)copyWithZone:(NSZone *)zone
    {
        return _person;
    }
    @end

    8:单例的宏定义:其中的表示下一行也属于宏定义

    // .h文件
    #define XMGSingletonH(name) + (instancetype)shared##name;
    
    // .m文件
    #define XMGSingletonM(name) 
    static id _instance; 
     
    + (instancetype)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:(NSZone *)zone 
    { 
        return _instance; 
    }

    9:非GCD模式下的单例:加锁的目的是保证线程安全,防止同一时间多条线程访问初始化同一个变量

    #import "XMGPerson.h"
    
    @interface XMGPerson()
    
    @end
    
    @implementation XMGPerson
    
    static id _instance;
    
    + (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;
    }
    
    - (id)copyWithZone:(NSZone *)zone
    {
        return _instance;
    }
    @end
  • 相关阅读:
    645. Set Mismatch
    400. Nth Digit
    633. Sum of Square Numbers
    507. Perfect Number
    453. Minimum Moves to Equal Array Elements
    441. Arranging Coins
    Girls and Boys
    二分图
    Gap
    SZU-A22
  • 原文地址:https://www.cnblogs.com/cqb-learner/p/5751805.html
Copyright © 2020-2023  润新知