美文网首页
iOS开发-消息传递方式-Block

iOS开发-消息传递方式-Block

作者: 张囧瑞 | 来源:发表于2018-03-15 11:23 被阅读50次

    说过了target-action

    说过了KVO

    说过了NotificationCenter

    这次我们来说一个一对一的消息传递方式,Block

    Block是什么

    Block,很多语言中翻译做闭包,用《Objective-C高级编程》中的话说:

    Blocks是C语言的扩充功能。可以用一句话来表示Blocks的扩充功能:带有自动变量(局部变量)的额匿名函数。

    所以,Block就是一个带有自动变量匿名函数

    匿名函数

    顾名思义,匿名函数就是没有名称的函数,C语言是不允许出现没有函数名的函数的,但是因为实际上调用函数也是调用指向函数的函数指针,但是没有函数名,就没办法获取到函数的指针。那么block具体是怎么来实现的呢?我们先往下看。

    自动变量

    在栈上声明一个变量如果不是静态变量或全局变量,是不可以在这个栈内声明的匿名函数中使用的,但是在block中却可以。

    Block结构

    在Xcode里,我们敲入快捷键inlineBlock就会看到有这样一个block的样式提供给我们。

    
    //增加了returnType,可省略
    
     returnType(^blockName)(parameterTypes) = ^returnType(parameters) {
    
     statements
    
     };
    
    

    其中第二个returnType是我加上去的,为了能看的明显一点。

    但是一下子看到一个这样的东西还是有点乱,我们把它拆分成声明部分和实现部分来看就会清楚很多了。

    声明Block

    
    returnType(^blockName)(parameterTypes)
    
    

    声明block中包括了返回类型、^、block名称、参数列表。

    实现Block

    
    ^returnType(parameters) {
    
     statements
    
     };
    
    

    实现block中包括了返回类型(可省略)、参数列表(可省略)、实现代码。

    其中返回值类型可以省略。

    
    ^(parameters) {
    
     statements
    
     };
    
    

    参数列表也可以省略。

    
    ^{statements};
    
    

    Block的使用

    因为block的传入参数和返回值都可以为空,所以Block的使用可以分为4中模式:

    • 1.无参数、无返回值。

    • 2.有参数、无返回值。

    • 3.无参数、有返回值。

    • 4.有参数、有返回值。

    接下来我们就来举例子看看这几种方式的使用。

    无参数、无返回值

    
    //无参数、无返回值
    
    - (void)blockWithoutParameterAndWithoutReturn
    
    {
    
     void(^noParameterNoReturn)(void) = ^(void){
    
     NSLog(@"无参数、无返回值");
    
     };
    
     noParameterNoReturn();
    
    }
    
    

    有参数、无返回值

    
    //有参数、无返回值
    
    - (void)blockWithParameterAndWithoutReturn
    
    {
    
     void(^parameterNoReturn)(NSInteger number) = ^(NSInteger number){
    
     NSLog(@"有参数、无返回值,参数是%lu",number);
    
     };
    
     parameterNoReturn(10);
    
    }
    
    

    无参数、有返回值

    
    //无参数、有返回值
    
    - (void)blockWithoutParameterAndWithReturn
    
    {
    
     NSInteger(^noParameterReturn)(void) = ^{
    
     NSInteger number = 20;
    
     NSLog(@"无参数、有返回值,返回值是%lu",number);
    
     return number;
    
     };
    
     NSInteger number = noParameterReturn();
    
     NSLog(@"返回值是%lu",number);
    
    }
    
    

    有参数、有返回值

    
    //有参数、有返回值
    
    - (void)blockWithParameterAndWithReturn
    
    {
    
     NSInteger(^parameterAndReturn)(NSInteger numberA, NSInteger numberB) = ^(NSInteger numberA, NSInteger numberB){
    
     NSLog(@"有参数、有返回值,参数是%lu、%lu,返回值是%lu",numberA,numberB,numberA+numberB);
    
     return numberA+numberB;
    
     };
    
     NSInteger numberSum = parameterAndReturn(30,40);
    
     NSLog(@"返回值是%lu",numberSum);
    
    }
    
    

    使用typedef定义

    除了上边的常规操作之外,block还可以作为OC中的一个参数,这时候可以用到typedef来定义一个block,然后在函数调用时进行参数传递。

    比如先定义一个block参数:

    
    //number作为参数,无返回值
    
    typedef void(^typedefBlock)(NSInteger number);
    
    

    然后声明一个函数中带有此变量

    
    //typedef block
    
    - (void)testTypedefBlockWith:(typedefBlock)testTypedefBlock
    
    {
    
     NSLog(@"开始使用typedef block");
    
     testTypedefBlock(12);
    
     NSLog(@"结束使用typedef block");
    
    }
    
    

    这时候调用此方法,在回调的方法中就可以获取到传递过来的值。

    
     [self testTypedefBlockWith:^(NSInteger number) {
    
     NSLog(@"回调 typedef block number %lu",number);
    
     }];
    
    

    Block与外界变量

    默认情况

    通常情况下,对于block外的变量引用,block默认是将其复制到block的数据结构中实现访问的,也就是说只有block中用到的变量,block才会把他自动截获进来,而且因为截取的是瞬时值,所以之后在外部改变变量的值也不会改变值得大小。因为截获自动变量会存储在block内部,所以会导致block体积变大。

    另外需要注意的一点就是block内部只能调用getter方法,不可以调用setter方法,所以是没办法修改外部变量的值的。

    block-capture-1.jpg

    比如:

    
    - (void)autoParamterTest
    
    {
    
     NSInteger number = 100;
    
     void(^autoParamter)(void) = ^(void){
    
     NSLog(@"%lu",number); //输出100 
    
     };
    
     number = 200;
    
     autoParamter();         
    
    }
    
    

    这段代码最后会输出100,因为在定义block时,他已经把number的值复制到block中了,所以再改变他,对block中的值也不会有影响。

    另外,在block中对number赋值时,编译器会直接报错。

    [图片上传中...(block-setter.png-8afa38-1521083690526-0)]

    __block

    对于这种情况,OC提供了_block(两个下划线)来修饰外部变量,使用了__block修饰的外部变量,block内部是复制其引用地址来实现访问数据的,所以block内部可以修改block外部的变量值。

    block-capture-2.jpg
    
    - (void)autoBlockParameterTest
    
    {
    
     __block NSInteger number = 100;
    
     void(^autoBlockParamter)(void) = ^(void){
    
     NSLog(@"%lu",number); //输出200
    
     number = 300;
    
     NSLog(@"%lu",number); //输出300
    
     };
    
     number = 200;
    
     autoBlockParamter();
    
    }
    
    

    那为什么在加了 __block修饰符之后就可以访问了呢?后边我们会详细说明,我们先往下看。

    Block的循环引用

    Block是很好用,但是用不好的时候就容易出现循环引用,比如在某各类将block作为自己的变量,然后又在这个block的方法中使用了这个类自己的东西,这时候两者互相持有就会发生循环引用,引起内存泄漏的问题。比如如下代码:

    
    - (void)blockCircularReference
    
    {
    
     self.circleBlock = ^(NSInteger number) {
    
     [self autoParamterTest];
    
     };
    
    }
    
    

    但是苹果也给出了相应的解决方案来处理block下的循环引用。

    __weak修饰

    可以直接用__weak(有两个下划线)来修饰,来打破block中的循环,使用__weak修饰解决循环引用一共有三种实现的方式。

    1. 使用__weak ClassName
    
    - (void)blockCircularReference
    
    {
    
     __weak MPBlockViewController *weakSelf = self;
    
     self.circleBlock = ^(NSInteger number) {
    
     [weakSelf autoParamterTest];
    
     };
    
    }
    
    
    1. 使用__weak typeof(self)
    
    - (void)blockCircularReference
    
    {
    
     __weak typeof (self) weakSelf = self;
    
     self.circleBlock = ^(NSInteger number) {
    
     [weakSelf autoParamterTest];
    
     };
    
    }
    
    
    1. 使用Reactive Cocoa中的@weakify和@strongify
    
    - (void)blockCircularReference
    
    {
    
     @weakify(self);
    
     self.circleBlock = ^(NSInteger number) {
    
     @strongify(self);
    
     [self autoParamterTest];
    
     };
    
    }
    
    

    @weakify, @strongify的具体使用可以看这里

    __block

    在MRC下,可以直接使用__block进行修饰。

    也可以先用__block修饰,然后在block方法中使用完将其设为nil,但是要注意就是block必须要被调用一次。

    
    - (void)blockCircularReference
    
    {
    
     __block MPBlockViewController *blockSelf = self;
    
     self.circleBlock = ^(NSInteger number) {
    
     [blockSelf autoParamterTest];
    
     blockSelf = nil; //必须设为nil
    
     };
    
     self.circleBlock(10);  //必须至少调用一次
    
    }
    
    

    将self作为参数传递

    也可以直接将self作为一个参数传递到block中。

    
    - (void)blockCircularReference
    
    {
    
     self.circleBlock = ^(MPBlockViewController *vc) {
    
     [vc autoParamterTest];
    
     };
    
    }
    
    

    Block的实现

    block实际上是用C语言源码来处理的,含有block的源码首先被转换成C语言编译器能够处理的源码,再作为C进行编译。

    Clang

    使用LLVM编译器的clang可以将OC的代码翻译成C++的源代码,说是C++的代码,但是实际上也就是C语言的源代码。

    使用的方式就是打开Terminal,cd到源代码文件目录,输入:

    
    clang -rewrite-objc 源代码文件名
    
    

    比如这样一段代码(这里没有引用其他OC的框架,因为引入之后clang出来的cpp文件会巨大,有好几千行):

    
    #include <stdio.h>
    
    int main() {
    
     void (^ blk)(void) = ^{printf("Block\n");};
    
     blk();
    
     return 0;
    
    }
    
    

    这段简单的block代码clang之后就会变成如下源码(这里删除了部分代码,只显示了重要的部分):

    
    struct __block_impl {
    
     void *isa;
    
     int Flags;
    
     int Reserved;
    
     void *FuncPtr;
    
    };
    
    
    
    struct __main_block_impl_0 {
    
     struct __block_impl impl;
    
     struct __main_block_desc_0* Desc;
    
     __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    
     impl.isa = &_NSConcreteStackBlock;
    
     impl.Flags = flags;
    
     impl.FuncPtr = fp;
    
     Desc = desc;
    
     }
    
    };
    
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    
    printf("Block\n");}
    
    static struct __main_block_desc_0 {
    
     size_t reserved;
    
     size_t Block_size;
    
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
    
    int main() {
    
     void (* blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    
     ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    
     return 0;
    
    }
    
    

    乍一看,我天,这都是什么鬼啊 = =。 没事我们一部分一部分来看。

    __cself

    这里边的参数__cself就相当于C++中指向自身的变量this,在OC中就是self,即参数__cself就是指向block值的变量。

    __block_impl

    __block_impl是我们要介绍的第一个block中的成员变量,他是一个结构体,其结构如下:

    
    struct __block_impl {
    
     void *isa;
    
     int Flags;
    
     int Reserved;
    
     void *FuncPtr;
    
    };
    
    
    • isa指针,所有对象都有改指针,用于实现对象相关的功能。

    • Flags,用于按bit位表示一些block的附加信息。

    • Reserved,保留变量。

    • FuncPtr,函数指针,指向block要执行的函数,即__main_block_func_0。

    __main_block_desc_0

    __main_block_desc_0是我们要介绍的第二个block中的成员变量,也是一个结构体,其结构如下:

    
    static struct __main_block_desc_0 {
    
     size_t reserved;
    
     size_t Block_size;
    
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
    
    
    • reserved,结构体信息保留字段。

    • Block_size,block的大小。

    初始化__main_block_func_0

    另外一部分就是__main_block_func_0的初始化,用到的就是之前介绍的两个结构体。

    
    struct __main_block_impl_0 {
    
     struct __block_impl impl;
    
     struct __main_block_desc_0* Desc;
    
     __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    
     impl.isa = &_NSConcreteStackBlock;
    
     impl.Flags = flags;
    
     impl.FuncPtr = fp;
    
     Desc = desc;
    
     }
    
    };
    
    

    实现__main_block_func_0

    这里主要就是我们在block中要实现的代码。

    
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    
    printf("Block\n");
    
    }
    
    

    实现main函数

    另外main函数的源码在这里。

    
    int main() {
    
     void (* blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    
     ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    
     return 0;
    
    }
    
    

    截获自动变量

    看完了block的基本实现方式,那我们再看看他是如何截获自动变量呢?

    我们先定义一个number

    
    #include <stdio.h>
    
    int main() {
    
     int number = 10;
    
     void (^ blk)(void) = ^{printf("%d",number);};
    
     blk();
    
     return 0;
    
    }
    
    

    这时候clang之后发现:

    
    struct __main_block_impl_0 {
    
     struct __block_impl impl;
    
     struct __main_block_desc_0* Desc;
    
     int number;  //number被直接加入了__main_block_impl_0结构体中
    
     __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _number, int flags=0) : number(_number) {
    
     impl.isa = &_NSConcreteStackBlock;
    
     impl.Flags = flags;
    
     impl.FuncPtr = fp;
    
     Desc = desc;
    
     }
    
    };
    
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    
     int number = __cself->number; // bound by copy
    
    printf("%d",number);}
    
    static struct __main_block_desc_0 {
    
     size_t reserved;
    
     size_t Block_size;
    
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
    
    int main() {
    
     int number = 10;
    
     void (* blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, number));
    
     ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    
     return 0;
    
    }
    
    

    我们看到number被直接加到了__main_block_impl_0结构体中。

    __block

    这时候我们再用__block来修饰一下number看看:

    
    #include <stdio.h>
    
    int main() {
    
     __block int number = 10;
    
     void (^ blk)(void) = ^{printf("%d",number);};
    
     blk();
    
     return 0;
    
    }
    
    

    clang之后发现:

    
    struct __Block_byref_number_0 {
    
     void *__isa;
    
    __Block_byref_number_0 *__forwarding;
    
     int __flags;
    
     int __size;
    
     int number;
    
    };
    
    struct __main_block_impl_0 {
    
     struct __block_impl impl;
    
     struct __main_block_desc_0* Desc;
    
     __Block_byref_number_0 *number; // by ref
    
     __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_number_0 *_number, int flags=0) : number(_number->__forwarding) {
    
     impl.isa = &_NSConcreteStackBlock;
    
     impl.Flags = flags;
    
     impl.FuncPtr = fp;
    
     Desc = desc;
    
     }
    
    };
    
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    
     __Block_byref_number_0 *number = __cself->number; // bound by ref
    
    printf("%d",(number->__forwarding->number));}
    
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->number, (void*)src->number, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->number, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static struct __main_block_desc_0 {
    
     size_t reserved;
    
     size_t Block_size;
    
     void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
    
     void (*dispose)(struct __main_block_impl_0*);
    
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
    
    int main() {
    
     __attribute__((__blocks__(byref))) __Block_byref_number_0 number = {(void*)0,(__Block_byref_number_0 *)&number, 0, sizeof(__Block_byref_number_0), 10};
    
     void (* blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_number_0 *)&number, 570425344));
    
     ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    
     return 0;
    
    }
    
    

    我们只是加了一个__block,结果代码一下子增加了巨多!

    这时候仔细看代码,就能发现多了一个段代码:

    
    __Block_byref_number_0 number = {
    
    (void*)0,(__Block_byref_number_0 *)&number,
    
     0, 
    
     sizeof(__Block_byref_number_0),
    
     10};
    
    

    找到这个结构体的声明:

    
    struct __Block_byref_number_0 {
    
     void *__isa;
    
    __Block_byref_number_0 *__forwarding;
    
     int __flags;
    
     int __size;
    
     int number;
    
    };
    
    

    那如果这时候我们给number赋一个新的值会怎么样呢?

    
    #include <stdio.h>
    
    int main() {
    
     __block int number = 10;
    
     void (^ blk)(void) = ^{
    
     number = 20;
    
     printf("%d",number);
    
     };
    
     blk();
    
     return 0;
    
    }
    
    

    clang后发现多了这里变化(就不贴全部代码了):

    
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    
     __Block_byref_number_0 *number = __cself->number; // bound by ref
    
     (number->__forwarding->number) = 20;
    
     printf("%d",(number->__forwarding->number));
    
     }
    
    

    我们看到在向__block变量赋值时,block的__main_block_impl_0结构体实例持有指向__block变量的__Block_byref_number_0结构体实例的指针。

    __Block_byref_number_0结构体的实例的成员变量__forwarding持有指向该实例自身的指针。并通过成员变量__forwarding访问成员变量val。

    那么__forwarding又是什么呢?别急,后边我们再说他。

    Block的存储域

    在__main_block_func_0的初始化时,我们看到了有一行代码是:

    
     impl.isa = &_NSConcreteStackBlock;
    
    

    从名字应该可以判断出来,这行代码的意思是这个block是存储在栈上的。

    那么除了栈,block还存储在哪些地方呢?

    | 类 | 设置对象的存储域 |

    | ------| ------ |

    | _NSConcreteStackBlock | 栈块 |

    | _NSConcreteGlobalBlock | 全局块 |

    | _NSConcreteMallocBlock | 堆块 |

    block_Storage_Domain.png

    从名字就能看出来:

    • 栈块存在栈内存中,超出作用域后就会马上销毁。

    • 全局块在全局内存中,和全局变量一样。

    • 堆块存在堆内存中,是一个带有引用计数的对象,需要自行管理内存。

    那么我们怎么样能够知道block是保存在哪里呢?

    全局块

    一般情况下,当满足以下情况时,block为_NSConcreteGlobalBlock类对象,也就是放在全局数据区。

    • 1.记录全局变量的地方有block语法时。

    • 2.block语法的表达式中不使用应截获的自动变量时。

    栈块

    理论上,除了在全局块条件之外的情况下,block都为_NSConcreteStackBlock类对象,也就是设置在栈区。

    堆块

    那么如果这样说起来,岂不是没有block会在堆上了么?

    这就要说到一个问题,就是ARC和MRC下block的不同情况,MRC下访问外界变量的block默认就是存储在栈中了,但是ARC下,block会自动被从栈区拷贝到堆区,然后自动释放。

    那为什么ARC下,访问外部变量的block会自动从栈区拷贝到堆区呢?

    block中的copy

    在栈上的block,如果所在的作用域结束,block和block中的__block变量都会被废弃掉。

    block_copy_1.png

    所以,我们需要将Block复制到堆中,延长其生命周期,这样即使是block所在的作用域结束,block还是可以在堆中继续存在。

    开启了ARC时,大多数情况下编译器会恰当的判断是否有需要将block从栈复制到堆,如果有,自动生成将block从栈上复制到堆上的代码,block复制执行的是copy实例方法,只要调用了copy方法,栈块就会变成堆块,一般在如下情况时,block会自动copy到堆上。

    • 1.调用Block的copy方法。

    • 2.将Block作为函数返回值时(MRC下需要手动调用copy,否则无效)。

    • 3.将Block赋值给__strong修改变量时(MRC时无效)。

    • 4.向Cocoa框架中含有usingBlock的方法或者GCD的API传递Block参数时。

    block_copy_2.png
    
     int count = 0;
    
     blk_t blk = ^(){
    
     NSLog(@"In Stack:%d", count);
    
     };
    
     NSLog(@"blk's Class:%@", [blk class]);//打印:blk's Class:__NSMallocBlock__
    
     NSLog(@"Global Block:%@", [^{NSLog(@"Global Block");} class]);//打印:Global Block:__NSGlobalBlock__
    
     NSLog(@"Copy Block:%@", [[^{NSLog(@"Copy Block:%d",count);} copy] class]);//打印:Copy Block:__NSMallocBlock__
    
     NSLog(@"Stack Block:%@", [^{NSLog(@"Stack Block:%d",count);} class]);//打印:Stack Block:__NSStackBlock__
    
    

    block的复制操作执行的是copy实例方法,不同类型的block使用copy方法的效果如下:

    | block 的类 | 副本源的配置存储域 | 复制效果 |

    | ------| ------ | ----- |

    | _NSConcreteStackBlock | 栈块 | 从栈复制到堆 |

    | _NSConcreteGlobalBlock | 全局块 | 什么也不做 |

    | _NSConcreteMallocBlock | 堆块 | 引用计数增加 |

    不管block配置在何处,用copy方法复制都不会引起任何问题,在不确定是调用copy即可。

    
    blk = [[[[blk copy] copy] copy] copy];
    
    // 经过多次复制,变量blk仍然持有Block的强引用,该Block不会被废弃。
    
    

    __block变量的存储域

    之前只说到了block,那__block变量又会有什么影响呢?使用__block变量的block从栈复制到堆上时,__block变量也会受到影响。

    | __block变量的配置存储域 | block从栈复制到堆时的影响 |

    | ------| ------ |

    | 栈 | 从栈复制到堆并被block持有 |

    | 堆 | 被block持有 |

    那么栈上的__block变量复制到堆上之后,block是可以同时访问栈上的__block变量和堆上的__block变量,但是具体访问时到底是访问栈上的还是堆上的呢?这时候还记得我们之前说的****__forwarding****变量么?

    block_copy_3.png

    通过__forwarding, 无论是在block中还是 block外访问__block变量, 也不管该变量在栈上或堆上, 都能顺利地访问同一个__block变量。

    Block的实践

    说了这么多,我们来看看block在实际开发中比较常见的使用方法吧。

    一般情况下,block会用来作为方法回调的功能,和代理的方法比较相似,处理一些比较耗时的操作比如网络数据的下载,在下载好之后直接调用block回调,返回正确或错误的信息。

    block会使得代码结构紧凑,逻辑清晰,接下来我们就看一个简单的🌰:

    首先我们先声明一个typedef block

    
    typedef void(^MPBlockDownloadHandler)(NSData * receiveData, NSError * error);
    
    

    然后在下载函数中传入block作为参数,并在下载结束后调用block。

    
    - (void)downloadWithURL: (NSString *)URL parameters: (NSDictionary *)parameters handler: (MPBlockDownloadHandler)handler
    
    {
    
     NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:URL]];
    
     NSURLSession * session = [NSURLSession sharedSession];
    
     //执行请求任务
    
     NSURLSessionDataTask * task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    
     if (handler) {
    
     dispatch_async(dispatch_get_main_queue(), ^{
    
     handler(data,error);
    
     });
    
     }
    
     }];
    
     [task resume];
    
    }
    
    

    最后调用这个函数:

    
     [self downloadWithURL:@"https://www.vactualpapers.com/web/wallpapers/sights-and-scenes-of-beautiful-singapore-hd-wallpaper-29/thumbnail/lg.jpg" parameters:nil handler:^(NSData *receiveData, NSError *error) {
    
     if (error) {
    
     NSLog(@"下载失败:%@",error);
    
     }else {
    
     NSLog(@"下载成功,%@",receiveData);
    
     }
    
     }];
    
    

    这样,一个简单的利用block实现网络加载回调的功能就做好了。

    最后

    好了,这就是block的全部内容了。说是写消息传递,好像越来越跑偏了。。。

    另外以上内容仅供个人学习使用,大部分内容来自《Objective-C高级编程 iOS与OS X多线程和内存管理》。如果有什么地方不对,还请大佬们多多指教。

    参考文档

    Objective-C 高级编程

    iOS开发-由浅至深学习block

    《Objective-C 高级编程》干货三部曲(二):Blocks篇

    谈Objective-C block的实现

    译 Block 小测验

    iOS Block用法和实现原理

    iOS Block 详解

    block_copy_2.png

    相关文章

      网友评论

          本文标题:iOS开发-消息传递方式-Block

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