Block基本概念 Block是OC中的一种数据类型 是一个能工作的代码单元,可以在任何需要的时候被执行 本质上是轻量级的匿名函数,可以作为其他函数的参数或者返回值 块代码本身可能有一个参数列表,也可能有一个返回值 可以把块代码赋给一个变量,并在需要的时候调用,就像调用一个普通函数一样 块代码使用的注意事项 默认情况下,不允许在块代码内部修改外部变量的数值 __block 循环引用 __weak 格式说明: (返回类型)(^块名称)(参数类型列表) = ^(形参列表) {代码实现}; void (^myBlock)(int,int) = ^(int x , int y) { //代码实现 } //下面是Block的简单使用 #import <Foundation/Foundation.h> typedef double(^MyBlock)(double, double); MyBlock area = ^(double x, double y) { return x * y; }; int a = 0; static int b = 11; /** block的定义*/ void test1() { // block 定义,是准备好一段代码片段,在需要的时候执行 // block是C语言的格式 // 输入:inlineblock // 格式:返回类型 (^blockName) (参数类型) = ^(形参列表) { 代码实现 }; // 提示:如果没有参数,格式可以简化 // 格式:返回类型 (^blockName) () = ^ { 代码实现 }; void (^myBlock)() = ^{ NSLog(@"block的最简单定义"); }; //调用myblock myBlock(); //最全的定义 int (^block) (int , int , int) = ^(int x, int y, int z) { return x + y + z; }; int sum = 0; sum = block(10,19,20); NSLog(@"sum - %d",sum); } /** 外部访问变量,常见面试题1*/ void test2() { int x = 10; __block int y = 20; NSLog(@"x - %d , x - add - %p",x,&x); // 在定义block时,如果block中使用到外部的“局部”变量,block会建立该变量的副本(会记录当前x的数值) void (^block)() = ^{ /**********在使用局部变量时,默认不允许在块代码中直接修改局部变量的数值********/ // x = 20; /************全局变量可以在block中修改其值******************/ a = 10; /**********如果要在block中修改外部变量的值,需要使用 __block***************/ y = 30; NSLog(@"block - x = %d",x); NSLog(@"block - x = %p",&x); //默认存储是再栈中 }; block(); NSLog(@"最后的x = %d",x); } /** 外部访问变量,常见面试题2*/ void test3() { NSMutableString *str = [NSMutableString stringWithFormat:@"hello"]; NSLog(@"定义之前 - %p 定义之前 - %p", &str, str); void (^block)() = ^{ // 修改指针指向内存空间的内容 [str setString:@"水水水水"]; NSLog(@"block里面通过指针 - %p %@", &str, str); }; block(); } int main(int argc, const char * argv[]) { test3(); return 0; }
使用注意点
@property (nonatomic , copy) myblock testblock; //定义用copy
1 //水平移动(向左) 2 - (void)test6 3 { 4 CGPoint p = self.myView.center; 5 __weak typeof(self) weakSelf = self; //弱引用 6 [UIView animateWithDuration:0.9 animations:^{ 7 weakSelf.myView.center = CGPointMake(weakSelf.myView.center.x + 12, weakSelf.myView.center.y); 8 }completion:^(BOOL finished) { 9 weakSelf.testblock = ^{ 10 weakSelf.myView.center = p; 11 }; 12 }]; 13 }
更详细的block介绍
Objective C之Block教程
引言
Block是C级别的语法和运行时特性。Block比较类似C函数,但是Block比之C函数,其灵活性体现在栈内存、堆内存的引用,我们甚至可以将一个Block作为参数传给其他的函数或者Block。
目录
- 一、Block的基本介绍
- 1、什么是Block
- 2、Block和C语言函数指针和什么区别
- 3、如何调用Block
- 二、Block内存管理与其他特性
- 1、Block放在哪里
- 2、Block引用的变量在哪里
- 三、Block揭开神秘面纱
一、Block的基本介绍
1、什么是Block
Block是一个C Level的语法以及运行时的一个特性,和标准C中的函数(函数指针)类似,但是其运行需要编译器和运行时支持,从iOS4.0开始就很好的支持Block了。广泛用于两个对象之前的回调函数。
下面我们来看一下Block的声明:
int(^hbFunction) (BOOL a);
其中int为block的返回类型,hbFunction为block名称,a为参数。
2、Block和C语言函数指针和什么区别
首先我们来看看C函数指针:
int (* hbFunction) (int a); // 函数声明 int resut = hbFunction(10); // 函数调用
再看看Block的用法:
int (^ hbFunction) (int a); // 函数声明
int resut = hbFunction(10); // 函数调用
C语言函数指针typedef
typedef int (*SumFunction)(int a,int b);
Block中的typedef
typedef int (^SumBlock)(int a,int b);
3、如何调用Blocks
主动调用一下:
- (void)someMethod
{
BoolBlock ablock = ^(BOOL bValue) {
NSLog(@"Bool block!");
};
ablock();
}
作为参数返回:
typedef void (^BoolBlock)(BOOL);
- (BoolBlock)foo()
{
BoolBlock ablock = ^(BOOL bValue) {
NSLog(@"Bool block!");
};
return [[ablock copy] autorelease];//一定要copy,将其复制到堆上,更详细的原理,将在后续章节讲解
}
类的一个成员:
@interface OBJ1 : NSObject @property (nonatomic, copy)BoolBlock block;//理由同上啊,同学们 @end
OBJ1 *obj1 = ...
obj1.block = ^(BOOL bValue) {
NSLog(@"Bool block!");
};
其他函数的参数:
- (void)foo(BoolBlock block) { if (block) { block(); } }
甚至其他block的参数:
BoolBlock bBlock = ^(BOOL bV){if(Bv){/*do some thing*/}}; HugeBlock hBlock = ^(BoolBlock bB) {bB();}; hBolck(bBlock);
二、Block内存管理与其他特性
1、Block放在哪里
1.1栈和堆
以下情况中的block位于堆中:
void foo() { __block int i = 1024; int j = 1; void (^blk)(void); void (^blkInHeap)(void); blk = ^{ printf("%d, %d ", i, j);};//blk在栈里 blkInHeap = Block_copy(blk);//blkInHeap在堆里 } - (void)fooBar { _oi = 1; OBJ1* oj = self; void (^oblk)(void) = ^{ printf("%d ", oj.oi);}; void (^oblkInHeap)(void) = [oblk copy];//oblkInHeap在堆中 }
1.2全局
以下情况中的block位于全局区
static int(^maxIntBlock)(int, int) = ^(int a, int b){return a>b?a:b;}; - (void)fooBar { int(^maxIntBlockCopied)(int, int) =[maxIntBlock copy]; } void foo() { int(^maxIntBlockCopied)(int, int) = Block_copy(maxIntBlock); }
需要注意的是,这里复制过后的block依旧位于全局区,实际上,复制操作是直接返回了原block对象。
2、Block引用的变量在哪里
1.全局区
全局区的变量存储位置与block无关:
static int gVar = 0; //__block static int gMVar = 1; void foo() { static int stackVar = 0; // __block static int stackMVar = 0; }
注意,static变量是不允许添加__block标记的
2.堆栈
void foo() { __block int i = 1024; // 此时i在栈上 int j = 1; // 此时j在栈上 void (^blk)(void); blk = ^{ printf("%d, %d ",i,j); }; //此时blk位于栈上,其使用的变量也在栈上 blk(); j++; int (^blkInHeap)(int, int) = Block_copy(blk); // 复制block后,block位于堆上,有__block标记的i会被复制一份至堆,而没有__block标记的j并不会动依旧位于栈上。 }
三、Block揭开神秘面纱
1、Block到底是什么
我们使用clang的rewrite-objc命令来获取转码后的代码。
我们来看看最简单的一个block
__block int i = 1024;
int j = 1;
void (^blk)(void);
blk = ^{
printf("i :%d,j:%d,&i:%p,&j:%p",i,j,&i,&j);
};
这个block仅仅打印栈变量i和j的值,其被clang转码为:
truct __main_block_impl_0{
struct __block_impl impl;
struct __main_block_desc_0 * Desc;
int j;
__Block_byref_i_0 *i;
__main_block_impl_0(void * fp, struct __main_block_desc_0 *desc,int _j,__Block_byref_i_0 *_i, int flags = 0) : j(_j),int(_i->__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_i_0 *i = __cself->i;
int j = __cself->j;
printf("i :%d,j:%d,&i:%p,&j:%p",i->__forwarding->i,j,&(i->__forwarding->i),&j);
}
static void __main_block_copy_0(struct __main_block_impl_0 *dst, struct __main_block_impl_0 *src){
_Block_object_assign((void *)&dst->i, (void *)src->i,8);
}
static void __main_block_dispose_0(struct __main_block_impl_0 *src){
_Block_object_dispose((void *)src->i,8);
}
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)(