D30:Block, GCD方式实现多线程

一. block实现反向传值

二. block封装下载类型

三. GCD基本概念

GCD是iOS4之后的技术, 是基于C语言来实现的, 速度更快, 效率更高
通常使用GCD来实现多线程

四. GCD的使用

五. GCD下载图片

六. GCD扩展

七. 抓包(选项目)

这里使用paros.jar来抓包
下载安装JAVA SDK环境: Oracle官网: Downloads - JAVA SE - SDK - MACOS 64bit
如果还是无法打开paros.jar, 请将paros相关文件放至在英文目录下

获取电脑在当前局域网内的IP: 192.168.80.15
保证手机和电脑在同一个网段下
a) 连上同一个wifi
b) 在电脑上设一个热点, 手机连接该热点

手机进入Wi-Fi界面, 进入连接的网络, 滑到最下方, 设置HTTP代理, 改为手动模式, 服务器设为电脑的IP, 设置一个较大的端口, 如8080或8888(charles抓包软件默认端口)
此时打开paros.jar即可检测到设备的网络活动

a)打开iTunes,进入apple store界面
b)选中一个应用,点击进入应用的详情介绍界面
c)点击“获取”按钮,下载这个应用
d)下载完成之后,找到下载的这个应用,右击选择“show In Finder”,显示ipa文件
e)右击ipa文件->打开方式->归档工具
f)在生成的文件夹下面,有一个payload文件夹,选中文件夹下面的文件,右击->显示包内容,这样就能够看到应用所用的资源文件

1)html文件,three20第三方库,直接用UIWebView显示这个链接
2)有些应用Andriod和iphone的接口不一样


一. block实现反向传值

1. 实现常规的导航控制器push跳转页面功能
//
//  ViewController.m
//  01_BlockReverseValues

#import "ViewController.h"
#import "MyUtility.h"
#import "DetailViewController.h"

@interface ViewController ()

@property (nonatomic, strong) UILabel *label;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    // 显示文字
    _label = [[UILabel alloc] initWithFrame:CGRectMake(50, 100, 100, 40)];
    [self.view addSubview:_label];
    
    // 创建按钮
    UIButton *btn = [MyUtility createButtonWithFrame:CGRectMake(100, 200, 80, 40) title:@"跳转" backgroundImageName:nil target:self action:@selector(gotoNextPage)];
    [self.view addSubview:btn];
    
}

- (void)gotoNextPage
{
    DetailViewController *dvc = [[DetailViewController alloc] init];
    [self.navigationController pushViewController:dvc animated:YES];
    
    };
}

//
//  DetailViewController.h
//  01_BlockReverseValues  

#import <UIKit/UIKit.h>

@interface DetailViewController : UIViewController

@end

#import "DetailViewController.h"
#import "MyUtility.h"

@implementation DetailViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    
    [self createBtns];
}

- (void)createBtns
{
    UIButton *btn1 = [MyUtility createButtonWithFrame:CGRectMake(100, 100, 80, 40) title:@"Button1" backgroundImageName:nil target:self action:@selector(clickBtn:)];
    UIButton *btn2 = [MyUtility createButtonWithFrame:CGRectMake(100, 200, 80, 40) title:@"Button2" backgroundImageName:nil target:self action:@selector(clickBtn:)];
    UIButton *btn3 = [MyUtility createButtonWithFrame:CGRectMake(100, 300, 80, 40) title:@"Button3" backgroundImageName:nil target:self action:@selector(clickBtn:)];
    [self.view addSubview:btn1];
    [self.view addSubview:btn2];
    [self.view addSubview:btn3];
}
    
@end
2. 增加利用block传值的代码
  1. ViewController的gotoNextPage方法
    - (void)gotoNextPage
    {
    DetailViewController *dvc = [[DetailViewController alloc] init];
    [self.navigationController pushViewController:dvc animated:YES];

         // 给block赋值
         __weak ViewController *weakSelf = self;
         dvc.clickBlock = ^(NSString *title){
             // 修改Label上面的文字
             weakSelf.label.text = title;
         };
     }
    
  2. 为DetailViewController类增加一个block属性, 在点击按钮执行的方法中由该block属性来处理需要反向传递的值

     @interface DetailViewController : UIViewController
     
     // block传值
     @property (nonatomic, copy) void (^clickBlock)(NSString *param);
     
     @end
    
     @implementation DetailViewController
     
     - (void)clickBtn:(UIButton *)sender
     {
         NSString *title = sender.currentTitle;
         // [sender titleForState:UIControlStateNormal];
         
         // 使用
         if (self.clickBlock) {
             self.clickBlock(title);
         }
     }  
    

二. block封装下载类型

1. 按照常规步骤编写MyDownloader类, 下载结束和下载失败的代码暂不处理
//
//  MyDownloader.h
//  02_BlockDownloader

#import <Foundation/Foundation.h>

@interface MyDownloader : NSObject <NSURLConnectionDataDelegate, NSURLConnectionDelegate>

// 下载数据
- (void)downloadWithURLString:(NSString *)urlString

@end  
  
//
//  MyDownloader.m
//  02_BlockDownloader
  
#import "MyDownloader.h"

@implementation MyDownloader
{
    // 下载数据
    NSMutableData *_receiveData;
    // 下载对象
    NSURLConnection *_connection;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        // 初始化下载数据
        _receiveData = [NSMutableData data];
    }
    return self;
}

- (void)downloadWithURLString:(NSString *)urlString
{
    _connection = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]  delegate:self];
}

#pragma mark - NSURLConnection
// 下载失败
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    
}

// 每次数据下载回来后
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [_receiveData appendData:data];
}

// 请求响应
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    [_receiveData setLength:0];
}

// 下载成功
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    
}

@end
2. 修改MyDownload类: 修改下载数据的公开方法, 令其包含参数类型为NSData(下载结束所得的数据类型)的block参数和 参数类型为NSError(下载失败所得的数据类型的)block参数, 并各自增加该类型的block作为成员变量
// 把变量名去掉就是block的类型
// 下载数据
- (void)downloadWithURLString:(NSString *)urlString finishBlock:(void (^)(NSData *data))finishBlock failBlock:(void (^)(NSError *error))failBlock;  

//
//  MyDownloader.m
//  02_BlockDownloader

#import "MyDownloader.h"

@implementation MyDownloader
{
    …………………………………………………………………………………………
        
    // 声明
    // 下载成功
    void (^_finishBlock)(NSData *data);
    // 下载失败
    void (^_failBlock)(NSError *error);
}

…………………………………………………………………………………………

- (void)downloadWithURLString:(NSString *)urlString finishBlock:(void (^)(NSData *))finishBlock failBlock:(void (^)(NSError *))failBlock
{
    // block的赋值
    // 给block成员变量赋值
    if (_finishBlock != finishBlock) {
        _finishBlock = nil;
        _finishBlock = finishBlock;
    }
    
    if (_failBlock != failBlock) {
        _failBlock = nil;
        _failBlock = failBlock;
    }
    
    _connection = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]  delegate:self];
}

#pragma mark - NSURLConnection
// 下载失败
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    // 调用
    if (_failBlock) {
        _failBlock(error);
    }
}

// 每次数据下载回来后
…………………………………………………………………………………………

// 请求响应
…………………………………………………………………………………………

// 下载成功
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    // 调用
    if (_finishBlock) {
        _finishBlock(_receiveData);
    }
}

@end
3. 在ViewController.m中导入MyDownloader类使用
#import "ViewController.h"
#import "MyDownloader.h"

// 下载链接
#define kLimitUrl (@"http://iappfree.candou.com:8080/free/applications/limited?currency=rmb&page=1")

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    MyDownloader *downloader = [[MyDownloader alloc] init];
    
    [downloader downloadWithURLString:kLimitUrl finishBlock:^(NSData *data) {
        
        // 解析数据
        id result = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
        if ([result isKindOfClass:[NSDictionary class]]) {
            NSDictionary *dict = result;
            
            NSArray *array = dict[@"applications"];
            NSLog(@"%@", array);
        }
        
    } failBlock:^(NSError *error) {
        NSLog(@"%@", error);
    }];
}

三. GCD基本概念

  1. GCD的队列
  1. 主线程所在的串行队列
    串行队列的意思是前面的代码执行完成, 才开始执行后面的代码
    并行队列是前面的代码和后面的代码同时开始执行, 并行运行的方式效率更高
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
  2. 全局的并行队列
  3. 自己创建的队列:
    • 并行队列
    • 串行队列
  1. 怎样实现线程
  1. 同步的提交(不能让主线程(串行)同步执行一段代码, 程序会卡死)

  2. 异步的提交

    • (void)viewDidLoad {
      [super viewDidLoad];
      // Do any additional setup after loading the view, typically from a nib.

      // GCD实现多线程是将代码块(线程的执行体)提交到队列里面执行

      // 1. GCD的队列
      // 1) 主线程所在的串行队列
      // 串行队列的意思是前面的代码执行完成, 才开始执行后面的代码
      // 并行队列是前面的代码和后面的代码同时开始执行, 并行运行的方式效率更高
      dispatch_queue_t mainQueue = dispatch_get_main_queue();

      // 2) 全局的并行队列
      /*
      第一个参数: 队列的优先级
      第二个参数: 是apple预留的一个参数, 传0即可
      */
      dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

      // 3) 自己创建的队列
      /*
      第一个参数: 队列的标识符
      第二个参数: 用来区分串行还是并行
      DISPATCH_QUEUE_SERIAL 串行
      DISPATCH_QUEUE_CONCURRENT 并行
      */
      // a) 串行队列
      dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
      // b) 并行队列
      dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

      // 2. 怎样实现线程
      // 1) 同步的提交(不能让主线程(串行)同步执行一段代码, 程序会卡住直到代码运行完成)
      /*
      第一个参数: 线程在哪个队列执行
      第二个参数: 代码块, 线程的执行体
      */
      dispatch_sync(serialQueue, ^{
      for (int i = 0; i < 100; i++) {
      NSLog(@"执行了任务1: %d", i);
      }
      });

      // 2) 异步的提交
      /*
      第一个参数: 线程在哪个队列执行
      第二个参数: 代码块, 线程的执行体
      */
      dispatch_async(mainQueue, ^{
      for (int i = 0; i < 100; i++) {
      NSLog(@"执行了任务2: %d", i);
      }
      });
      }


四. GCD的使用

1. 以同步的方式向串行队列提交代码
/*
 前面的线程执行完毕之后, 才开始执行后面的线程
 */
- (void)testSyncMethodWithSerialQueue
{
    // 创建一个串行的队列
    dispatch_queue_t serialQueue = dispatch_queue_create("Queue1", DISPATCH_QUEUE_SERIAL);
    
    // 以同步的方式提交两个代码块
    dispatch_sync(serialQueue, ^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"%@执行了任务1: %d", [NSThread currentThread], i);
        }
    });
    
    dispatch_sync(serialQueue, ^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"%@执行了任务2: %d", [NSThread currentThread], i);
        }
    });
}
2. 以同步的方式向并行队列提交代码
/*
 前面的线程执行完毕之后, 才开始执行后面的线程
 */
- (void)testSyncMethodWithConcurrentQueue
{
    // 获取全局队列
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 以同步的方式创建两个代码块
    dispatch_sync(globalQueue, ^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"%@执行了任务1: %d", [NSThread currentThread], i);
        }
    });
    
    dispatch_sync(globalQueue, ^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"%@执行了任务2: %d", [NSThread currentThread], i);
        }
    });
}
3. 以异步的方式向串行队列提交代码
- (void)testAsyncMethodWithserialQueue
{
    // 创建串行队列
    dispatch_queue_t serialQueue = dispatch_queue_create("queueThree", DISPATCH_QUEUE_SERIAL);
    
    // 异步提交两个代码块
    dispatch_async(serialQueue, ^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"%@执行了任务1: %d", [NSThread currentThread], i);
        }
    });
    
    dispatch_async(serialQueue, ^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"%@执行了任务2: %d", [NSThread currentThread], i);
        }
    });
}
4. 以异步的方式向并行队列提交代码
- (void)testAsyncMethodWithConcurrentQueue
{
    // 创建并行队列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("queueFour", DISPATCH_QUEUE_CONCURRENT);
    
    // 异步提交两段代码块
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"%@执行了任务1: %d", [NSThread currentThread], i);
        }
    });
    
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"%@执行了任务2: %d", [NSThread currentThread], i);
        }
    });
}

五. GCD下载图片

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    // 使用GCD下载图片
    // @"http://img3.3lian.com/2006/027/08/007.jpg"
    
    // 获取全局并行队列
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 向队列中异步提交代码块
    __weak ViewController *weakSelf = self;
    
    dispatch_async(globalQueue, ^{
        
        // 下载图片
        NSURL *url = [NSURL URLWithString:@"http://img3.3lian.com/2006/027/08/007.jpg"];
        // 下载数据
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 下载完成后回到主线程修改UI
        dispatch_async(dispatch_get_main_queue(), ^{
            
            UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(40, 100, 240, 320)];
            imageView.image = [UIImage imageWithData:data];
            [weakSelf.view addSubview:imageView];
            
        });
    });
}

六. GCD扩展

1. 代码执行多次
- (void)testMultiTimes
{
    /*
     第一个参数: 执行的次数
     第二个参数: 代码执行体所在的队列
     第三个参数: 线程的执行体(代码块)
     */
    dispatch_apply(5, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t time) {
        // time 表示现在执行的是第几次
        NSLog(@"执行第%zu次", time);
    });
    /*
     执行结果是无序的
     2015-06-12 14:28:25.975 06_GCDMore[1195:23542] 执行第0次
     2015-06-12 14:28:25.975 06_GCDMore[1195:23643] 执行第1次
     2015-06-12 14:28:25.975 06_GCDMore[1195:23644] 执行第2次
     2015-06-12 14:28:25.976 06_GCDMore[1195:23542] 执行第4次
     2015-06-12 14:28:25.975 06_GCDMore[1195:23645] 执行第3次
     */
}  
2. 代码在一段时间之后运行
- (void)testGCDAfter
{
    NSLog(@"now");
    
    // 时间值
    // 十秒之后
    dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 10);
    
    /*
     第一个参数: 线程执行的时间
     第二个参数: 线程执行的队列
     第三个参数: 线程的执行体
     */
    dispatch_after(t, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"after");
    });
}
3. 代码在程序运行期间只执行一次
// 通常用来实现单例
- (void)testGDCOnlyOnce
{
    static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken, ^{
        NSLog(@"代码执行1次");
    });
}
4. Group: 在几个线程都执行完成之后需要(才)做一些操作
- (void)testGDCGroup
{
    // 创建一个Group
    dispatch_group_t myGroup = dispatch_group_create();
    
    // 队列
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 执行两个线程
    /* 
     第一个参数: group
     第二个参数: 队列
     第三个参数: 线程的执行体
     */
    dispatch_group_async(myGroup, globalQueue, ^{
        
        for (int i = 0; i < 100; i++) {
            NSLog(@"执行了线程1:%d", i);
        }
        
    });
    
    dispatch_group_async(myGroup, globalQueue, ^{
        
        for (int i = 0; i < 100; i++) {
            NSLog(@"执行了线程2:%d", i);
        }
        
    });
    
    // 在两个线程都执行完成之后, 执行一些操作
    dispatch_group_notify(myGroup, globalQueue, ^{
        NSLog(@"两个线程都执行完成");
        NSLog(@"%s", __func__);
    });
}
5. 让所有的线程分成2部分
- (void)testGCDBarrier
{
    // 并行队列
    // 这种方式只能使用自己创建的并行队列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"执行了线程1:%d" ,i);
        }
    });
    
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"执行了线程2:%d" ,i);
        }
    });
    
    dispatch_barrier_async(concurrentQueue, ^{
        NSLog(@"Barrier");
    });
    
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"执行了线程3:%d" ,i);
        }
    });
    
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"执行了线程4:%d" ,i);
        }
    });
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容