iOS開發(fā)-消息傳遞方式-Block

說過了target-action

說過了KVO

說過了NotificationCenter

這次我們來說一個一對一的消息傳遞方式师溅,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方法浅妆,所以是沒辦法修改外部變量的值的。

block-capture-1.jpg

比如:


- (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外部的變量值贪庙。

block-capture-2.jpg

- (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)的方式。

  1. 使用__weak ClassName

- (void)blockCircularReference

{

 __weak MPBlockViewController *weakSelf = self;

 self.circleBlock = ^(NSInteger number) {

 [weakSelf autoParamterTest];

 };

}

  1. 使用__weak typeof(self)

- (void)blockCircularReference

{

 __weak typeof (self) weakSelf = self;

 self.circleBlock = ^(NSInteger number) {

 [weakSelf autoParamterTest];

 };

}

  1. 使用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 | 堆塊 |

block_Storage_Domain.png

從名字就能看出來:

  • 棧塊存在棧內存中,超出作用域后就會馬上銷毀蝠嘉。

  • 全局塊在全局內存中最疆,和全局變量一樣。

  • 堆塊存在堆內存中蚤告,是一個帶有引用計數(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_copy_1.png

所以勋篓,我們需要將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ù)時。

block_copy_2.png

 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****變量么粱锐?

block_copy_3.png

通過__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 高級編程

iOS開發(fā)-由淺至深學習block

《Objective-C 高級編程》干貨三部曲(二):Blocks篇

談Objective-C block的實現(xiàn)

譯 Block 小測驗

iOS Block用法和實現(xiàn)原理

iOS Block 詳解

block_copy_2.png

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末橡淆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子母赵,更是在濱河造成了極大的恐慌逸爵,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凹嘲,死亡現(xiàn)場離奇詭異痊银,居然都是意外死亡,警方通過查閱死者的電腦和手機施绎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門溯革,熙熙樓的掌柜王于貴愁眉苦臉地迎上來致稀,“玉大人俱尼,你說我怎么就攤上這事遇八。” “怎么了货矮?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵囚玫,是天一觀的道長读规。 經(jīng)常有香客問我束亏,道長,這世上最難降的妖魔是什么定铜? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任宿稀,我火速辦了婚禮赖捌,結果婚禮上越庇,老公的妹妹穿的比我還像新娘。我一直安慰自己涩惑,他們只是感情好桑驱,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赊级,像睡著了一般理逊。 火紅的嫁衣襯著肌膚如雪盒揉。 梳的紋絲不亂的頭發(fā)上刚盈,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天扁掸,我揣著相機與錄音,去河邊找鬼锈麸。 笑死牺蹄,一個胖子當著我的面吹牛沙兰,可吹牛的內容都是我干的。 我是一名探鬼主播舀奶,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼育勺,長吁一口氣:“原來是場噩夢啊……” “哼罗岖!你這毒婦竟也來了桑包?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤烧颖,失蹤者是張志新(化名)和其女友劉穎蒜焊,沒想到半個月后泳梆,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體榜掌,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡憎账,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年胞皱,在試婚紗的時候發(fā)現(xiàn)自己被綠了反砌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宴树。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡酒贬,死狀恐怖,靈堂內的尸體忽然破棺而出蠢莺,到底是詐尸還是另有隱情躏将,我是刑警寧澤埠况,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布夺衍,位于F島的核電站喜命,受9級特大地震影響,放射性物質發(fā)生泄漏赎瞎。R本人自食惡果不足惜颊咬,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一喳篇、第九天 我趴在偏房一處隱蔽的房頂上張望麸澜。 院中可真熱鬧炊邦,春花似錦馁害、人聲如沸蜗细。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缎岗。三九已至,卻和暖如春眷细,著一層夾襖步出監(jiān)牢的瞬間溪椎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留轧膘,地道東北人扶供。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像闽晦,于是被迫代替她去往敵國和親仙蛉。 傳聞我的和親對象是個殘疾皇子碱蒙,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內容