objective-c 代码块详解

简介

代码块是一种轻量级的匿名函数,可以在代码块中创建和传递可执行的代码片段,因为代码块是基于C语言的闭包概念,所以允许在代码块中捕获变量,形成闭包环境。

使用

声明并定义一个代码块(这个代码块不带参数,没有返回值)

//声明并定义一个代码块
void (^myBlock)(void) = ^(){

	NSLog(@"这是代码看执行的");
	
};

//执行代码块
myBlock();

!!!注意:大括号后面记得添加 ‘;’ 号

声明一个带参数和返回值的代码块

int (^addBlock)(int, int) = ^(int a, int b){

	return a + b;

};

int sum = addBlock(2, 3);

NSLog(@"%d", sum);

输出:5

代码块声明格式:

ReturnType (^BlockName)(ParameterTypes)

// ReturnType:代码块的返回类型。
// BlockName:为代码块类型创建的别名。
// ParameterTypes:代码块的参数类型列表,没有参数里面可填void或不填,多个参数类型用‘,’隔开。

代码块定义格式:

^(NSInteger a, NSInteger b) { 
	return a + b; 
};

// 1.以‘^’开头
// 2.‘()’里和函数定义一样写传入的参数,不传参可以不要‘()’
// 3.‘{}’里写执行的语句

!!!注意:大括号后面记得添加 ‘;’ 号

变量捕获

变量捕获允许在代码块中引用代码块外部的变量

两种捕获方式:

  • 自动捕获
  • 静态捕获

1.自动捕获:
代码块可以捕获在其作用域内定义的变量
下面例子中会捕获变量,获取变量的值:

int i = 10;

void (^printIBlock)(void) = ^{

	NSLog(@"i = %d", i);

};

printIBlock();

输出:i = 10

代码块修改捕获的变量:

// 需要加__block,不然会报错
__block int i = 10;

void (^setIBlock)(void) = ^{

	i = 20;
	NSLog(@"setIBlock1: i = %d", i);

};

setIBlock();

NSLog(@"main: i = %d", i);

输出:
setIBlock1: i = 20
main: i = 20

如果需要修改基本数据类型(int、float…),需要在变量声明前加“__block”

代码块获取和修改对象的值:


NSMutableArray *arr = [NSMutableArray arrayWithArray: @[@"a", @"b", @"c"]];

NSLog(@"%p: %@", arr, arr);

void (^addArrBlock)(void) = ^{

	[arr addObject:@"d"];

	NSLog(@"%p: %@", arr, arr);

};

addArrBlock();

NSLog(@"%p: %@", arr, arr);

输出:
0x600000c00c90: (a,b,c)
0x600000c00c90: (a,b,c,d)
0x600000c00c90: (a,b,c,d)

如果是对象的,指向听一个地址。

2.静态捕获:

static int n = 5;

void (^staticBlock)(void) = ^{

	n = 8;
	NSLog(@"n = %d", n);

};

staticBlock();

输出:n = 8

静态捕获都是直接引用外部变量

代码块copy

通常情况下,创建代码块会存储在栈空间,如果代码块以参数的形式传递给另一个方法,然后代码块声明的作用域失效了,那么代码块的空间会被释放,在别的方法调用就会出现问题。

使用copy把代码块拷贝到堆空间就不会出现下面的上面的问题。

下面两个Demo是在MRC模式下运行的,ARC模式下都是正常的,应该是ARC做了其他处理。

不使用copy:

void (^getblock(void))(void) {

	// 方法执行完后会被释放
	int a = 3;

	void (^block)(void) = ^{

		NSLog(@"%d", a);

	};

	return block;
}


int main {

	void (^block)(void) = getblock();

	block();

}

输出:9375

上面Demo中,当getblock方法执行完后里面的东西会被释放,所以在main方法里执行block代码块时打印的是错误的值

使用copy:

void (^getblock(void))(void) {

	// 方法执行完后会被释放
	int a = 3;

	void (^block)(void) = ^{

		NSLog(@"%d", a);

	};

	// 使用copy把block拷贝到堆空间,copyblock指向拷贝后的地址
	void (^copyblock)(void) = [block copy];

	return copyblock;
}


int main {

	void (^block)(void) = getblock();
	
	block();

}

输出:3

上面Demo中,执行了[block copy]方法,把代码块拷贝到堆空间,然后返回代码块在堆空间的地址,所以输出正常

注:栈空间作用域结束后会自动释放内容,堆空间需要手动释放

代码块与内存管理

在ARC模式下需要注意一个循环应用的问题。

当在代码块内部使用代码块外的对象时,引用计数器会+1。这就需要注意循环引用问题,如果不想引用计数器+1,可以使用 __weak。

__weak typeof(self) weakSelf = self;

void (^block2)(void) = ^{
	// 在代码块里使用weakSelf代替self
	weakSelf

};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值