扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
Block 是将函数及其执行上下文封装起来的对象。 比如:
成都创新互联专业为企业提供武鸣网站建设、武鸣做网站、武鸣网站设计、武鸣网站制作等企业网站建设、网页设计与制作、武鸣企业网站模板建站服务,10多年武鸣做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。
通过 clang -rewrite-objc WYTest.m 命令编译该 .m 文件,发现该 block 被编译成这个形式:
其中 WYTest 是文件名, blockTest 是方法名,这些可以忽略。其中 WYTest blockTest_block_impl_0 结构体为
--block_impl 结构体为
block 内部有 isa 指针,所以说其本质也是 OC 对象
block 内部则为:
所以说 Block 是将函数及其执行上下文封装起来的对象
既然 block 内部封装了函数,那么它同样也有参数和返回值。
如果你正在跳槽或者正准备跳槽不妨动动小手,添加一下咱们的交流群1012951431来获取一份详细的大厂面试资料为你的跳槽多添一份保障。
这里的输出是 6 而不是 2,原因就是对局部变量 num 的截获是值截获。同样,在 block 里如果修改变量 num ,也是无效的,甚至编译器会报错。
打印为 1,2,3
局部对象变量也是一样,截获的是值,而不是指针,在外部将其置为 nil ,对 block 没有影响,而该对象调用方法会影响
输出为 2,意味着 num = 1 这里的修改 num 值是有效的,即是指针截获。同样,在 block 里去修改变量 m ,也是有效的。
编译后
( impl.isa = _NSConcreteStackBlock ;这里注意到这一句,即说明该 block 是栈 block )
可以看到局部变量被编译成值形式,而静态变量被编成指针形式,全局变量并未截获。而 --block 修饰的变量也是以指针形式截获的,并且生成了一个新的结构体对象:
该对象有个属性: num5 ,即我们用 --block 修饰的变量。这里 --forwarding 是指向自身的(栈 block )。
一般情况下,如果我们要对 block 截获的局部变量进行赋值操作需添加 --block 修饰符,而对全局变量,静态变量是不需要添加 --block 修饰符的。
另外, block 里访问 self 或成员变量都会去截获 self 。
分为全局 Block(_NSConcreteGlobalBlock) 、栈 Block(_NSConcreteStackBlock) 、堆
Block(_NSConcreteMallocBlock) 三种形式
其中栈 Block 存储在栈 (stack) 区,堆 Block 存储在堆 (heap) 区,全局 Block 存储在已初始化数据 (.data) 区
输出:
比如:
输出:
日常开发常用于这种情况:
比如堆 1中的全局进行 copy 操作,即赋值:
输出:
仍是全局 block
而对 2中的栈 block 进行赋值操作:
输出:
对栈 blockcopy 之后,并不代表着栈 block 就消失了,左边的 mallock 是堆 block ,右边被 copy 的仍是栈 block 比如:
输出:
即如果对栈 Block 进行 cop ,将会 copy 到堆区,对堆 Block 进行 copy ,将会增加引用计数,对全局 Block 进行 copy ,因为是已经初始化的,所以什么也不做。
另外, --block 变量在 copy 时,由于 --forwarding 的存在,栈上的 --forwarding 指针会指向堆上的-- forwarding 变量,而堆上的 --forwarding 指针指向其自身,所以,如果对 --block 的修改,实际上是在修改堆上的 --block 变量。
即 --forwarding 指针存在的意义就是,无论在任何内存位置,都可以顺利地访问同一个 --block 变量 。
另外由于 block 捕获的 --block 修饰的变量会去持有变量,那么如果用 --block 修饰 self ,且 self 持有
block ,并且 block 内部使用到 --block 修饰的 self 时,就会造成多循环引用,即 self 持有 block , block 持有 --block 变量,而 --block 变量持有 self ,造成内存泄漏。
比如:
如果要解决这种循环引用,可以主动断开 --block 变量对 self 的持有,即在 block 内部使用完 weakself 后, 将其置为 nil ,但这种方式有个问题,如果 block 一直不被调用,那么循环引用将一直存在。
所以,我们最好还是用 --weak 来修饰 self
以上就是 block 篇的面试题合集了,感谢观看~!
在平时的开发中,对于block,只是知道主要用于数据回调上,对于其内部实现方式,以及一些使用时的注意事项,还不是很了解。在网上找个一些关于block的文章,做了一个简单的整理。希望对于想学习研究block的同学有帮助。另外,非常感谢这些文章的作者,感谢你们把自己的知识经验分享出来。
首先是一篇唐巧的文章,对于唐巧,作为iOS开发者应该不陌生。
谈Objective-C block的实现
下边这篇文章是block的学习使用
OS开发-由浅至深学习block
下边这篇文章是在唐巧的文章中摘出来的,写的也很详尽。作者也引用了很多其他优秀的文章,可以作为学习参考。
对Objective-C中Block的追探
下边这篇文章也对block的原理做了很详细的介绍,
Objective-C中的Block
再次感谢以上开发者的分享,如果大家有其他优秀的文章,欢迎留言分享,一起进步学习。
然而若定义一个block属性,并在block的实现中做如下操作:
此时当调用ModalViewController的dismiss方法的时候不会调用dealloc方法中的打印语句,说明ModalViewController没有被真正的销毁。因为ModalViewController强引用一个block属性,block会对内部的强指针self进行一次强引用。所以造成循环引用
如果这样呢?
这样呢?
这样呢?
如果想在block内部修改从外部捕获的auto变量的值,可以在该auto变量定义的时候,加上关键字__block
__block只可以用来作用于auto变量,它的目的就是为了能够让auto变量能够在block内部内修改。而全局变量和static变量本来就可以从block内部进行修改,因此__block对它们来说没有意义,所以__block被规定只能用于修饰auto变量
我们把main.m 文件 改成main.cpp文件看下block到底是什么。
为了比较,我特意加了一个int b作为对比。
基本类型的auto变量被block捕获的时候,就是通过值拷贝的形式把值赋给block内部相对应的基本类型变量。而例子里的__block int a = 10,我们可以看到在底层,系统是把int a包装到了一个叫__Block_byref_a_0的对象里面。
看下__block_byref_a_0的对象里有啥:
这样能看出来,外面使用__block 修饰的int a 在__block_byref_a_0对象里,就是最后的int a 这个结构体里的int a 也就是我们在block里需要调用的a。
1、在block外部 使用__block修饰变量。
2、在block内部使用被修饰的变量时,会创建一个__Block_byref_xx_0 *_xx对象。(此处的xx为变量名,下同)
3、被创建的对象,会生成__Block_byref_xx_0 *__forwarding;//指向自身类型对象的指针
4、最后__forwarding这个指针指向最新的值,然后再指回__Block_byref_xx_0 *_xx
5、最终当我们打印的时候,如果变量被__block修饰了,会被内部修改。
声明一个无返回值的block
typedef void(^onSearch)(NSString *searchText); ////////onSearch 就是这个block的函数指针
2.在控制器中头文件.h中定义block,分两步:
1定义成属性
@property (nonatomic) onSearch searchBlock;
2定义一个block的setter方法
-(void)setSearchBlock:(void(^)(NSString *searchText)) block;
3.在控制器的实现文件.m中来实现
-(void)setSearchBlock:(void(^)(NSString *searchText)) block {
if (block) {
self.searchBlock = block;
}
}
4.在控制器中使用block实现回调. 当点击search按钮时,将search的文本传递给订阅者
-(void)searchHandler:(id)sender {
if (self.searchBlock) {
self.searchBlock(@"your search keyword")
}
}
5.在订阅者中,接收过来的参数,并做其它操作
YourVC *vc = [YourVC new];
[vc setSearchBlock:^(NSString *searchText) {
NSLog(@"get search keyword")
}]
声明和使用Block:
Apple文档中介绍了如何将一个Block声明为变量,并将其作为一个函数使用:
int (^oneFrom)(int) = ^(int anInt) {
return anInt - 1;
};
// 创建了一个内联块^(int anInt)... ,其函数体和结果被传到了另外一个名为OneFrom的Block。
printf("1 from 10 is %d", oneFrom(10));
// 打印出: "1 from 10 is 9"
// 这个block函数(distanceTraveled)传入3个float型参数,返回float值。
float (^distanceTraveled) (float, float, float) =
^(float startingSpeed, float acceleration, float time) {
float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);
return distance;
};
也可以传入一个Block作为一个参数,而不要以如上的方式声明,这样就可以在需要将block作为参数的时候以内联代码的方式简单地实现。
NSArray *anArray = [NSArray arrayWithObjects: @"cat", @"dog",nil];
sortFunction(anArray, ^(string *a string *b){
if ( a == @"cat") return TRUE; });
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流