先看这一篇 juejin.cn/post/684490…
基本定义
<#返回值类型#>(^<#blockName别名#>)(<#parameterTypes传参#>) = ^(<#parameters#>) {
<#statements#>
};
//没有返回值,有一个传参数
void(^testBlock)(NSString* ) = ^(NSString*name) {
NSLog(@"%@",name);
};
testBlock(@"我是说");
//有返回值,有两个传参数
NSInteger(^numtotal)(NSInteger,NSInteger) = ^(NSInteger a,NSInteger b) {
return a + b ;
};
NSLog(@" 两数之和:%ld",numtotal(1,2));
block本质上也是oc对象
handleNum block1 = ^(NSInteger num){
// NSLog(@"%ld",a);
};
NSLog(@"%@",[block1 class]);
NSLog(@"%@",[[block1 class] superclass]);
NSLog(@"%@",[[[block1 class] superclass] superclass]);
NSLog(@"%@",[[[[block1 class] superclass] superclass] superclass]);
打印结果

捕获外部值
//没有返回值,有一个传参数
NSInteger age = 12;
void(^testBlock)(NSString* ) = ^(NSString*name) {
NSLog(@"%@--%ld",name,age);
};
NSLog(@"%ld",age);
age = 433;
NSLog(@"%ld",age);
testBlock(@"我是说");

__block NSInteger age = 12;
或者是用个static关键字,全局变量
void(^testBlock)(NSString* ) = ^(NSString*name) {
age = 3;
NSLog(@"%@--%ld",name,age);
};
NSLog(@"%ld",age);
age = 433;
NSLog(@"%ld",age);
testBlock(@"我是说");
NSLog(@"%ld",age);
打印结果如下

Person* age = [Person new];
age.name= @"12";
void(^testBlock)(NSString* ) = ^(NSString*name) {
NSLog(@"%@--%@",name,age);
};
NSLog(@"%@",age);
[age setName:@"33333"];
NSLog(@"%@",age);
testBlock(@"我是说");


Person* age = [Person new];
age.name= @"12";
void(^testBlock)(NSString* ) = ^(NSString*name) {
[age setName:@"dddd"];
NSLog(@"%@--%@",name,age);
};
NSLog(@"%@",age);
[age setName:@"33333"];
NSLog(@"%@",age);
testBlock(@"我是说");
而这样是可以的,因为它操作的是指针指向的那个对象,并没有改变指针的指向。
在实际开发中,很少像上边那么使用,更多的情况是作为一个参数传递。
typedef void(^handleNum)(NSInteger);
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = @"aaavc";
self.view.backgroundColor = [UIColor lightGrayColor];
handleNum ha = ^(NSInteger num){
NSLog(@"%ld",num);
};
//第二个参数是block
[self numbOperation:@[@2,@4,@5,@6] andhanle:^(NSInteger num) {
NSLog(@"%ld",num);
}];
}
- (void)numbOperation:(NSArray*)array andhanle:(handleNum)block
{
NSUInteger count = [array count];
for (NSUInteger i = 0; i < count; i++) {
block([array[i] integerValue]);
}
}

使用block最常见的问题就是循环引用问题,循环引用也可能发生在delegate或NSTimer中,具体可以自行查阅。 前文讲过如果block内部访问了外部变量会进行值捕获,block同样是一个对象,他也有isa指针,也有引用计数,如过一个对象持有了一个block而block内部也捕获了这个对象,那么就会产生循环引用。

{
NSString* _name;
}
__weak AViewController* weakVC = self;
self.nameBlock = ^{
__strong AViewController* vc = weakVC;
vc->_name ;
};
备注:self.property会调用到settergetter方法 ,vc->_name是访问成员变量到唯一方法,不走setter方法。
问题二:__strong 什么时候会用?
block里使用一个对象,如果用弱指针,对象释放,如果进行add的话,会crash,为了保证block执行过程中对象不被释放,我们可以在block内部初始化一个strong指针。文章中讲的验证了都是正确的, 需要注意arc环境下,不能直接调用retaincount,需要使用桥接
NSLog(@"after block retainCount:%zd", CFGetRetainCount((__bridge CFTypeRef)obj));
问题三:block为啥用copy修饰?
block分为三种: 一 NSGlobalBlock:全局的静态block 没有访问外部变量 你的block类型就是这种类型(也就是说你的block没有调用其他外部变量) 二 NSStackBlock:保存在栈中的block,没有用copy去修饰并且访问了外部变量,你的block类型就是这种类型,会在函数调用结束被销毁 (需要在MRC) 三 NSMallocBlock 保存在堆中的block 此类型blcok是用copy修饰出来的block 它会随着对象的销毁而销毁,只要对象不销毁,我们就可以调用的到在堆中的block。
默认情况下,block会存档在栈中(栈是吃了吐),所以block会在函数调用结束被销毁,在调用会报空指针异常,如果用copy修饰的话,可以使其保存在堆区(堆是吃了拉) ,它的生命周期会随着对象的销毁而结束的。只要对象不销毁,我们就可以调用在堆中的block。
问题三:block不能修改对象的指针,为什么? 这个原因和int a类似,先看这张图

在block内部修改p,肯定改不了外边p的指向,所以报错。
block的内存结果是怎么样的?