block
的基础知识
block
修饰是用strong
还是copy
对于现在而言,这两种都可以了,因为在ARC
的模式下,它会把block
移动到堆上
block
的分类
//ARC 模式下
// Global:没有访问auto变量:__NSGlobalBlock__
void (^block1)(void) = ^{
NSLog(@"block1---------");
};
// Stack:访问了auto变量: __NSStackBlock__
int a = 10;
void (^block2)(void) = ^{
NSLog(@"block2---------%d", a);
};
NSLog(@"%@ %@", [block1 class], [block2 class]);
// __NSStackBlock__调用copy : __NSMallocBlock__
NSLog(@"%@ %@", [[block2 copy] class],[^{
NSLog(@"block2---------%d", a);
} class]);
//MRC 模式下
// Global:没有访问auto变量:__NSGlobalBlock__
void (^block4)(void) = ^{
NSLog(@"block4---------");
};
// Stack:访问了auto变量: __NSStackBlock__
int a1 = 10;
void (^block5)(void) = ^{
NSLog(@"block5---------%d", a1);
};
NSLog(@"MAC %@ %@", [block4 class], [block5 class]);
// __NSStackBlock__调用copy : __NSMallocBlock__
NSLog(@"%@ %@", [[block5 copy] class],[^{
NSLog(@"block2---------%d", a);
} class]);
/*
2020-03-20 16:51:43.250229+0800 block解读[6703:829455] __NSGlobalBlock__ __NSMallocBlock__
2020-03-20 16:51:43.251585+0800 block解读[6703:829455] __NSMallocBlock__ __NSStackBlock__
2020-03-20 16:34:07.418258+0800 block解读[6611:820683] mac __NSGlobalBlock__ __NSStackBlock__
2020-03-20 16:34:07.418335+0800 block解读[6611:820683] __NSMallocBlock__ __NSStackBlock__
*/
-
NSGlobalBlock
全局block
void (^myBlock) (void) = ^(void){
NSLog(@"this is edison ");
};
//能用%@ 说明block是对象 可打印
NSLog(@"%@",myBlock);
/*
<__NSGlobalBlock__: 0x10c36b088>
*/
-
NSMallocBlock
堆block
int a = 9;
void (^myBlock) (void) = ^(void){
NSLog(@"this is edison %d",a);
};
NSLog(@"%@",myBlock);
/*
<<__NSMallocBlock__: 0x604000446390>
一个栈的变量 进入block之后就变成了malloc block
*/
一个栈的变量 进入block之后就变成了malloc block ????黑人问好??? 后面会解释
-
NSMallocBlock
栈block
NSLog(@"%@",^(void){
//NSLog(@"this is edison %d",a);
NSLog(@"this is edison ");
});
/*
<__NSGlobalBlock__: 0x10a927080>
引入a的是NSStackBlock 没有引入a的是NSGlobalBlock
*/
block的底层探讨
__block
如上图所示,当我们需要在block
里引用局部变量(引用全局变量不会出现这样的错误),就会报错,当然我们都知道这种解决报错的方式就是使用__block
修饰局部变量,但是为什么要这么解决呢
带着问题我们来探讨
首先用终端创建一个.c的文件
touch blockTest.c
vim blockTest.c
.c文件里写入如下代码,其实就是c语言的main
#include "stdio.h"
int main(){
void (^block)(void) = ^(void){
printf("this is edison block \n");
}
block();
return 0 ;
}
然后编译.c文件
gcc blockTest.c
就会产生一个 a.out
的可执行文件
继续执行这个 a.out
的可执行文件
./a.out blockTest.c
/*
结果:
this is edison block
*/
这个只是执行一下 a.out
的可执行文件
重点来了,我们继续
clang
命令
clang -rewrite-objc blockTest.c
结果就会产生一个blockTest.cpp
,
blockTest.c
变成了blockTest.cpp
的c++
文件
,用c++
的原理探讨block
打开blockTest.cpp
文件,吓一跳讲真,我们拖到最底下,找到
int main(){
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0 ;
}
对比blockTest.c
int main(){
void (^block)(void) = ^(void){
printf("this is edison block \n");
}
block();
return 0 ;
}
现在改变.C
#include "stdio.h"
int main(){
int a = 0;
void (^block)(void) = ^(void){
printf("this is edison block %d\n",a);
};
block();
return 0 ;
}
blockTest.cpp
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int a = __cself->a; // bound by copy
printf("this is edison block %d\n",a);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(){
int a = 0;
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0 ;
}
就是c文件的a是在栈里的没问题吧,block被拷贝进了堆了这个也没问题吧,
blockTest.cpp
文件这个如上图标示出来的这段代码
int a = __cself->a; // bound by copy --> int a 表示重新定义了一个a 去接收传进来的__cself->a的值
这里传的是a的值
再先引进一个代码解释
int a = 9;
NSLog(@"a=%p",&a);
void (^myBlock) (void) = ^(void){
NSLog(@"this is edison %p",&a);
};
myBlock();
/*
2018-08-06 21:27:41.239550+0800 BlockTest[909:63152] a=0x7ffeed15bbdc
2018-08-06 21:27:41.239748+0800 BlockTest[909:63152] this is edison 0x604000253070
*/
你会发现外面的a
和里面的a
根本不是一个地址,说明两个a
根本不一样
__block int a = 9;//底层会做了把 栈/常量 等copy到堆区里去,并且开辟一个堆的空间 这里传的是a的指针地址
NSLog(@"a=%p",&a);
void (^myBlock) (void) = ^(void){
NSLog(@"this is edison %p",&a);
};
myBlock();
__block修饰的传的是a的地址
所以如果a++
这是可以的,因为a++
其实修改的是地址++
来达到修改外部变量的效果
循环引用
1
关于block
里的属性,如果是带下划_name
线操作的而不是self.name
的操作一样会引起循环引用,因为self
对name
进行了强引用
2
__block ViewController * weSelf = self;
self.block3 = ^(NSString *name) {
NSLog(@"---%@",weSelf);
};
这样能解决循环引用嘛。答案是不能
__block的作用是把当前的内存拷贝到堆里去,那么可以手动释放
__block ViewController * weSelf = self;
self.block3 = ^(NSString *name) {
NSLog(@"---%@",weSelf);
weSelf= nil;
};
这样就可以了 ,把指向的地址置空
3
在block的参数里把self
传进去
self.block3 = ^(ViewController *vc) {
NSLog(@"name=%@",vc.name);
}
4
__weak __strong 先弱后强
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};