什么是Block?
为什么使用Block?
怎么使用Block?
本文将从这三个问题入手来逐渐了解Block。
本文使用的范例传送门:https://github.com/Elbertz/ZDXBlockStudy
什么是Block?
首先我们先来观察一下block的书写格式
a (^b)(c,d)=^(c name1,d name2){};
a:Block的返回值类型,可以为空(void);
b:Block对象名称,可以理解为变量名;
^:块的语法标记,声明b为一个Block对象;
c:第一个参数类型
d:第二个参数类型
name1,name2:参数名;
{}:Block代码块的主题部分。
有人认为Block是代码块,以闭包形式存在的一段代码良好的独立性和可交互性;
也有人把Block当作OC的匿名函数,可以很好的传递参数;
还有人认为Block是一种特殊的数据类型,创建的Block对象可以更好的被上下文所调用;
还有一群人,他们觉得Block就是Block,盲目而片面的定义掩盖了它简易精妙的使用,因此他们从使用场景中描述Block是什么。
为什么使用Block?
Block作为参数、返回值可以更便捷的在各个场景间传递;
Block作为回调可以广泛的使用在多线程GCD、动画、排序的情形下;
下面是Block的基础使用,对Block的使用思路清晰的童鞋可以通过下面的2个案例进行深度学习:
Block实现各种排序:https://github.com/JiongXing/JXSort
AFNetworking:https://github.com/AFNetworking/AFNetworking
怎么使用Block?
在使用之前,我们先对Block在ARC和MRC环境下使用的基础知识点普及:
ARC:系统会帮你管理你自己创建的对象的内存
MRC:你需要管理你自己创建的对象的内存
当你创建一个新的project时默认是ARC环境,你可以在Build Setting下关闭自动引用记数切换到MRC环境。
为什么使用copy来修饰Block?
使用copy可以将Block从栈上转移到堆上
MRC下,默认是栈上为了控制Block生命周期,需要将其copy的堆上,不可以用reatin代替。 ARC下大多数情况默认是在堆上,但是因为一般遵循传统,会写上copy,但是可以用strong来代替。
ARC下需不需要对Block进行手动copy?
不用手动copy的情形?
1.当 Block 被强引用时。
2.系统的 API 中带有 usingBlock 时。
3.Block 作为函数返回值。
用手动copy的情形?
当block 作为函数参数的时候,在 arc 下我们自定义的Block 要写上 copy。
注意:copy的特点
1.如果原来在栈上,通过copy,被复制到堆上。
2.如果原来在全局数据区,不会发生改变
3.如果在堆区:其引用计数加1
在Block使用过程中,如何避免循环引用?
ARC情况下
1.如果用copy修饰Block,该Block就会存储在堆空间。则会对Block的内部对象进行强引用,导致循环引用。内存无法释放。
2.如果用weak修饰Block,该Block就会存放在栈空间。不会出现循环引用问题。
MRC情况下
用copy修饰后,如果要在Block内部使用对象,则需要进行(__block typeof(Target) blockTarget = Target )处理。在Block里面用blockTarget进行操作。
__block在MRC下有两个作用
1. 允许在Block中访问和修改局部变量
2. 禁止Block对所引用的对象进行隐式retain操作
__block在ARC下只有一个作用
1. 允许在Block中访问和修改局部变量
__weak可以避免在ARC下Block造成循环引用
使用场景?
标准范式:
// 1.使用typedef定义Block类型
typedef int(^MyBlock)(int, int);
// 2.定义一个形参为Block的OC函数
- (void)useBlockForOC:(MyBlock)aBlock
{
NSLog(@"result = %d", aBlock(300,200));
}
// 3.声明并赋值定义一个Block变量
MyBlock addBlock = ^(int x, int y){
return x+y;
};
// 4.以Block作为函数参数,把Block像对象一样传递
[self useBlockForOC:addBlock];
// 将第3点和第4点合并一起,以内联定义的Block作为函数参数
[self useBlockForOC:^(int x, int y){
return x+y;
}];
情景一:传递参数
viewController 控制器1,testViewController 控制器2,控制器1跳转到控制器2,然后在控制器2触发事件回调修改控制器1的对应控件的背景色为红色?
testViewController
typedef void (^myBlock)(UIColor* color);
@property (nonatomic,copy)myBlock block1;
UIColor *color = [UIColor redColor];
//给Block传入参数color
self.block1(color);
[self.navigationController popViewControllerAnimated:YES];
viewController
//plan1
testViewController *testVC = [[testViewController alloc]init];
testVC.block1 = ^(UIColor *color){
label1.backgroundColor = color;
};
[self.navigationController pushViewController:testVC animated:YES];
//plan2
//当block中使用了self时,需要对self添加__weak关键字,避免循环调用
__weak typeof(self) weakself = self;//等价于下一行代码
//__weak UIViewController* weakself = self;
testViewController *testVC = [[testViewController alloc]init];
testVC.block1 = ^(UIColor *color){
weakself.label2.backgroundColor = color;
};
[self.navigationController pushViewController:testVC animated:YES];
当在Block代码块中使用了self,请注意要避免循环应用。
Q:说好的女朋友呢? 看好了,女朋友的问题来了
一天,你的女朋友逛淘宝的时候对你说,这个包包好漂亮,买给我嘛?么么哒! 面对糖衣炮弹轰击对你默默的登录了自己的支付宝账号点下了确认支付的按钮。
女朋友想买的东西委托给你去买,这是代理模式的典型案例,同样也可用block实现。
1)代理模式实现
girlFriends *gf = [girlFriends shareInstance];
gf.delegate = self;
[gf buyALadiesBackpackForMe];
2)使用Block实现
__weak typeof(self) weakself = self;
//block先赋值,再使用
girlFriends *gf = [girlFriends shareInstance];
//[gf buyAnotherLadiesBackpackForMe]; //wrong write place
gf.buyBlock = ^(NSString *goods){
NSString *tempStr = [NSString stringWithFormat:@"%@❤️❤️❤️",goods];
[weakself.label4 setText:tempStr];
};
[gf buyAnotherLadiesBackpackForMe];
if (timer.isValid == YES) {
[timer invalidate];
}
3.使用通知模式实现
//tips:先注册观察者,再发送通知
girlFriends *gf = [girlFriends shareInstance];
//[gf buyAnyoneLadiesBackpackForMe];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(buyLVAction:) name:@"buybag" object:nil];
[gf buyAnyoneLadiesBackpackForMe];
-(void)dealloc{
//记得释放通知的观察者对象
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"buybag" object:nil];
}
情景二:Block作为参数
girlFriends *gf = [girlFriends shareInstance];
[gf buyMoreLadiesBackpackForMe:^float(int loveNumber) {
//
NSLog(@"520");
float result = 520+(float)loveNumber/10000;
NSLog(@"%.4f",(float)loveNumber/10000);
NSString *tempStr = [NSString stringWithFormat:@"%.4f❤️❤️❤️",result];
[_label6 setText:tempStr];
return result;
}];
if (timer.isValid == YES) {
[timer invalidate];
}
girlFriends.m
-(void)buyMoreLadiesBackpackForMe:(myLoveBlock2)ablock{
float result = ablock(1314);
NSLog(@"%.4f",result);
}
情景三:Block作为返回值
通过递归调用来体现以Block作为返回值的函数的调用和实现
调用
[self digui:3];
实现
- (int)digui:(int)number{
if (number <= 2 && number > 0) {
return number;
} else {
int tempResult = [self digui:number-1];
return number*tempResult;
}
}
调用
gf.setupTab5(3);
实现
- (int(^)(int))setupTab5
{
__weak typeof (self)weakSelf = self;
int(^block)(int) = ^(int a){
_recursiveResult2 *= a;
NSLog(@”%d – %d”, _recursiveResult2,a);
if (a>1) {
weakSelf.setupTab5(a-1);
//weakSelf setupTab5;
}
return _recursiveResult2;
};
return block;
}