block,我们把它叫做代码块, ^加上{}就组成了一个block, {}后面加上()就可以执行{}里面的代码
^{
NSLog(@"这就是block");
}()
如果想在用到的时候再调用, 就要用个变量把它存起来
//定义一个block,并用变量存起来
void (^block)() = ^ {
NSLog(@"这就是block");
}
//调用block
block();
block本质上是一个OC对象, 是封装了函数调用以及函数调用环境的OC对象, 它就像我们知道的其他OC对象一样, 它的内部也有isa指针
block的底层数据结构
block捕获变量(block访问block外面的变量的情况)
下面这段代码执行完毕后, 会输出多少呢?
int a = 100;
void (^block)() = ^ {
NSLog(@"a is %d",a);
}
a = 200;
block();
上面这段代码, 打印后输出100, 为什么会输出100, 而不是200呢? 这就是我们今天要讨论的block捕获变量.
我们定义的block, 可以在其他要用的地方进行调用,可是局部变量a出了它的作用域, 就被销毁了, 于是在我们调用block的时候, 它要访问的局部变量a可能就不存在了, 也就没法使用这个局部变量a了. 所以block为了能够在使用局部变量a的时候确保能够用, 它把局部变量a在它那里存一份.
下面我们再来看另一段代码, 打印后输出的值是多少呢?
static int b = 100;
void (^block)() = ^ {
NSLog(@"b = %d",b);
}
b = 200;
block();
上面这段代码, 打印后输出的值是200. 那么问题来了, 为什么会出现这种差异呢?
因为使用static修饰的变量b, 会一直存在内存中, block可以随时访问static修饰的变量b. 变量b也会存放到block里面去, 不过变量b跟变量a不一样, 变量b是把指针传进了block, 而变量a是把它的值传进了block. 这样无论外面的变量b是否发生改变, block都能使用真正的变量b.
我们再来看这段代码, 打印后输出的值是多少呢?
int a = 100;
static int b = 100;
int main() {
@autoreleasepool {
void (^block)(void) = ^{
NSLog(@"a = %d, b = %d",a,b);
}
a = 200;
b = 200;
block();
}
return 0;
}
上面这段代码输出的200, 200.为什么会这样呢?
因为这两个都是全局变量, 它会一直存在程序中, 不会像局部变量一样出了作用域就销毁, block在任何地方访问它们, 都是可以的. 并且block不会捕获全局变量, 因为block可以随时访问它们, 没必要对它们进行捕获, 而且将它们捕获到block里面, 还要花费资源把它们存起来, 这对程序来说简直是一种浪费.
总结上面的示例代码, 我们发现, 只要是局部变量, block都会捕获它. 我们没有主动使用任何修饰符修饰的变量(系统默认使用auto帮我们修饰了,也叫自动变量), block会对它进行值捕获, static修饰的静态变量, block会对它进行指针捕获. 而全局变量, block不会对它进行捕获.我们把以上总结做成下面这个表格

有了上面的总结, 我们再来看一段代码, 调用下面的test, 会对self进行捕获?
#import "Test.h"
@implementtation Test
- (void)test {
void (^block)(void) = ^{
NSLog(@"test:%p",self);
}
block();
}
block会对这个self进行捕获, 在我们的iOS中, 所有方法的调用, 底层都是给调用者发送消息, 所有的方法默认都会带两个参数, 方法调用者slef, 和方法名. 所以slef它是一个参数,参数都是局部变量, 所以slef它是一个局部变量. 我们上面已经总结出, 只要是局部变量, block就会捕获, 所以block会对这个self进行捕获.
block的类型
block作为OC对象, 也就可以像OC对象一样调用class方法.
void (^block)(void) = ^{
NSLog(@"Hello Block!");
};
NSLog(@"%@", [block class]);
NSLog(@"%@",[ [block class] superclass]);
NSLog(@"%@",[[[block class] superclass] superclass]);
NSLog(@"%@",[[[[block class] superclass] superclass] superclass]);
2019-07-01 18:14:03.880499+0800 blockDemo[50007:626703] __NSGlobalBlock__
2019-07-01 18:14:03.880732+0800 blockDemo[50007:626703] __NSGlobalBlock
2019-07-01 18:14:03.880758+0800 blockDemo[50007:626703] NSBlock
2019-07-01 18:14:03.880772+0800 blockDemo[50007:626703] NSObject
Program ended with exit code: 0
我们可以看到, 分别打印了3种不同的block, 和它们的之间的继承关系, 以及block最终的父类NSObject, 它的isa指针也跟其他OC对象一样是从NSObject来的 这也充分说明了block它是OC对象.

未完待续...
网友评论