什么是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;
}