block的理解 ios_iOS 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 *)&__NSConstantStringImpl__var_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内部的值,同时内部的值也是可以修改的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值