多线程网络(二)

1.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)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self gcdDemo4];
}

#pragma mark - 最常用的组合 - 线程间通讯
// MARK: 最常用的代码!
- (void)gcdDemo4 {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 执行耗时操作!
        NSLog(@"耗时操作 %@", [NSThread currentThread]);
        
        // 更新 UI dispatch_get_main_queue 主队列,专门用来调度任务在主线程上执行的
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"更新UI %@", [NSThread currentThread]);
        });
    });
}

#pragma mark - GCD演练
// MARK: 精简版
/**
 与 NSThread 的对比
 
 1. 所有的代码写在一起的,让代码更加简单,易于阅读和维护
    NSThread 是通过 selector 来调用指定的方法
 2. 使用 GCD 不需要管理线程的创建/销毁/复用的过程!
    如果用 NSThread 显然不好搞!
 */
- (void)gcdDemo3 {
    for (int i = 0; i < 10; ++i) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"%@", [NSThread currentThread]);
        });
    }
}

// MARK: 基本演练 - 同步执行任务不开线程
- (void)gcdDemo2 {
    // 队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 任务
    void (^task)() = ^ {
        NSLog(@"%@", [NSThread currentThread]);
    };
    // 指定执行任务的函数
    dispatch_sync(queue, task);
}

// MARK: 基本演练 - 异步执行任务,开线程
- (void)gcdDemo1 {
    // 1. 队列-全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 2. 任务-用 block 定义一个任务
    void (^task)() = ^ {
        NSLog(@"%@", [NSThread currentThread]);
    };
    
    // 3. 指定任务的"执行函数" -> 同步执行/异步执行
    // 同步 - 这一条指令不执行完,"不会"执行下一条语句,顺序执行,不需要开启线程
    // 异步 - 这一条指令不执行完,"就可以"执行下一条语句,会开启线程
    //  "异步"是多线程的代名词
    dispatch_async(queue, task);
}

@end

2.GCD加载网络图片

#import "ViewController.h"

@interface ViewController () <UIScrollViewDelegate>
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, weak) UIImageView *imageView;
@end

@implementation ViewController

#pragma mark - UIScrollViewDelegate
// "告诉" scrollView 缩放哪一个视图,就应该有"返回值"
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    return self.imageView;
}

/**
 加载视图,和 storyboard & xib 等价的,一旦写了这个方法 storyboard & xib 会实效
 
 创建界面上所有子视图的层次结构!
 */
- (void)loadView {
    
    _scrollView = [[UIScrollView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    // 设置缩放比例
    _scrollView.minimumZoomScale = 0.5;
    _scrollView.maximumZoomScale = 2;
    // 设置代理
    _scrollView.delegate = self;
    
    self.view = _scrollView;
    
    UIImageView *iv = [[UIImageView alloc] init];
    _imageView = iv;
    [self.view addSubview:iv];
}

/**
 视图加载完毕后执行,加载数据
 */
- (void)viewDidLoad {
    [super viewDidLoad];

    // 在后台执行耗时的操作
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 确定网络上唯一的资源
        NSURL *url = [NSURL URLWithString:@"http://d.hiphotos.baidu.com/image/pic/item/0df3d7ca7bcb0a46e18d93706863f6246a60afcf.jpg"];
        
        // 加载图片数据->所有网络上传输的都是"二进制数据"
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 将二进制数据转换成图像
        UIImage *image = [UIImage imageWithData:data];
        
        // 耗时操作执行完成后 - 更新UI - 主线程
        dispatch_async(dispatch_get_main_queue(), ^{
            // 设置图像视图
            self.imageView.image = image;
            // 调整图像视图大小和image一样
            [self.imageView sizeToFit];
            
            // 指定滚动视图的范围
            self.scrollView.contentSize = image.size;
        });
    });
}

@end

3.GCD队列

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
/**
 GCD 核心概念
 
 - 队列-负责"调度"任务
    * "串行"队列 - 一个接一个 - 一个任务不调度完,"不会"调度下一个
    * "并发"队列 - 可以同时的 - 一个任务不调度完,"可以"调度下一个
    * 主队列 - 负责在主线程上调度任务,实现线程间通讯
        注意:主队列不是主线程
    * 全局队列 - 为了方便程序员使用,提供的一个全局队列,可以直接 get
        全局队列就是并发队列,执行效果和自己创建的并发队列完全一样!
    
    * 问题:全局队列&并发队列
        - 全局队列没有名字
            无论 MRC & ARC 都不需要考虑释放
        - 并发队列有名字,和 NSThread 的 name 属性很像
            如果在 MRC 开发时,需要使用 dispatch_release(q); 释放相应的对象
 
        - 如果我们前期日常开发中,建议使用"全局队列"
        - 并发队列,是在专业的应用程序或者第三方框架中使用的
 
 - 任务(名词) - 是用 block 封装的一个代码块
    block 是一组预先转被好的代码,在需要的时候执行!
 
 - 指定任务执行的函数(动词)
    dispatch_async  - 异步执行
    * 这一句话不执行完,就可以执行下一句
    * 不是阻塞式的
    * 异步是多线程的代名词
 
    dispatch_sync   - 同步执行
    * 这一句不执行完,不能执行下一句
    * 是阻塞式的
    * 不会开启线程
 
 - 小结
    - 开不开线程由执行任务的函数决定
        * 异步开,多线程的代名词
        * 同步不开
    - 异步执行的函数,开几条线程由队列决定
        * 串行队列 开一条线程
        * 并发队列 开多条线程
 
 - 队列的选择
    * 多线程的目的:将耗时的操作放在后台执行!
 
    * 串行队列(斯坦福大学的视频),只开一条线程,所有任务顺序执行
        * 如果任务有先后执行顺序的要求
        * 效率低 -> 执行慢,"省电"
        * 有的时候,用户其实不希望太快!使用 3G 流量,"省钱"
 
    * 并发队列,会开启多条线程,所有任务不按照顺序执行
        * 如果任务没有先后执行顺序的要求
        * 效率高 -> 执行快,"费电"
        * WIFI,包月
 
 - 在实际开发中,线程数量如何决定?
    * WIFI 线程数 6 条
    * 3G / 4G 移动开发的时候,2~3条,再多会费电费钱!
 
 - 同步任务的作用 - "依赖"唯一
    在多线程开发的时候,有的时候,需要使用同步任务,挡住后面的异步任务,
    让所有后续的任务,都等待同步任务执行完成之后,才能继续
 
    * 所有后续的任务,都"依赖"最前面的同步任务
    
 "异步是多线程的代名词!"
 */
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self gcdDemo3];
}

#pragma mark - 全局队列
/**
 执行效果:开多个线程,并发执行
 队列的效果:全局队列就是并发队列,执行效果和自己创建的并发队列完全一样!
 */
- (void)gcdDemo8 {
    /**
     参数
     
     1. 服务质量(队列对任务调度的优先级)/iOS 7.0 之前,就是优先级
        - iOS 8.0
            QOS_CLASS_USER_INTERACTIVE 0x21,                用户交互(希望最快完成-不能用太耗时的操作)
            QOS_CLASS_USER_INITIATED 0x19,                  用户期望(希望快,也不能太耗时)
            QOS_CLASS_DEFAULT 0x15,                         默认(用来底层重置队列使用的,不是给程序员用的)
            *** QOS_CLASS_UTILITY 0x11,                     实用工具(专门用来处理耗时操作!)
            QOS_CLASS_BACKGROUND 0x09,                      后台
            QOS_CLASS_UNSPECIFIED 0x00,                     未指定,可以和iOS 7.0 适配
        - iOS 7.0
            DISPATCH_QUEUE_PRIORITY_HIGH 2                  高优先级
            DISPATCH_QUEUE_PRIORITY_DEFAULT 0               默认优先级
            DISPATCH_QUEUE_PRIORITY_LOW (-2)                低优先级
            DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN    后台优先级

        结论:如果要适配 iOS 7.0 & 8.0,使用以下代码:
        dispatch_get_global_queue(0, 0);
     
        提示:不要选择"后台"的优先级,苹果认为,后台优先级的任务,用户不需要关心什么时候执行完!
        ** 任务执行会慢得令人发指!不利于调试!
     
     2. 为未来保留使用的,应该永远传入0
     */
    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    
    for (int i = 0; i < 10; ++i) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    NSLog(@"come here");
}

#pragma mark - 同步任务作用!
- (void)gcdDemo7 {
    /**
     在有的时候,是会希望任务执行过程中有一定的先后顺序 -> 依赖关系
     
     举个例子:登录,下载小说 A,下载小说 B...
     实际目标:下载过程是并发的,但是登录一定要在最前面!
     */
    dispatch_queue_t q = dispatch_queue_create("ios", DISPATCH_QUEUE_CONCURRENT);
    
    // 登录必须第一个执行完,才能执行后序的任务
    dispatch_sync(q, ^{
        NSLog(@"登录 %@", [NSThread currentThread]);
    });
    dispatch_async(q, ^{
        NSLog(@"download A %@", [NSThread currentThread]);
    });
    dispatch_async(q, ^{
        NSLog(@"download B %@", [NSThread currentThread]);
    });
}

#pragma mark - 主队列
// MARK: 主队列,同步执行
/**
 提问:come here?
 猜测:最后/没有!
 答案:死锁!
 */
- (void)gcdDemo6 {
    dispatch_queue_t q = dispatch_get_main_queue();
    NSLog(@"!!!");
    
    dispatch_sync(q, ^{
        NSLog(@"%@", [NSThread currentThread]);
    });
    
    NSLog(@"come here");
}

// MARK: 主队列,异步执行
/**
 提问:执行顺序,会开启线程吗?come here?
 猜测:顺序,不会,最后!
 答案:顺序,不会,最前
 */
- (void)gcdDemo5 {
    // 1. 队列,主队列->主队列是随着程序启动就存在的->主队列不需要创建,可以直接获取
    dispatch_queue_t q = dispatch_get_main_queue();
    
    // 2. "异步"执行
    for (int i = 0; i < 10; ++i) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    NSLog(@"睡会");
    [NSThread sleepForTimeInterval:2.0];
    NSLog(@"come here");
}

#pragma mark - GCD 演练
// MARK: 并发队列,"同步"任务
/**
 提问:执行顺序,会开启线程吗?come here?
 猜测:顺序,开"一条",不是最后?
 结果:顺序,不开线程,最后
 
 并发队列:可以同时调度多个任务
 同步执行:不会开启线程
 */
- (void)gcdDemo4 {
    // 1. 队列
    dispatch_queue_t q = dispatch_queue_create("ios", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 任务
    for (int i = 0; i < 10; ++i) {
        dispatch_sync(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    
    NSLog(@"come here");
}

// MARK: 并发队列,"异步"任务
/**
 提问:执行顺序,会开启线程吗?come here?
 答案:不是顺序,开"多条",不是最后

 
 并发队列:同时调度多个任务
 异步执行:会开启线程
 */
- (void)gcdDemo3 {
    // 1. 队列
    dispatch_queue_t q = dispatch_queue_create("ios", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 执行任务
    for (int i = 0; i < 10; ++i) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    
    NSLog(@"come here");
}

// MARK: 串行队列,异步任务
/**
 提问:执行顺序,会开启线程吗?come here?
 答案:顺序,开"一条",不是最后
 
 
 原因:
 队列:一个接一个,最多只需要开一个就可以
 任务:异步,会开启线程
*/
- (void)gcdDemo2 {
    // 1. 队列
    dispatch_queue_t q = dispatch_queue_create("ios", NULL);
    
    // 2. 执行任务的函数
    for (int i = 0; i < 10; ++i) {
        NSLog(@"%d------", i);
        
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    NSLog(@"come here");
}

// MARK: 串行队列,同步任务(不用!)
/**
 提问:执行顺序,会开启线程吗?come here?
 答案:顺序,不开,最后!
 
 
 原因:串行队列一个接一个的调度任务  -> 顺序的
      同步执行,本身不需要开线程
 */
- (void)gcdDemo1 {
    // 1. 队列
    /**
     参数:
     1. 队列的名称
     2. 队列的属性 - 指定是什么类型的队列
        DISPATCH_QUEUE_SERIAL           串行
        - NULL
        DISPATCH_QUEUE_CONCURRENT       并发
        - 太长,使用 \ 拼接
     */
    // 以下两句代码是等价的!
//    dispatch_queue_t q = dispatch_queue_create("ios", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t q = dispatch_queue_create("ios", NULL);
    
    // 2. 指定任务执行的函数
    for (int i = 0; i < 10; ++i) {
        NSLog(@"%d------", i);
        
        dispatch_sync(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    NSLog(@"come here");
}

@end

4.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)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self gcdDemo4];
}

/**
 通过 block 的嵌套,可以组合出非常复杂的任务关系!
 
 很难用一些表格或者规则表述出所有的情况!
 要理解队列和任务之间的关系,一定要理清"队列"的特点&"执行任务"函数的特点!
 
 提示:多线程开发要"尽量简单"!不要给自己"埋的太深"!
 */
// MARK: 同步任务增强 - 依赖关系
- (void)gcdDemo4 {
    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    
    void (^task)() = ^ {
        // 1. 异步执行,在其他线程执行
        NSLog(@"===> %@", [NSThread currentThread]);
        
        // 2. 同步任务不开启线程,就在当前线程立即执行
        // 同步任务不执行完,就不能执行下一条指令
        // 同步任务执行完毕,线程就空闲出来,后面的并发完全可以利用!
        dispatch_sync(q, ^{
            NSLog(@"login %@", [NSThread currentThread]);
        });
        
        // 3. 同步执行完毕后,并发执行后续两个任务
        dispatch_async(q, ^{
            NSLog(@"download A %@", [NSThread currentThread]);
        });
        dispatch_async(q, ^{
            NSLog(@"download B %@", [NSThread currentThread]);
        });
    };
    
    dispatch_async(q, task);
}

- (void)gcdDemo3 {
    
    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    
    // 问题: login 在主线程,用户登录也是耗时操作!
    // 因为同步任务不会开启线程 => 同步任务在那一个线程都不会开启线程
    dispatch_sync(q, ^{
        NSLog(@"login %@", [NSThread currentThread]);
    });
    dispatch_async(q, ^{
        NSLog(@"download A %@", [NSThread currentThread]);
    });
    dispatch_async(q, ^{
        NSLog(@"download B %@", [NSThread currentThread]);
    });
}

// MARK:同步任务不死锁
/**
 主队列在调度任务的时候,如果主线程上有任务,就暂时不调度
 
 造成主线程和主队列相互等待,造成死锁
 */
- (void)gcdDemo2 {
    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    
    void (^task)() = ^ {
        NSLog(@"hehe");
        
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"%@", [NSThread currentThread]);
        });
    };
    
    dispatch_async(q, task);
    
    NSLog(@"come here");
}

- (void)gcdDemo1 {
    dispatch_queue_t q = dispatch_get_main_queue();
    
    NSLog(@"hehe");
    
    dispatch_sync(q, ^{
        NSLog(@"%@", [NSThread currentThread]);
    });
}

@end

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)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self delay];
}

// MARK: 延时功能
- (void)delay {
    
    /**
     从现在开始,经过多少纳秒,由"队列"调度异步执行 block 中的代码
     
     参数
     1. when    从现在开始,经过多少纳秒
     2. queue   队列
     3. block   异步执行的任务
     */
    dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
    
    NSLog(@"haha");
//    dispatch_after(when, dispatch_get_main_queue(), ^{
//        NSLog(@"%@", [NSThread currentThread]);
//    });
    
    // 全局队列 延时多长时间后,异步执行某些方法!
//    dispatch_after(when, dispatch_get_global_queue(0, 0), ^{
//        NSLog(@"%@", [NSThread currentThread]);
//    });
    // 串行队列 - 也会开线程!
    dispatch_after(when, dispatch_queue_create("ios", NULL), ^{
        NSLog(@"%@", [NSThread currentThread]);
    });
    
    NSLog(@"come here");
}

@end

6.只执行一次的代码

#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)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self demoOnce];
}

// MARK: 多线程测试 dispatch_once
- (void)demoOnce {
    
    for (int i = 0; i < 10; ++i) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [self once];
        });
    }
}

/**
 有的时候,在程序开发中,有些代码只想从程序启动就只执行一次
 典型的应用场景就是“单例”
 
 有的人会以为单例内部使用的是"互斥锁"!
 */
- (void)once {
    // token 令牌,只要 token == 0,才会执行 block 中的代码
    // dispatch 内部也有一把锁,是能够保证"线程安全"的!而且是苹果公司推荐使用的
    // 应该能够做到,多线程执行的时候,block 同样只会执行一次!
    static dispatch_once_t onceToken;
    
    NSLog(@"come here %ld", onceToken);

    // 同步执行!可以保证后续的代码能够使用到一次性执行后的结果!
    dispatch_once(&onceToken, ^{
        // 只需要执行一次的代码
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"%@", [NSThread currentThread]);
    });
    
    NSLog(@"come here");
}

@end

7.单例

#import <Foundation/Foundation.h>
/**
 "单"例的特点
 
 1. 在内存中只有一个实例
 2. 提供一个全局的访问点-类方法统一方法
 
 目前使用过的单例:
 - UIApplication
 - NSUserDefaults
 - NSFileManager
 - NSNotificationCenter
 ...
 
 目前在 iOS 开发中,单例已经到达被滥用的程度!很多公司的面试题中都要求手写单例!
 */
@interface Singleton : NSObject

/**
 *  用 dispatch_once 建立的对象
 */
+ (instancetype)sharedSingleton;

/**
 *  用互斥锁建立的对象
 */
+ (instancetype)syncSingleton;

@end
#import "Singleton.h"

@implementation Singleton

+ (instancetype)sharedSingleton {
    // 永远只有一个实例,因此需要保存在静态区,随着应用程序启动就分配空间,随着应用程序一起被销毁
    static id instance;

    // 可以保证对象只被实例化一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    
    return instance;
}

+ (instancetype)syncSingleton {
    static id syncInstance;

    @synchronized(self) {
        // 懒加载的写法,不能保证线程安全
        if (syncInstance == nil) {
            syncInstance = [[self alloc] init];
        }
    }
    
    return syncInstance;
}

@end
#import "ViewController.h"
#import "Singleton.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/**
 互斥锁真的没有dispatch_once性能好!
 
 dispatch_once是苹果推荐的一次性执行的解决方案!
 */
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    long largeNumber = 1000 * 10000;
    
    // dispatch_once
    // 开始时间
    CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
    
    for (int i = 0; i < largeNumber; ++i) {
        [Singleton sharedSingleton];
    }
    
    // 结束时间
    CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
    NSLog(@"dispatch once %f", end - start);
    
    // 互斥锁
    start = CFAbsoluteTimeGetCurrent();
    for (int i = 0; i < largeNumber; ++i) {
        [Singleton syncSingleton];
    }
    end = CFAbsoluteTimeGetCurrent();
    NSLog(@"互斥锁 %f", end - start);
}

@end

8.调度组

#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)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self group2];
}

// MARK: 调度组
/**
  AFN,内部使用到了 enter & leave 代码!
 
 终端输入
 $ man dispatch_group_enter
 
void dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block)
{
    dispatch_retain(group);
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        block();
        dispatch_group_leave(group);
        dispatch_release(group);
    });
}
*/
- (void)group2 {
    // 实例化调度组
    dispatch_group_t group = dispatch_group_create();
    
    // 队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 进入组(增加一个标记-后面再定义的 block,就会被添加到组中)
    // *** dispatch_group_enter & dispatch_group_leave 必须成对出现
    // *** dispatch_group_leave 必须是最后一句
    dispatch_group_enter(group);
    // 定义任务
    void (^task1)() = ^ {
        NSLog(@"download A %@", [NSThread currentThread]);

        // "当所有任务完成之后",离开组->group 就能够知道任务完成了!
        dispatch_group_leave(group);
    };
    
    dispatch_group_enter(group);
    // 定义任务
    void (^task2)() = ^ {
        NSLog(@"download B %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:2.0];
        
        // "当所有任务完成之后",离开组->group 就能够知道任务完成了!
        dispatch_group_leave(group);
    };
    
    // 将任务添加到队列
    dispatch_async(queue, task1);
    dispatch_async(queue, task2);

    // 监听群组任务的结束
//    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//        NSLog(@"OVER");
//    });
    // 等待调度组中所有任务执行完毕,时间是永远->死等,一直要等到所有任务执行完成
    // dispatch_group_wait 等待方式是同步的(知道就行!)
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    NSLog(@"come here");
}

// MARK: 调度组
- (void)group1 {
    // 实例化调度组
    dispatch_group_t group = dispatch_group_create();
    
    // 队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 添加任务
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"download A, %@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:0.8];
        NSLog(@"download B, %@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:0.9];
        NSLog(@"download C, %@", [NSThread currentThread]);
    });
    
    // dispatch_group_notify 可以监听调度组中所有的异步任务,在执行完毕后,获得通知!
    // 监听工作是异步的!
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 监听到所有任务执行完成后,在主线程执行 block
        // 以下载小说为例:可以异步下载小说,下载完毕后,在主线程更新UI
        NSLog(@"OVER %@", [NSThread currentThread]);
    });
    NSLog(@"come here");
}

@end

9.时钟

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
/**
 运行循环 
 
 作用:
 1. 保证程序不会退出
 2. 监听所有的事件:触摸、时钟、网络事件
 3. 如果没有任何事件发生,会进入休眠状态,省电
 4. 一旦有事件发生,运行循环会再次启动,并且处理事件
 
 在"今天"的实际开发中,运行循环几乎用不倒,概念非常重要!
 
 runloop是在iOS开发的原始时代,需要大量使用的!
 */
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 就是用 default 模式,将时钟添加到运行循环!
//    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
    // 将时钟添加到运行循环
    /**
     NSDefaultRunLoopMode - 默认的运行循环模式:监听时钟,网络等跟UI无关的事件
     NSRunLoopCommonModes - 通用的运行循环模式:监听用户交互事件,优先级最高,保证用户一旦交互,立即做出响应!
     */
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

/**
 时钟的触发方法是同步的还是异步的 => 并没有开启线程,是同步的!
 
 一定需要注意的,时钟触发方法中,不能有太耗时的操作!否则会造成主线程的卡顿!
 */
- (void)updateTimer {

    static int num = 0;
    NSLog(@"");
    [NSThread sleepForTimeInterval:1.0];
    
    NSLog(@"%d %@", num++, [NSThread currentThread]);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

10.时钟(多线程)

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
/**
 运行循环 
 
 作用:
 1. 保证程序不会退出
 2. 监听所有的事件:触摸、时钟、网络事件
 3. 如果没有任何事件发生,会进入休眠状态,省电
 4. 一旦有事件发生,运行循环会再次启动,并且处理事件
 
 在"今天"的实际开发中,运行循环几乎用不倒,概念非常重要!
 
 runloop是在iOS开发的原始时代,需要大量使用的!
 
 NSDefaultRunLoopMode - 默认的运行循环模式:监听时钟,网络等跟UI无关的事件
 NSRunLoopCommonModes - 通用的运行循环模式:监听用户交互事件,优先级最高,保证用户一旦交互,立即做出响应!

 */
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 将时钟放在异步触发
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"开启时钟 %@", [NSThread currentThread]);
        
        NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
        
        // 如果要监听事件,需要启动子线程的运行循环!仅供了解
        // 这句话是真正的死循环!启动运行循环,不再执行后续的代码!
        // 如果用 run 直接启动运行循环,很难停掉循环!线程永远不会被终止
        [[NSRunLoop currentRunLoop] run];
        
        NSLog(@"come here");
    });
}

- (void)updateTimer {

    static int num = 0;
    NSLog(@"");
    [NSThread sleepForTimeInterval:1.0];
    
    NSLog(@"%d %@", num++, [NSThread currentThread]);
}

// 停止时钟
- (IBAction)stopTimer:(id)sender {
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

11.停止运行循环

#import "ViewController.h"

@interface ViewController ()
// 标记是否停止
@property (nonatomic, assign, getter=isFinished) BOOL finished;
@end

@implementation ViewController
/**
 运行循环 
 
 作用:
 1. 保证程序不会退出
 2. 监听所有的事件:触摸、时钟、网络事件
 3. 如果没有任何事件发生,会进入休眠状态,省电
 4. 一旦有事件发生,运行循环会再次启动,并且处理事件
 
 在"今天"的实际开发中,运行循环几乎用不倒,概念非常重要!
 
 runloop是在iOS开发的原始时代,需要大量使用的!
 
 NSDefaultRunLoopMode - 默认的运行循环模式:监听时钟,网络等跟UI无关的事件
 NSRunLoopCommonModes - 通用的运行循环模式:监听用户交互事件,优先级最高,保证用户一旦交互,立即做出响应!

 */
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 将时钟放在异步触发
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"开启时钟 %@", [NSThread currentThread]);
        
        NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
        
        // 如果要监听事件,需要启动子线程的运行循环!仅供了解
        self.finished = NO;
        // 手写了一个死循环
        // 问题:性能不好!这种方法是通过 OC 停止运行循环使用最广泛的方法。
        while (!self.isFinished) {
            // 让运行循环监听 0.1 秒的事件
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
        }
        
        // 一旦执行到这句话,就说明线程要死亡了!
        NSLog(@"come here");
    });
}

- (void)updateTimer {

    static int num = 0;
    NSLog(@"");
    [NSThread sleepForTimeInterval:1.0];
    
    NSLog(@"%d %@", num++, [NSThread currentThread]);
}

// 停止时钟
- (IBAction)stopTimer:(id)sender {
    self.finished = YES;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

12.CF停止运行循环

#import "ViewController.h"

@interface ViewController ()
/** 时钟线程所在的运行循环 */
@property (nonatomic, assign) CFRunLoopRef timerRunloop;
@end

@implementation ViewController
/**
 运行循环 
 
 作用:
 1. 保证程序不会退出
 2. 监听所有的事件:触摸、时钟、网络事件
 3. 如果没有任何事件发生,会进入休眠状态,省电
 4. 一旦有事件发生,运行循环会再次启动,并且处理事件
 
 在"今天"的实际开发中,运行循环几乎用不倒,概念非常重要!
 
 runloop是在iOS开发的原始时代,需要大量使用的!
 
 NSDefaultRunLoopMode - 默认的运行循环模式:监听时钟,网络等跟UI无关的事件
 NSRunLoopCommonModes - 通用的运行循环模式:监听用户交互事件,优先级最高,保证用户一旦交互,立即做出响应!

 */
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 将时钟放在异步触发
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"开启时钟 %@", [NSThread currentThread]);
        
        NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
        
        // 如果要监听事件,需要启动子线程的运行循环!仅供了解
        // 使用 CoreFoundation 是 C 语言的框架
        // 以下代码,是绝大多数第三方框架中使用的方法!
        self.timerRunloop = CFRunLoopGetCurrent();
        // 启动运行循环
        // 利用底层的方式启动运行循环,不需要程序员再写 while 死循环,就能够利用 runloop 本身的特性
        // 没有事件自动休眠
        CFRunLoopRun();

        // 一旦执行到这句话,就说明线程要死亡了!
        NSLog(@"come here");
    });
}

- (void)updateTimer {

    static int num = 0;
    NSLog(@"");
    [NSThread sleepForTimeInterval:1.0];
    
    NSLog(@"%d %@", num++, [NSThread currentThread]);
}

// 停止时钟
- (IBAction)stopTimer:(id)sender {
    // 让时钟所在线程的运行循环停止
    CFRunLoopStop(self.timerRunloop);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

13.自动释放池

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic, copy) NSString *name;

+ (instancetype)personWithName:(NSString *)name;

@end
#import "Person.h"

@implementation Person
/**
 有alloc/init就需要有release
 */
+ (instancetype)personWithName:(NSString *)name {
    // autorelease 的目的->延迟释放
    // autorelase的对象出了作用域才会被添加到自动释放池中->达到延迟释放
//    Person *p = [[[self alloc] init] autorelease];
    Person *p = [[self alloc] init];
    
    p.name = name;

//    [p release];
    return p;
//    [p release];
}

@end
#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)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

// 一个大数
long largeNumber = 1000;

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /** 
     问题:以下代码有什么问题?
     如果有?如何解决?
     有几种解决方式?请简述各种方式的优缺点及效率!
     
     在 Foundation 框架中,如果是返回对象的类方法,对象都是自动释放的 - autorelease 的对象
     
     答案:
     1. 每一个str 都是自动释放的,如果循环次数很大,会占用过多的自动释放池空间!甚至让自动释放池耗尽!
     
     2. 解决方法:网络上有两种解决方法!
     1> 在for循环外面使用自动释放池,一个运行循环不仅仅只有一个for,前后都会有代码
        结果:在for循环结束后,倾倒自动释放池!
     
     2> 在for循环内使用自动释放池
        结果:内存非常的平缓!
     
     3. 哪一个执行效率高!
        网上答案统一是外面快!
     
            
     在实际工作中,应该毫不犹豫的使用自动释放池!
     */
    CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
    [self question1];
    CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
    NSLog(@"%f", end - start);
    
    start = CFAbsoluteTimeGetCurrent();
    [self question2];
    end = CFAbsoluteTimeGetCurrent();
    NSLog(@"%f", end - start);
}

- (void)question2 {
    NSLog(@"开始");
    for (int i = 0; i < largeNumber; i++) {
        @autoreleasepool {
            NSString *str = [NSString stringWithFormat:@"Hello - %04d", i];
            str = [str stringByAppendingString:@" - World"];
            str = [str uppercaseString];
        }
    }
    NSLog(@"内 --- 结束");
}

- (void)question1 {
    NSLog(@"开始");
    @autoreleasepool {
        for (int i = 0; i < largeNumber; i++) {
            NSString *str = [NSString stringWithFormat:@"Hello - %04d", i];
            str = [str stringByAppendingString:@" - World"];
            str = [str uppercaseString];
        }
    }
    NSLog(@"外 --- 结束");
}

- (void)question {
    // 问题:以下代码有什么问题?如果有?如何解决?有几种解决方式?请简述各种方式的优缺点及效率!
    for (int i = 0; i < largeNumber; i++) {
        NSLog(@"----- %d", i);
        
        NSString *str = [NSString stringWithFormat:@"Hello - %04d", i];
        NSLog(@"%p", str);
        str = [str stringByAppendingString:@" - World"];
        NSLog(@"%p", str);
        str = [str uppercaseString];
        NSLog(@"%p", str);
        
        NSLog(@"%@", str);
    }
}

@end

 

posted @ 2015-11-12 15:00  ios开发那点事  阅读(167)  评论(0编辑  收藏  举报