美文网首页
多线程2

多线程2

作者: project_DLQ | 来源:发表于2016-10-18 18:00 被阅读0次

Block基本演练

@interface ViewController ()

//把block定义成全局属性
@property (nonatomic,copy) void(^task)();

@end

/**
 Block是一段代码块,只在被调用时执行,类似于函数和方法
 Block是一个匿名函数,只有函数体,没有函数名
 Bolck是一种数据类型,类似于int / NSString ...
 可以定义成临时变量,可以直接调用的临时变量
 可以当做参数传递,可以传递到另外的方法或者类里面去调用
 可以定义成全局属性,但是需要使用copy修饰符
 block也是一个指向函数的指针对象
 
 */
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //调用函数
   // demo();
    
    //定义block
    void(^task)() = ^{
        NSLog(@"haha");
    };
    
    //保存代码块
    self.task = task;
}
#pragma mark - 可以当做参数传递,可以传递到另外的方法
-(void)blockDemo2{
    //定义block
    void (^task)() = ^{
        NSLog(@"blockDemo2 - hello");
    };
    [self callBack:task];
}
//接收block的方法
-(void)callBack:(void (^)())task {
    //调用外界传入的task
    task();
}

#pragma mark - 定义临时变量, 可以直接调用的临时变量
//void(^)() 表示block的类型,类似于int
//void (^task) 表示给block起个变量名task
//提示:如果不知道Block怎么写,可以先写个`int num = 10; `,然后照着写
//注意点:一定要会手写无参无返回值的Block;(笔试题经常遇到)
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //[self blockDemo1];
    self.task();
}
-(void)blockDemo1{
//    <#returnType#>(^<#blockName#>)(<#parameterTypes#>) = ^(<#parameters#>) {
//        <#statements#>
//    };
    //定义一个无参无返回值的Block / 代码块
    void(^task)() = ^{
        NSLog(@"blockDemo1 - hello");
    };
    //调用Block : 跟函数的调用是一样的
    task();
}
//定义一个无参无返回值函数
void demo(){
    NSLog(@"demo");
}

block反向传值


主界面

  1. 准备等待执行的代码块
  2. 如果需要接收外界传入的值,需要定义参数
  3. 向目标控制器传递代码块
#import "ViewController.h"
#import "DetailViewController.h"
@interface ViewController ()
//展示用户名
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;

@end

@implementation ViewController

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

//监听push按钮的点击事件
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    //NSLog(@"%s",__func__);
    
    //1.获取目标控制器(deatilVC)
    DetailViewController *vc = segue.destinationViewController;
    //判断目标控制器是否为空
    if(vc != nil){
        
        //2.准备代码块:等待详情调用时才执行
        void(^completion)(NSString *) = ^(NSString *name){
            self.nameLabel.text = name;
        };
        //3.向deatilVC传递代码块
        vc.completion = completion;
    }
}

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


@end

detail界面

  1. 准备属性接收代码块
  2. 回调代码块
#import "DetailViewController.h"

@interface DetailViewController ()
//获取name的空间
@property (weak, nonatomic) IBOutlet UITextField *nameTextField;

@end

@implementation DetailViewController
//保存按钮的点击事件
- (IBAction)save:(UIBarButtonItem *)sender {
    //NSLog(@"点击事件");
    
    //1.把name传递到主页控制器
    if(self.completion != nil){
        //回调主页传入的代码块
        self.completion(self.nameTextField.text);
    }
    //2.pop主页控制器
    [self.navigationController popViewControllerAnimated:YES];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

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

Block相关

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self blockDemo2];
}
#pragma mark - 以下面试题仅做参考
-(void)blockDemo3{
    
    NSMutableString *strM = [NSMutableString stringWithString:@"hello"];
    void(^block)() = ^{
        //strM 本来就在堆区
        [strM appendString:@"hehe"];
    };
    block();
}

#pragma mark - 当在Block内部"修改"外部变量时

/**
 1. 当在Block内部"修改"外部变量,不被允许
 2. 如果非要在Block内部修改外部变量,需要使用__block修饰外部变量
 3. __block修饰外部变量作用:使外部变量可以在Block内部修改
 4. 被__block标记的外部变量,一旦在block内部使用过(访问或修改),那么block对外部变量的拷贝就不是临时的了;block外部变量的真实值就会发生变化
 5. 为什么在block内部不能去访问外部的一个变量?
    因为block设计用来做数据的传递的,block一般会传递到另外的类里面做回调,如果block内部的变量在栈区,那么block在传递的过程中,它内部的变量容易丢失
 
 */
-(void)blockDemo2{
    //使用__block标记外部变量
    __block int num = 10;
    NSLog(@"===>%p - %d",&num,num);
    void(^task)() = ^{
        num = 30;//在block内部不能直接修改外部变量 如果非要修改需要使用__block修饰外部变量
        NSLog(@"%d",num);
        NSLog(@"===>%p - %d",&num,num);
    };
    num = 20;
    NSLog(@"===>%p - %d",&num,num);
    task();
}
#pragma mark - 当在block内部访问外部变量时

/**
 1. 当在block内部"访问"外部变量时,block会对外部的变量进行一次临时的拷贝
 2. 临时拷贝的结果: 把栈区的地址拷贝到堆区
 3. 其实,在block内部操作的是副本(临时拷贝出来的那一份),对block外部的变量的真实值不会有影响
 */
-(void)blockDemo1{
    int num  = 10;
    NSLog(@"===>%p - %d",&num,num);//===>0x7fff5c1d163c - 10 栈区
    //其实在定义代码块时,就已经完成了拷贝
    void(^task)() = ^{
        NSLog(@"%d",num);
        NSLog(@"===>%p - %d",&num,num);//===>0x6000000456f0 - 10 堆区
    };
    num = 20;//===>0x7fff5c1d163c - 20 栈区
    NSLog(@"===>%p - %d",&num,num);
    task();
}

Block内存管理

解决Block定义成属性为什么使用copy修饰符

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self blockDemo3];
}

#pragma mark -为什么Block定义成属性使用copy修饰符?MRC 演示

/**
 MRC :block存储在栈区,如果block想全局共享,那么需要在堆区
 MRC :定义block属性使用copy的原因,是为了在赋值时,在setter方法内部,会自动把栈区的Block拷贝到堆区
 ARC :block存储在堆区,所以,定义block属性,可以选择strong或者copy
 */
-(void)blockDemo3{
    int num = 10;
    void(^task)() = ^{
        NSLog(@"hello %d",num);
    };
    //MRC : 保存代码块: 以下代码在执行时,会调用setter方法,在setter方法内部会自动的把栈区的block拷贝到堆区
    self.task = task;
    //MRC :地址由栈区变到堆区
    NSLog(@"%@ -  %@",task , self.task);
    
}

#pragma mark - 函数体会发生变化
/**
 1.ARC:当函数体会发生变化时,Block存储在堆区
 2.MRC:当函数体会发生变化时,Block存储在栈区
 3.提示:Block是在ios4.0时引入的;那时候,是MRC开发环境
 */
-(void)blockDemo2{
    int num = 10;
    void(^task)() = ^{
        NSLog(@"hello %d",num);
    };
    //ARC : 堆区
    //MRC : 栈区
    NSLog(@"%@",task);
}


#pragma mark - 函数体不会发生变化

/**
 当block的函数体不会去发生变化时,无论是ARC还是MRC,内存都存储在全局区
 */
-(void)blockDemo1{
    
    void(^task)() = ^{
        NSLog(@"hello");
    };
    //ARC :<__NSGlobalBlock__: 0x10c4d7090> :全局
    NSLog(@"%@",task);
}

block的循环引用

@interface ViewController ()

@property (nonatomic,copy) void(^task)();
@property (nonatomic,strong) NSMutableArray *array;

@end

@implementation ViewController{
    NSMutableArray *_arr;
}

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

/**
 不要在block内部使用成员变量
 */
-(void)blockDemo2{
    //解决 :使用__weak 修饰 self
    __weak typeof(self) weakSelf = self;
    
    //__weak ViewController *weakSelf = self;
    //block内部访问了`self`,会对`self`进行拷贝(强引用)
    self.task = ^{
        NSLog(@"%@ - %@",weakSelf.view,_arr);//在block内部建议不要使用成员变量,因为会有循环引用,但是不好发现 即使发现了,不好解决
    };
    //self.task = task;
}



/**
 提示:一旦在block内部发现了  `self.` ,需要注意是否有循环引用发生
 解决循环引用的思路:不让block在内部对self强引用
 使用__weak 修饰 self
 __weak :标记若引用;__strong:标记强引用;
 __weak:就是告诉Block不要对self 进行强引用
 */
-(void)blockDemo{
    //解决 :使用__weak 修饰 self
    __weak typeof (self) weakSelf = self;
    
    //__weak ViewController *weakSelf = self;
    //block内部访问了`self`,会对`self`进行拷贝(强引用)
    void(^task)() = ^{
        NSLog(@"%@",weakSelf.view);
    };
    self.task = task;
}
-(void)dealloc{
    NSLog(@"%s",__func__);
}

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


@end

GCD简介

全称是Grand Central Dispatch
纯C语言的,提供了非常强大的函数
GCD的优势
GCD是苹果公司为多核的并行运算提出的解决方案
GCD会自动利用更多的CPU(双核 四核)
GCD会自动管理线程的生命周期(创建线程 调度任务 销毁线程)
程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
GCD的核心
将任务添加到队列
任务:执行什么操作
队列:用来存放任务
GCD使用的两个步骤
创建任务:
确定要做的事情
GCD中的任务是BLOCK封装的
将任务添加到队列
GCD会自动将队列中的任务取出,放到对应的线程中
任务的取出遵循队列的FIFO原则 : 先进先出,后进后出.


@interface ViewController ()

//根视图
@property (strong,nonatomic) UIScrollView *scrollView;

//图片子视图
@property (nonatomic,weak) UIImageView *imgView;

@end
//需求:异步下载网络图片,图片可以滚动,滚动视图要是根视图
//分析需求:下载是耗时的操作,需要在子线程异步执行
//准备控件的工作UIImageView/UIScrollView(根视图)
//
@implementation ViewController
/*
 loadView:优先于viewDidLoad
 loadView:当self.view == nil 时调用
 loadView:不需要调用super
 */
-(void)loadView{
    //创建根视图
    self.scrollView = [[UIScrollView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    //把根视图替换成scrollView
    self.view = self.scrollView;
    self.scrollView.backgroundColor = [UIColor redColor];
    
    //创建图片子视图
    UIImageView *imgView = [[UIImageView alloc] init];
    [self.view addSubview:imgView];
    //给属性赋值:一定不能少
    self.imgView = imgView;
}

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

//在子线程下载图片,在主线程更新UI,是线程间通信的一种
//线程间通信:一个线程把他执行的结果,传递到另外的一个线程

//下载图片的主方法
-(void)loadImageData{
    //将异步任务添加到队列
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //URL
        NSURL *url = [NSURL URLWithString:@"http://img05.tooopen.com/images/20150202/sy_80219211654.jpg"];
        //发送网络请求,获取图片二进制数据,是个耗时的操作
        NSData *data = [NSData dataWithContentsOfURL:url];
        //image就是子线程执行的结果,需要传递到主线程
        UIImage *image = [UIImage imageWithData:data];//下载的结果
        //下载完成后,通知主线程刷新UI
        dispatch_async(dispatch_get_main_queue(), ^{//通知主线程刷新UI
            self.imgView.image = image;
            [self.imgView sizeToFit];
            self.scrollView.contentSize = image.size;

        });
    }); 
}

队列

CGD的队列可以分为两大类型
串行队列:

  1. 让任务一个接着一个有序的执行:不管队列里面放的是什么任务,一个任务执行完毕后,再执行下一个任务
  2. 同时只能调度一个任务

并行队列:

  1. 可以让多个任务 并发/同时 执行,自动开启多个线程同时执行多个任务
  2. 同时可以调度多个任务执行
  3. 并发队列的并发功能只有内部的任务是异步任务时,才有效

任务

CGD中有2个用来执行任务的函数
同步的方式执行任务:在当前线程中依次执行任务
异步的方式执行任务:新开线程,在新线程中执行任务

GCD串行队列

明确无论串行队列里面添加什么任务都会按序执行
特点
以先进先出的方式,顺序调度队列中的任务执行
无论队列中所指定的执行函数是同步还是异步,都会等待前一个任务执行完成后,再调度后面的任务

@interface ViewController ()

@end
/*
串行队列:调度任务是有序的,必须前面一个任务执行完,再调度后面的任务;先进先出
同步任务:在当前线程,有序执行
异步任务:新开线程,在新线程执行
*/
@implementation ViewController

- (void)viewDidLoad {
   [super viewDidLoad];
   // Do any additional setup after loading the view, typically from a nib.
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
   [self GCDDemo2];
}


#pragma mark -串行队列+异步任务

/**
开线程:只开一个,一个就够了
是否有序:有序
*/
-(void)GCDDemo2{
    dispatch_queue_t queue = dispatch_queue_create("szios07", DISPATCH_QUEUE_SERIAL);
   for (int i= 0; i< 10; i++) {
       dispatch_async(queue, ^{
           NSLog(@"%zd - %@",i,[NSThread currentThread]);
       });
   }
}
#pragma mark -串行队列+同步任务

/**
是否开线程?不开
*/
-(void)GCDDemo1{
   //1创建串行队列
   //参数1:表示队列的标识符
   //参数2:队列的属性(决定队列是串行的还是并行的) DISPATCH_QUEUE_SERIAL :串行队列
   dispatch_queue_t queue = dispatch_queue_create("szios07", DISPATCH_QUEUE_SERIAL);
   //循环向队列里面添加10个同步任务
   for (int i= 0; i< 10; i++) {
       //把同步任务添加到穿行队列
       dispatch_sync(queue, ^{
           NSLog(@"%zd - %@",i,[NSThread currentThread]);
       });
   }
}


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


@end

GCD并行队列

明确并发队列想一次同时调度多个任务执行,那么任务必须是异步任务

#import "ViewController.h"
/*
 并行队列:可以同时调度多个任务执行,先进先出
 同步:在当前线程,有序执行
 异步任务:新开线程,在新线程执行
 
 总结:队列仅仅决定了任务的调度方式,无法决定开不开线程
 提示:任务怎么执行,是由队列和任务同时决定的
 */
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self GCDDemo2];
}
#pragma mark - 并行队列添加异步任务

/**
 开线程:
 无序:
 */
-(void)GCDDemo2{
    //创建并行队列
    //DISPATCH_QUEUE_CONCURRENT : 表示并行队列
    dispatch_queue_t queue = dispatch_queue_create("ios07", DISPATCH_QUEUE_CONCURRENT);
    //将异步任务添加到并行队列
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%zd - %@",i,[NSThread currentThread]);
        });
    }
}

#pragma mark - 并行队列添加同步任务

/**
 是否开线程:不开
 有序
 */
-(void)GCDDemo1{
    //创建并行队列
    //DISPATCH_QUEUE_CONCURRENT : 表示并行队列
    dispatch_queue_t queue = dispatch_queue_create("ios07", DISPATCH_QUEUE_CONCURRENT);
    //将同步任务添加到并行队列
     for (int i = 0; i < 10; i++) {
         dispatch_sync(queue, ^{
            NSLog(@"%zd - %@",i,[NSThread currentThread]);
        });
     }
}

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


@end

GCD主队列

#import "ViewController.h"

@interface ViewController ()

@end
/*
 主队列:
 程序一起动就会自动创建主队列,所以只需要get,不需要create
 主队列是特殊的串行队列,主队列里面无论是什么任务都是有序执行的
 主队列是专门在主线程上调度任务执行的,主队列里面的任务"一定"是在主线程执行的
 小结:主队列里面,无论添加什么任务,都是在主线程有序执行的
 提示:主队列是主队列,主线程是主线程
 注意:队列和线程的关系,队列是调度任务的,线程是执行任务的
 "主队列调度任务执行必须满足一个条件,就是只有主线程空闲时,主队列才会调度任务在主线程执行
 主队列里面必须添加异步任务
 
 */
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self GCDDemo2];
}
#pragma mark - 主队列+同步任务
-(void)GCDDemo2{
    //获取主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    //把同步任务添加到主队列:不被允许的指令Xcode 8 报错 Xcode 7 死锁
    dispatch_sync(mainQueue, ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
}

#pragma mark - 主队列+异步任务

/**
 不开线程,只在主线程有序执行
 */
-(void)GCDDemo1{
    //获取主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    //把异步任务添加到主队列
    //异步任务:可以不执行完,就让后面的代码先执行
    dispatch_async(mainQueue, ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
}

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


@end

GCD全局队列(并行)

知道全局队列两个参数代表什么意思

-(void)GCDDemo{
    //并行队列
    //参数1:队列的标识符
    //参数2:队列的属性,决定了队列是串行还是并行的
    dispatch_queue_t queue = dispatch_queue_create("hm", DISPATCH_QUEUE_CONCURRENT);
    //全局队列(并行)
    //提示:程序启动会默认创建好,直接get,执行的效果是跟并行队列是一模一样的
    //全局队列的作用:方便程序员尽快的实现程序的异步执行的
    //参数2:苹果"目前"不知道这个参数是干什么用的,预留参数
    //参数1:表示队列的服务质量 / 队列的优先级,开发中,不要修改,就选择默认的(0)
    //服务质量和优先级是一一对应的,用户相关-->默认-->工具-->后台
    dispatch_queue_t global_queue = dispatch_get_global_queue(0, 0);
}

相关文章

  • Day18-多线程

    1.多线程技术1 2.多线程技术2 3.多线程技术 4.join函数

  • 多线程

    多线程技术 多线程技术2 多线程应用 server client join函数

  • Python-day-18多线程

    1、多线程技术1 二、多线程技术2 三、多线程应用 四、jion函数

  • iOS-多线程相关

    本篇涵盖多线程解析、应用等. 1.iOS多线程--彻底学会多线程之『RunLoop』2.iOS多线程--彻底学会多...

  • day18-多线程

    recode 一、多线程技术1 二、多线程技术2 三、client 多线程应用 四、join函数 耗时操作

  • iOS性能篇——并行开发其实很简单

    概览 1.多线程 1.1 简介 1.2 iOS 多线程 2.NSThread 2.1 解决多线程阻塞问题 2.2 ...

  • Java多线程目录

    Java多线程目录 Java多线程1 线程基础Java多线程2 多个线程之间共享数据Java多线程3 原子性操作类...

  • iOS多线程技术方案

    多线程技术方案 目录 一、多线程简介 1、多线程的由来 2、耗时操作的模拟试验 3、进程和线程 4、多线程的...

  • iOS开发之多线程(5)—— Pthreads

    文集 iOS开发之多线程(1)—— 概述iOS开发之多线程(2)—— ThreadiOS开发之多线程(3)—— G...

  • iOS开发之多线程(4)—— Operation

    文集 iOS开发之多线程(1)—— 概述iOS开发之多线程(2)—— ThreadiOS开发之多线程(3)—— G...

网友评论

      本文标题:多线程2

      本文链接:https://www.haomeiwen.com/subject/rzgzyttx.html