博客> iOS block的使用与深入理解
iOS block的使用与深入理解
2019-09-17 18:01 评论:0 阅读:1546 AFyingshi
block

一、block的基本语法与使用 概述 Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block是一种特殊的数据类型,其可以正常定义变量、作为参数、作为返回值,特殊地,Block还可以保存一段代码,在需要的时候调用,目前Block已经广泛应用于iOS开发中,常用于GCD、动画、排序及各类回调。

block的申明,跟C语言中的函数申明类似

void (^blockName) (int parameter); //void代表返回值类型,后面一次是block名,参数 block的赋值,跟C语言中函数的定义类似

blockName = ^(int a){ //这里参数要和定义的参数类型一致 NSLog(@"parameter=%d",a); }; block的调用

blockName(1); //输出 ‘parameter=1’ 使用typedef定义Block类型

// 定义一种无返回值无参数列表的Block类型 typedef void(^SayHello)();

// 我们可以像OC中声明变量一样使用Block类型SayHello来声明变量 SayHello hello = ^(){ NSLog(@"hello"); };

// 调用后控制台输出"hello" hello(); block作为函数的参数

// 1.使用typedef定义Block类型 typedef int(^MyBlock)(int, int);

// 2.定义一个形参为Block的OC函数

  • (void)useBlockForOC:(MyBlock)aBlock { NSLog(@"result = %d", aBlock(300,200)); }

// 3.声明并赋值定义一个Block变量 MyBlock addBlock = ^(int x, int y){ return x+y; };

// 4.以Block作为函数参数,把Block像对象一样传递 [self useBlockForOC:addBlock];

// 将第3点和第4点合并一起,以内联定义的Block作为函数参数 [self useBlockForOC:^(int x, int y){ return x+y; }]; 在block中访问局部变量的时候注意 在声明Block之后、调用Block之前对局部变量进行修改,在调用Block时局部变量值是修改之前的旧值

// 声明局部变量a int a = 100;

void(^myBlock)() = ^{ NSLog(@"a = %d", a); }; a = 101; // 调用后控制台输出"a = 100" myBlock(); 在block中不可以修改局部变量 原因后面再详细介绍

// 声明局部变量global int a = 100;

void(^myBlock)() = ^{ a ++; // 这句报错 NSLog(@"a = %d", a); }; // 调用后控制台输出"a = 100" myBlock(); block中可以访问全局变量

int global = 100; // 声明全局变量global

void(^myBlock)() = ^{ NSLog(@"global = %d", global); }; global = 101; // 调用后控制台输出"global = 101" myBlock(); 在实际应用中,从一个页面传值到另一个页面的情况比较常见,就简单介绍一下: 一、在第二个视图控制器的.h文件中定义声明Block属性

typedef void (^ReturnTextBlock)(NSString *showText); //申明一个block并重新定义一个名字 @interface TextFieldViewController : UIViewController @property (nonatomic, copy) ReturnTextBlock returnTextBlock; //定义一个block属性 @end 二、实现第二个视图控制器的方法

//在需要的方法中调用 if (self.returnTextBlock != nil) { self.returnTextBlock(self.inputTF.text); //block的调用 } 三、在第一个视图中获得第二个视图控制器,并且用第二个视图控制器来调用定义的属性

//在控制跳转到第二个页面的地方调用 TextFieldViewController tfVC = [[TextFieldViewController alloc]init]; [tfVC returnText:^(NSString showText) { self.showLabel.text = showText; }]; //block的定义 [self.navigationController pushViewController:tfVC animated:YES]; 可能刚学iOS的同学会有点懵,把这里的逻辑梳理一下。首先在第二个页面定义了一个block变量,然后在需要的地方(比如点击返回第一个页面)调用了这个block,把UITextField中的内容传入了进去,但是block的定义是在第一个页面定义的,并且这里定义的内容是把传过来的内容(也就是第二个页面UITextField的内容)显示在label上面。

二、block的深入理解 块本身其实就是一个对象,在存放块对象的内存区域中,首个变量是指向Class对象的指针(isa指针)

首先要说明一下,block在创建的时候默认是创建在栈中的,这时候不能保证block的生存周期,所以我们在声明一个block属性的时候往往是用copy关键字,使用copy后会吧block拷贝在堆内存中,这时候才能保证block的生存周期。

现在我们来看看block的底层实现

常规block访问局部变量的情况 // OC代码如下 void(^myBlock)() = ^{ NSLog(@"global = %d", global); };

// 转为C++代码如下 void(myBlock)() = ((void ()())&main_block_impl_0((void *)__main_block_func_0, &main_block_desc_0_DATA, global));

// 将变量类型精简之后C++代码如下,我们发现Block变量实际上就是一个指向结构体main_block_impl_0的指针,而结构体的第三个元素是局部变量global的值 void(*myBlock)() = &__main_block_impl_0(main_block_func_0, &__main_block_desc_0_DATA, global);

// 我们看一下结构体main_block_impl_0的代码 struct __main_block_impl_0 { struct block_impl impl; struct main_block_desc_0 Desc; int global; __main_block_impl_0(void fp, struct main_block_desc_0 *desc, int _global, int flags=0) : global(_global) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };

// 在OC中调用Block的方法转为C++代码如下,实际上是指向结构体的指针myBlock访问其FuncPtr元素,在定义Block时为FuncPtr元素传进去的main_block_func_0方法 ((void ()(__block_impl ))((block_impl )myBlock)->FuncPtr)((__block_impl )myBlock);

// main_block_func_0方法代码如下,由此可见NSLog的global正是定义Block时为结构体传进去的局部变量global的值 static void __main_block_func_0(struct main_block_impl_0 cself) { int global = cself->global; // bound by copy NSLog((NSString )&NSConstantStringImplvar_folders_6y_vkd9wnv13pz6lc_h8phss0jw0000gn_T_main_d5d9eb_mi_0, global); } 这是block访问局部变量的时候,由此可知,在Block定义时便是将局部变量的值传给Block变量所指向的结构体,因此在调用Block之前对局部变量进行修改并不会影响Block内部的值,同时内部的值也是不可修改的。

在局部变量前使用下划线下划线block修饰 //oc代码还是不变,只是将global前加一个block关键字 // 转为C++代码如下 void(myBlock)() = ((void ()())&main_block_impl_0((void )__main_block_func_0, &main_block_desc_0_DATA, (Block_byref_global_0 )&global, 570425344));

// 将变量类型精简之后C++代码如下,我们发现Block变量实际上就是一个指向结构体main_block_impl_0的指针,而结构体的第三个元素是局部变量global的指针 void(*myBlock)() = &__main_block_impl_0(main_block_func_0, &main_block_desc_0_DATA, &global, 570425344); 由此可知,在局部变量前使用block修饰,在Block定义时便是将局部变量的指针传给Block变量所指向的结构体,因此在调用Block之前对局部变量进行修改会影响Block内部的值,同时内部的值也是可以修改的。

关注AF影视免费看电影

收藏
1
sina weixin mail 回到顶部