什么是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:第一個參數(shù)類型
d:第二個參數(shù)類型
name1仓洼,name2:參數(shù)名;
{}:Block代碼塊的主題部分介陶。
有人認為Block是代碼塊,以閉包形式存在的一段代碼良好的獨立性和可交互性色建;
也有人把Block當作OC的匿名函數(shù)哺呜,可以很好的傳遞參數(shù);
還有人認為Block是一種特殊的數(shù)據(jù)類型箕戳,創(chuàng)建的Block對象可以更好的被上下文所調(diào)用某残;
還有一群人国撵,他們覺得Block就是Block,盲目而片面的定義掩蓋了它簡易精妙的使用玻墅,因此他們從使用場景中描述Block是什么介牙。
為什么使用Block?
Block作為參數(shù)澳厢、返回值可以更便捷的在各個場景間傳遞环础;
Block作為回調(diào)可以廣泛的使用在多線程GCD、動畫剩拢、排序的情形下线得;
下面是Block的基礎(chǔ)使用,對Block的使用思路清晰的童鞋可以通過下面的2個案例進行深度學(xué)習(xí):
Block實現(xiàn)各種排序:https://github.com/JiongXing/JXSort
AFNetworking:https://github.com/AFNetworking/AFNetworking
怎么使用Block徐伐?
在使用之前贯钩,我們先對Block在ARC和MRC環(huán)境下使用的基礎(chǔ)知識點普及:
ARC:系統(tǒng)會幫你管理你自己創(chuàng)建的對象的內(nèi)存
MRC:你需要管理你自己創(chuàng)建的對象的內(nèi)存
當你創(chuàng)建一個新的project時默認是ARC環(huán)境,你可以在Build Setting下關(guān)閉自動引用記數(shù)切換到MRC環(huán)境呵晨。
為什么使用copy來修飾Block魏保?
使用copy可以將Block從棧上轉(zhuǎn)移到堆上
MRC下,默認是棧上為了控制Block生命周期摸屠,需要將其copy的堆上谓罗,不可以用reatin代替。 ARC下大多數(shù)情況默認是在堆上季二,但是因為一般遵循傳統(tǒng)檩咱,會寫上copy,但是可以用strong來代替胯舷。
ARC下需不需要對Block進行手動copy刻蚯?
不用手動copy的情形?
1.當 Block 被強引用時桑嘶。
2.系統(tǒng)的 API 中帶有 usingBlock 時炊汹。
3.Block 作為函數(shù)返回值。
用手動copy的情形逃顶?
當block 作為函數(shù)參數(shù)的時候讨便,在 arc 下我們自定義的Block 要寫上 copy。
注意:copy的特點
1.如果原來在棧上以政,通過copy霸褒,被復(fù)制到堆上。
2.如果原來在全局數(shù)據(jù)區(qū)盈蛮,不會發(fā)生改變
3.如果在堆區(qū):其引用計數(shù)加1
在Block使用過程中废菱,如何避免循環(huán)引用?
ARC情況下
1.如果用copy修飾Block,該Block就會存儲在堆空間殊轴。則會對Block的內(nèi)部對象進行強引用衰倦,導(dǎo)致循環(huán)引用。內(nèi)存無法釋放梳凛。
2.如果用weak修飾Block耿币,該Block就會存放在検嵝樱空間韧拒。不會出現(xiàn)循環(huán)引用問題。
MRC情況下
用copy修飾后十性,如果要在Block內(nèi)部使用對象叛溢,則需要進行(__block typeof(Target) blockTarget = Target )處理。在Block里面用blockTarget進行操作劲适。
__block在MRC下有兩個作用
1. 允許在Block中訪問和修改局部變量
2. 禁止Block對所引用的對象進行隱式retain操作
__block在ARC下只有一個作用
1. 允許在Block中訪問和修改局部變量
__weak可以避免在ARC下Block造成循環(huán)引用
使用場景楷掉?
標準范式:
// 1.使用typedef定義Block類型
typedef int(^MyBlock)(int, int);
// 2.定義一個形參為Block的OC函數(shù)
- (void)useBlockForOC:(MyBlock)aBlock
{
NSLog(@"result = %d", aBlock(300,200));
}
// 3.聲明并賦值定義一個Block變量
MyBlock addBlock = ^(int x, int y){
return x+y;
};
// 4.以Block作為函數(shù)參數(shù),把Block像對象一樣傳遞
[self useBlockForOC:addBlock];
// 將第3點和第4點合并一起,以內(nèi)聯(lián)定義的Block作為函數(shù)參數(shù)
[self useBlockForOC:^(int x, int y){
return x+y;
}];
情景一:傳遞參數(shù)
viewController 控制器1,testViewController 控制器2霞势,控制器1跳轉(zhuǎn)到控制器2烹植,然后在控制器2觸發(fā)事件回調(diào)修改控制器1的對應(yīng)控件的背景色為紅色?
testViewController
typedef void (^myBlock)(UIColor* color);
@property (nonatomic,copy)myBlock block1;
UIColor *color = [UIColor redColor];
//給Block傳入?yún)?shù)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關(guān)鍵字草雕,避免循環(huán)調(diào)用
__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,請注意要避免循環(huán)應(yīng)用固以。
Q:說好的女朋友呢墩虹? 看好了,女朋友的問題來了
一天憨琳,你的女朋友逛淘寶的時候?qū)δ阏f诫钓,這個包包好漂亮,買給我嘛篙螟?么么噠菌湃! 面對糖衣炮彈轟擊對你默默的登錄了自己的支付寶賬號點下了確認支付的按鈕。
女朋友想買的東西委托給你去買遍略,這是代理模式的典型案例惧所,同樣也可用block實現(xiàn)。
1)代理模式實現(xiàn)
girlFriends *gf = [girlFriends shareInstance];
gf.delegate = self;
[gf buyALadiesBackpackForMe];
2)使用Block實現(xiàn)
__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.使用通知模式實現(xiàn)
//tips:先注冊觀察者纯路,再發(fā)送通知
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作為參數(shù)
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作為返回值
通過遞歸調(diào)用來體現(xiàn)以Block作為返回值的函數(shù)的調(diào)用和實現(xiàn)
調(diào)用
[self digui:3];
實現(xiàn)
- (int)digui:(int)number{
if (number <= 2 && number > 0) {
return number;
} else {
int tempResult = [self digui:number-1];
return number*tempResult;
}
}
調(diào)用
gf.setupTab5(3);
實現(xiàn)
- (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;
}