說過了KVO
這次我們來說一個一對一的消息傳遞方式师溅,Block。
Block是什么
Block盾舌,很多語言中翻譯做閉包墓臭,用《Objective-C高級編程》中的話說:
Blocks是C語言的擴充功能⊙矗可以用一句話來表示Blocks的擴充功能:帶有自動變量(局部變量)的額匿名函數(shù)窿锉。
所以,Block就是一個帶有自動變量的匿名函數(shù)膝舅。
匿名函數(shù)
顧名思義嗡载,匿名函數(shù)就是沒有名稱的函數(shù),C語言是不允許出現(xiàn)沒有函數(shù)名的函數(shù)的仍稀,但是因為實際上調用函數(shù)也是調用指向函數(shù)的函數(shù)指針洼滚,但是沒有函數(shù)名,就沒辦法獲取到函數(shù)的指針琳轿。那么block具體是怎么來實現(xiàn)的呢判沟?我們先往下看耿芹。
自動變量
在棧上聲明一個變量如果不是靜態(tài)變量或全局變量,是不可以在這個棧內聲明的匿名函數(shù)中使用的挪哄,但是在block中卻可以吧秕。
Block結構
在Xcode里,我們敲入快捷鍵inlineBlock
就會看到有這樣一個block的樣式提供給我們迹炼。
//增加了returnType砸彬,可省略
returnType(^blockName)(parameterTypes) = ^returnType(parameters) {
statements
};
其中第二個returnType
是我加上去的,為了能看的明顯一點斯入。
但是一下子看到一個這樣的東西還是有點亂砂碉,我們把它拆分成聲明部分和實現(xiàn)部分來看就會清楚很多了。
聲明Block
returnType(^blockName)(parameterTypes)
聲明block中包括了返回類型刻两、^增蹭、block名稱、參數(shù)列表磅摹。
實現(xiàn)Block
^returnType(parameters) {
statements
};
實現(xiàn)block中包括了返回類型(可省略)滋迈、參數(shù)列表(可省略)、實現(xiàn)代碼户誓。
其中返回值類型可以省略饼灿。
^(parameters) {
statements
};
參數(shù)列表也可以省略。
^{statements};
Block的使用
因為block的傳入?yún)?shù)和返回值都可以為空帝美,所以Block的使用可以分為4中模式:
1.無參數(shù)碍彭、無返回值。
2.有參數(shù)悼潭、無返回值庇忌。
3.無參數(shù)、有返回值女责。
4.有參數(shù)漆枚、有返回值。
接下來我們就來舉例子看看這幾種方式的使用抵知。
無參數(shù)墙基、無返回值
//無參數(shù)、無返回值
- (void)blockWithoutParameterAndWithoutReturn
{
void(^noParameterNoReturn)(void) = ^(void){
NSLog(@"無參數(shù)刷喜、無返回值");
};
noParameterNoReturn();
}
有參數(shù)残制、無返回值
//有參數(shù)、無返回值
- (void)blockWithParameterAndWithoutReturn
{
void(^parameterNoReturn)(NSInteger number) = ^(NSInteger number){
NSLog(@"有參數(shù)掖疮、無返回值初茶,參數(shù)是%lu",number);
};
parameterNoReturn(10);
}
無參數(shù)、有返回值
//無參數(shù)浊闪、有返回值
- (void)blockWithoutParameterAndWithReturn
{
NSInteger(^noParameterReturn)(void) = ^{
NSInteger number = 20;
NSLog(@"無參數(shù)恼布、有返回值螺戳,返回值是%lu",number);
return number;
};
NSInteger number = noParameterReturn();
NSLog(@"返回值是%lu",number);
}
有參數(shù)、有返回值
//有參數(shù)折汞、有返回值
- (void)blockWithParameterAndWithReturn
{
NSInteger(^parameterAndReturn)(NSInteger numberA, NSInteger numberB) = ^(NSInteger numberA, NSInteger numberB){
NSLog(@"有參數(shù)倔幼、有返回值,參數(shù)是%lu爽待、%lu,返回值是%lu",numberA,numberB,numberA+numberB);
return numberA+numberB;
};
NSInteger numberSum = parameterAndReturn(30,40);
NSLog(@"返回值是%lu",numberSum);
}
使用typedef定義
除了上邊的常規(guī)操作之外损同,block還可以作為OC中的一個參數(shù),這時候可以用到typedef來定義一個block鸟款,然后在函數(shù)調用時進行參數(shù)傳遞膏燃。
比如先定義一個block參數(shù):
//number作為參數(shù),無返回值
typedef void(^typedefBlock)(NSInteger number);
然后聲明一個函數(shù)中帶有此變量
//typedef block
- (void)testTypedefBlockWith:(typedefBlock)testTypedefBlock
{
NSLog(@"開始使用typedef block");
testTypedefBlock(12);
NSLog(@"結束使用typedef block");
}
這時候調用此方法何什,在回調的方法中就可以獲取到傳遞過來的值组哩。
[self testTypedefBlockWith:^(NSInteger number) {
NSLog(@"回調 typedef block number %lu",number);
}];
Block與外界變量
默認情況
通常情況下,對于block外的變量引用富俄,block默認是將其復制到block的數(shù)據(jù)結構中實現(xiàn)訪問的禁炒,也就是說只有block中用到的變量,block才會把他自動截獲進來霍比,而且因為截取的是瞬時值,所以之后在外部改變變量的值也不會改變值得大小暴备。因為截獲自動變量會存儲在block內部悠瞬,所以會導致block體積變大。
另外需要注意的一點就是block內部只能調用getter方法涯捻,不可以調用setter方法浅妆,所以是沒辦法修改外部變量的值的。
比如:
- (void)autoParamterTest
{
NSInteger number = 100;
void(^autoParamter)(void) = ^(void){
NSLog(@"%lu",number); //輸出100
};
number = 200;
autoParamter();
}
這段代碼最后會輸出100障癌,因為在定義block時凌外,他已經(jīng)把number的值復制到block中了,所以再改變他涛浙,對block中的值也不會有影響康辑。
另外,在block中對number賦值時轿亮,編譯器會直接報錯疮薇。
[圖片上傳中...(block-setter.png-8afa38-1521083690526-0)]
__block
對于這種情況羹奉,OC提供了_block(兩個下劃線)來修飾外部變量震鹉,使用了__block修飾的外部變量卫键,block內部是復制其引用地址來實現(xiàn)訪問數(shù)據(jù)的输拇,所以block內部可以修改block外部的變量值贪庙。
- (void)autoBlockParameterTest
{
__block NSInteger number = 100;
void(^autoBlockParamter)(void) = ^(void){
NSLog(@"%lu",number); //輸出200
number = 300;
NSLog(@"%lu",number); //輸出300
};
number = 200;
autoBlockParamter();
}
那為什么在加了 __block修飾符之后就可以訪問了呢?后邊我們會詳細說明拇舀,我們先往下看变抽。
Block的循環(huán)引用
Block是很好用,但是用不好的時候就容易出現(xiàn)循環(huán)引用掠抬,比如在某各類將block作為自己的變量补履,然后又在這個block的方法中使用了這個類自己的東西,這時候兩者互相持有就會發(fā)生循環(huán)引用剿另,引起內存泄漏的問題箫锤。比如如下代碼:
- (void)blockCircularReference
{
self.circleBlock = ^(NSInteger number) {
[self autoParamterTest];
};
}
但是蘋果也給出了相應的解決方案來處理block下的循環(huán)引用。
__weak修飾
可以直接用__weak(有兩個下劃線)來修飾雨女,來打破block中的循環(huán)谚攒,使用__weak修飾解決循環(huán)引用一共有三種實現(xiàn)的方式。
- 使用__weak ClassName
- (void)blockCircularReference
{
__weak MPBlockViewController *weakSelf = self;
self.circleBlock = ^(NSInteger number) {
[weakSelf autoParamterTest];
};
}
- 使用__weak typeof(self)
- (void)blockCircularReference
{
__weak typeof (self) weakSelf = self;
self.circleBlock = ^(NSInteger number) {
[weakSelf autoParamterTest];
};
}
- 使用Reactive Cocoa中的@weakify和@strongify
- (void)blockCircularReference
{
@weakify(self);
self.circleBlock = ^(NSInteger number) {
@strongify(self);
[self autoParamterTest];
};
}
@weakify, @strongify的具體使用可以看這里
__block
在MRC下氛堕,可以直接使用__block進行修飾馏臭。
也可以先用__block修飾,然后在block方法中使用完將其設為nil讼稚,但是要注意就是block必須要被調用一次括儒。
- (void)blockCircularReference
{
__block MPBlockViewController *blockSelf = self;
self.circleBlock = ^(NSInteger number) {
[blockSelf autoParamterTest];
blockSelf = nil; //必須設為nil
};
self.circleBlock(10); //必須至少調用一次
}
將self作為參數(shù)傳遞
也可以直接將self作為一個參數(shù)傳遞到block中。
- (void)blockCircularReference
{
self.circleBlock = ^(MPBlockViewController *vc) {
[vc autoParamterTest];
};
}
Block的實現(xiàn)
block實際上是用C語言源碼來處理的锐想,含有block的源碼首先被轉換成C語言編譯器能夠處理的源碼帮寻,再作為C進行編譯。
Clang
使用LLVM編譯器的clang
可以將OC的代碼翻譯成C++的源代碼赠摇,說是C++的代碼固逗,但是實際上也就是C語言的源代碼。
使用的方式就是打開Terminal藕帜,cd到源代碼文件目錄烫罩,輸入:
clang -rewrite-objc 源代碼文件名
比如這樣一段代碼(這里沒有引用其他OC的框架,因為引入之后clang出來的cpp文件會巨大洽故,有好幾千行):
#include <stdio.h>
int main() {
void (^ blk)(void) = ^{printf("Block\n");};
blk();
return 0;
}
這段簡單的block代碼clang之后就會變成如下源碼(這里刪除了部分代碼贝攒,只顯示了重要的部分):
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Block\n");}
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() {
void (* blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
return 0;
}
乍一看,我天时甚,這都是什么鬼啊 = =隘弊。 沒事我們一部分一部分來看。
__cself
這里邊的參數(shù)__cself
就相當于C++中指向自身的變量this
撞秋,在OC中就是self
长捧,即參數(shù)__cself
就是指向block值的變量。
__block_impl
__block_impl
是我們要介紹的第一個block中的成員變量吻贿,他是一個結構體串结,其結構如下:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
isa指針,所有對象都有改指針,用于實現(xiàn)對象相關的功能肌割。
Flags卧蜓,用于按bit位表示一些block的附加信息。
Reserved把敞,保留變量弥奸。
FuncPtr,函數(shù)指針奋早,指向block要執(zhí)行的函數(shù)盛霎,即__main_block_func_0。
__main_block_desc_0
__main_block_desc_0
是我們要介紹的第二個block中的成員變量耽装,也是一個結構體愤炸,其結構如下:
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)};
reserved,結構體信息保留字段掉奄。
Block_size规个,block的大小。
初始化__main_block_func_0
另外一部分就是__main_block_func_0的初始化姓建,用到的就是之前介紹的兩個結構體诞仓。
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
實現(xiàn)__main_block_func_0
這里主要就是我們在block中要實現(xiàn)的代碼。
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Block\n");
}
實現(xiàn)main函數(shù)
另外main函數(shù)的源碼在這里速兔。
int main() {
void (* blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
return 0;
}
截獲自動變量
看完了block的基本實現(xiàn)方式墅拭,那我們再看看他是如何截獲自動變量呢?
我們先定義一個number
:
#include <stdio.h>
int main() {
int number = 10;
void (^ blk)(void) = ^{printf("%d",number);};
blk();
return 0;
}
這時候clang之后發(fā)現(xiàn):
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int number; //number被直接加入了__main_block_impl_0結構體中
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _number, int flags=0) : number(_number) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int number = __cself->number; // bound by copy
printf("%d",number);}
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 number = 10;
void (* blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, number));
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
return 0;
}
我們看到number
被直接加到了__main_block_impl_0
結構體中憨栽。
__block
這時候我們再用__block來修飾一下number看看:
#include <stdio.h>
int main() {
__block int number = 10;
void (^ blk)(void) = ^{printf("%d",number);};
blk();
return 0;
}
clang之后發(fā)現(xiàn):
struct __Block_byref_number_0 {
void *__isa;
__Block_byref_number_0 *__forwarding;
int __flags;
int __size;
int number;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_number_0 *number; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_number_0 *_number, int flags=0) : number(_number->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_number_0 *number = __cself->number; // bound by ref
printf("%d",(number->__forwarding->number));}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->number, (void*)src->number, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->number, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main() {
__attribute__((__blocks__(byref))) __Block_byref_number_0 number = {(void*)0,(__Block_byref_number_0 *)&number, 0, sizeof(__Block_byref_number_0), 10};
void (* blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_number_0 *)&number, 570425344));
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
return 0;
}
我們只是加了一個__block
帜矾,結果代碼一下子增加了巨多!
這時候仔細看代碼屑柔,就能發(fā)現(xiàn)多了一個段代碼:
__Block_byref_number_0 number = {
(void*)0,(__Block_byref_number_0 *)&number,
0,
sizeof(__Block_byref_number_0),
10};
找到這個結構體的聲明:
struct __Block_byref_number_0 {
void *__isa;
__Block_byref_number_0 *__forwarding;
int __flags;
int __size;
int number;
};
那如果這時候我們給number
賦一個新的值會怎么樣呢?
#include <stdio.h>
int main() {
__block int number = 10;
void (^ blk)(void) = ^{
number = 20;
printf("%d",number);
};
blk();
return 0;
}
clang后發(fā)現(xiàn)多了這里變化(就不貼全部代碼了):
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_number_0 *number = __cself->number; // bound by ref
(number->__forwarding->number) = 20;
printf("%d",(number->__forwarding->number));
}
我們看到在向__block變量賦值時珍剑,block的__main_block_impl_0結構體實例持有指向__block變量的__Block_byref_number_0結構體實例的指針掸宛。
__Block_byref_number_0結構體的實例的成員變量__forwarding持有指向該實例自身的指針。并通過成員變量__forwarding訪問成員變量val招拙。
那么__forwarding又是什么呢唧瘾?別急,后邊我們再說他别凤。
Block的存儲域
在__main_block_func_0的初始化時饰序,我們看到了有一行代碼是:
impl.isa = &_NSConcreteStackBlock;
從名字應該可以判斷出來,這行代碼的意思是這個block是存儲在棧上的规哪。
那么除了棧求豫,block還存儲在哪些地方呢?
| 類 | 設置對象的存儲域 |
| ------| ------ |
| _NSConcreteStackBlock | 棧塊 |
| _NSConcreteGlobalBlock | 全局塊 |
| _NSConcreteMallocBlock | 堆塊 |
從名字就能看出來:
棧塊存在棧內存中,超出作用域后就會馬上銷毀蝠嘉。
全局塊在全局內存中最疆,和全局變量一樣。
堆塊存在堆內存中蚤告,是一個帶有引用計數(shù)的對象努酸,需要自行管理內存。
那么我們怎么樣能夠知道block是保存在哪里呢杜恰?
全局塊
一般情況下获诈,當滿足以下情況時,block為_NSConcreteGlobalBlock類對象心褐,也就是放在全局數(shù)據(jù)區(qū)舔涎。
1.記錄全局變量的地方有block語法時。
2.block語法的表達式中不使用應截獲的自動變量時檬寂。
棧塊
理論上,除了在全局塊條件之外的情況下桶至,block都為_NSConcreteStackBlock類對象昼伴,也就是設置在棧區(qū)。
堆塊
那么如果這樣說起來镣屹,豈不是沒有block會在堆上了么圃郊?
這就要說到一個問題,就是ARC和MRC下block的不同情況女蜈,MRC下訪問外界變量的block默認就是存儲在棧中了持舆,但是ARC下,block會自動被從棧區(qū)拷貝到堆區(qū)伪窖,然后自動釋放逸寓。
那為什么ARC下,訪問外部變量的block會自動從棧區(qū)拷貝到堆區(qū)呢覆山?
block中的copy
在棧上的block竹伸,如果所在的作用域結束,block和block中的__block變量都會被廢棄掉簇宽。
所以勋篓,我們需要將Block復制到堆中,延長其生命周期魏割,這樣即使是block所在的作用域結束譬嚣,block還是可以在堆中繼續(xù)存在。
開啟了ARC時钞它,大多數(shù)情況下編譯器會恰當?shù)呐袛嗍欠裼行枰獙lock從棧復制到堆拜银,如果有殊鞭,自動生成將block從棧上復制到堆上的代碼,block復制執(zhí)行的是copy實例方法盐股,只要調用了copy方法钱豁,棧塊就會變成堆塊,一般在如下情況時疯汁,block會自動copy到堆上牲尺。
1.調用Block的copy方法。
2.將Block作為函數(shù)返回值時(MRC下需要手動調用copy幌蚊,否則無效)。
3.將Block賦值給__strong修改變量時(MRC時無效)。
4.向Cocoa框架中含有usingBlock的方法或者GCD的API傳遞Block參數(shù)時。
int count = 0;
blk_t blk = ^(){
NSLog(@"In Stack:%d", count);
};
NSLog(@"blk's Class:%@", [blk class]);//打郁镎邸:blk's Class:__NSMallocBlock__
NSLog(@"Global Block:%@", [^{NSLog(@"Global Block");} class]);//打影盼觥:Global Block:__NSGlobalBlock__
NSLog(@"Copy Block:%@", [[^{NSLog(@"Copy Block:%d",count);} copy] class]);//打印:Copy Block:__NSMallocBlock__
NSLog(@"Stack Block:%@", [^{NSLog(@"Stack Block:%d",count);} class]);//打永颂:Stack Block:__NSStackBlock__
block的復制操作執(zhí)行的是copy實例方法,不同類型的block使用copy方法的效果如下:
| block 的類 | 副本源的配置存儲域 | 復制效果 |
| ------| ------ | ----- |
| _NSConcreteStackBlock | 棧塊 | 從棧復制到堆 |
| _NSConcreteGlobalBlock | 全局塊 | 什么也不做 |
| _NSConcreteMallocBlock | 堆塊 | 引用計數(shù)增加 |
不管block配置在何處不从,用copy方法復制都不會引起任何問題寝优,在不確定是調用copy即可。
blk = [[[[blk copy] copy] copy] copy];
// 經(jīng)過多次復制扔役,變量blk仍然持有Block的強引用坯钦,該Block不會被廢棄。
__block變量的存儲域
之前只說到了block治唤,那__block變量又會有什么影響呢棒动?使用__block變量的block從棧復制到堆上時,__block變量也會受到影響宾添。
| __block變量的配置存儲域 | block從棧復制到堆時的影響 |
| ------| ------ |
| 棧 | 從棧復制到堆并被block持有 |
| 堆 | 被block持有 |
那么棧上的__block變量復制到堆上之后船惨,block是可以同時訪問棧上的__block變量和堆上的__block變量柜裸,但是具體訪問時到底是訪問棧上的還是堆上的呢?這時候還記得我們之前說的****__forwarding****變量么粱锐?
通過__forwarding, 無論是在block中還是 block外訪問__block變量, 也不管該變量在棧上或堆上, 都能順利地訪問同一個__block變量疙挺。
Block的實踐
說了這么多,我們來看看block在實際開發(fā)中比較常見的使用方法吧怜浅。
一般情況下铐然,block會用來作為方法回調的功能,和代理的方法比較相似海雪,處理一些比較耗時的操作比如網(wǎng)絡數(shù)據(jù)的下載锦爵,在下載好之后直接調用block回調,返回正確或錯誤的信息奥裸。
block會使得代碼結構緊湊险掀,邏輯清晰,接下來我們就看一個簡單的??:
首先我們先聲明一個typedef block
typedef void(^MPBlockDownloadHandler)(NSData * receiveData, NSError * error);
然后在下載函數(shù)中傳入block作為參數(shù)湾宙,并在下載結束后調用block樟氢。
- (void)downloadWithURL: (NSString *)URL parameters: (NSDictionary *)parameters handler: (MPBlockDownloadHandler)handler
{
NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:URL]];
NSURLSession * session = [NSURLSession sharedSession];
//執(zhí)行請求任務
NSURLSessionDataTask * task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (handler) {
dispatch_async(dispatch_get_main_queue(), ^{
handler(data,error);
});
}
}];
[task resume];
}
最后調用這個函數(shù):
[self downloadWithURL:@"https://www.vactualpapers.com/web/wallpapers/sights-and-scenes-of-beautiful-singapore-hd-wallpaper-29/thumbnail/lg.jpg" parameters:nil handler:^(NSData *receiveData, NSError *error) {
if (error) {
NSLog(@"下載失敗:%@",error);
}else {
NSLog(@"下載成功侠鳄,%@",receiveData);
}
}];
這樣埠啃,一個簡單的利用block實現(xiàn)網(wǎng)絡加載回調的功能就做好了。
最后
好了伟恶,這就是block的全部內容了碴开。說是寫消息傳遞,好像越來越跑偏了博秫。潦牛。。
另外以上內容僅供個人學習使用挡育,大部分內容來自《Objective-C高級編程 iOS與OS X多線程和內存管理》巴碗。如果有什么地方不對,還請大佬們多多指教即寒。
參考文檔
《Objective-C 高級編程》干貨三部曲(二):Blocks篇