首先兩者作用是一樣的,都是進行單一回調。不通的是,delegate是個對象,然后用過一個對象自己調用代理協議函數來完成整個流程。block是傳遞一個函數指針,利用函數指針執行來進行回調。還有在內存管理上需要注意,delegate不需要保存引用。block對引用數據有copy的處理。1.block類型-存儲代碼塊的類型
在異步編程時常需要進行函數回調,在C#中會用匿名委托或者lambda表達式講一個操作作為參數進行傳遞.ObjC中是使用對于閉包的實現,在塊狀中我們可以持有或引用局部變量. 同時利用Block可以將一個操作作為參數進行傳遞;
blcok用法:
定義:返回值類型 ( ^變量名 ) ( 形參類型 );賦值:變量名=^(形參){代碼塊+形參變量};使用:變量(實參);例:
int (^myBlcok)(int ,int)=^(int m,int n){ return m+n; }; //無參數時大括號前()可省略 myBlock(10,5); //調用塊,省略了接受塊返回值;總結:經過簡單了解C與OC;發現從最小的一個變量到表達式再到一個函數,其實只起兩點作用: 值(返回值) 與 功能(行為,方法,作用).所以說一行代碼,按它是使用了值 還是 功能來解讀比較容易理解.
Block做使用場景:
如果回調方法比較少,1~2,最好不要超過3個,這個時候使用block比較合適如果回調方法非常多,同時又不用每一個方法都必須實現,這個時候用delegate會比較方便!block傳值的循環引用問題:
只有當block直接或間接的被self持有時,在block使用self時才需要替換為weak self。如果在 Block 內需要多次 訪問 self,則需要使用 strongSelf。
__weak __typeof__(self) weakSelf = self; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ __strong __typeof(self) strongSelf = weakSelf; [strongSelf doSomething]; [strongSelf doOtherThing]; });typedef block格式
類似函數指針,直接在定義格式之前加 typedef關鍵字,之后變量名就是類型的別名了.
typedef viod (^別名)(形參);一般以后需要使用block作為函數方法的參數時,為方便最好用別名.而在block作用返回值時,一定需要別名,因為編譯器不能識別做此時類型做何種解釋.延伸:經過測試,block在編譯時按代碼順序,而運行時按調用順序(變量作用域)使用例子:KCButton.h
#import <Foundation/Foundation.h>@class KCButton;typedef void(^KCButtonClick)(KCButton *);@interface KCButton : NSObject#pragma mark - 屬性@property (nonatomic,copy) KCButtonClick onClick;#pragma mark 點擊方法-(void)click;@endKCButton.m
#import "KCButton.h"@implementation KCButton-(void)click{ NSLog(@"Invoke KCButton's click method."); if (_onClick) { _onClick(self); }}@endmain.m
KCButton *button=[[KCButton alloc]init]; button.onClick=^(KCButton *btn){ NSLog(@"Invoke onClick method.The button is:%@.",btn); }; [button click]; /*結果: Invoke KCButton's click method. Invoke onClick method.The button is:<KCButton: 0x1006011f0>. */block訪問外部變量
block內部可以訪問外部局部變量,但是此時是const copy方式,地址不同,相當于值傳遞,只讀的.如果外部定義時加前綴__block時,內部可改變外部局變值.block內部如果創建了和外部同名的變量,會屏蔽外部作用域.此時內部的變量也存在棧區;原因:block本質是代碼塊,ARC下創建的時候在堆區,此時代碼只是單純儲存,沒有功能;當調用的時候,相當于代碼增加到main中,這樣代碼塊中創建的變量就跟正常的一樣; (block調用完成內部變量即釋放,而堆區的只在釋放block時一起釋放).如果是靜態變量(static修飾局變,生命周期延長,存儲在數據區(同初始化的全局))和全局變量.地址傳遞.此時block存儲在全局區.常量字符串@"abc",加__block會引用常量變量(如:a變量,a = @"abc",內部可以任意修改a 指向的內容)的地址。不加block就是@"abc"本身地址,不可變;三種類型block
根據block在內存中的位置"NSGlobaBlock"類似函數,存于代碼區--全局block"NNStackBlock"棧區,函數返回后的Block--棧"NSMallocBlock"堆block--堆block內沒有使用外部變量或是只使用了全局/靜態變量時.存于全局代碼區,為全局block;---(ARC和MRC下一致)當使用外部變量時MRC下,block代碼存于棧區;如果此外部變量A存于棧區,那么A會被copy到block分配的棧區;如果A是存于堆區,那么A在block塊內與快外相同.ARC下,block代碼存于堆區.如果此外部變量A存于棧區,那么A會被copy到block分配的堆區;如果A是存于堆區,那么A在block塊內與快外相同.如果需要修改外部變量,需要在變量前面聲明__Block;當使用下劃線Block修飾外部變量時:MRC下,無論變量A存于棧還是堆區,A在block塊內與快外相同;ARC下,如果此外部變量A存于棧區,那么A會被轉移而不是復制到堆區;如果A是存于堆區,那么A在block塊內與快外相同.面試題:block的@property參數(內存管理參數)為什么要用copy:如果不用copy,此時不論ARC還是MRC都是棧Bolck,棧block會提前釋放,導致無法繼續使用;可以copy到堆區手動管理內存.(而字符串copy是防止字符串如果是非常量的,外部可變,造成非預估的結果;)
block在MRC下得內存隱患(NNStacKBlock)
Block_copy將block及內部變量拷貝到堆區.使用完畢用Blok_release(block變量)釋放此堆區空間;
block使用技巧
block結構快速顯示:inlineBlock...(也可右下角自定義快速顯示其他格式)Block作為方法參數時,最好把參數列表部分加上,這樣后面調用方法時,會自動有格式;做方法參數時,需要加上返回值類型;做返回值時,先定義別名,最后別忘記執行返回值;方法中,void(^)()表示block類型同int,做參和返回值;做實例變量@property (nonatomic, copy) void(^變量名)()get點語法獲取block類型實例變量時,自動執行,后面需加();2.protocl協議的概念及使用
在ObjC中使用@protocol定義一組方法規范. 實現此協議的類 也必須實現對應的方法. 面向對象的語言接口本身是對象行為描述的 協議規范, 也就是說ObjC中@protocol和其他語言的接口定義是類似的, 只是ObjC的@interface關鍵字已經用于定義類了, 因此不會像C# 和java中那樣使用interface來定義接口;
1.定義:
只聲明而不實現方法 ,而讓遵守此協議的類實現協議聲明的方法;在.h 中
@protocol 協議名 <系統協議>,<其他協議2>... //方法聲明列表 @end2.類遵守協議:
類聲明文件中:
@interface 類名:父類<協議1,協議2>@end3.實現協議:
只需在此類的.m文件中實現協議聲明方法即可(根據協議定義時@option的關鍵字,有些必須實現,有些選擇實現);
4.應用:
協議只聲明多個方法,不實現, 不能定義屬性;只要遵守協議,就可以擁有協議的所有方法聲明;一個類遵守協議而實現的方法,聲明和實現都會遺傳給子類,而沒有實現的方法聲明會遺傳給子類,即協議可以 繼承 ;協議和類可以多對多,而一個協議又可以繼承其他一個或多個協議.所有協議默認遵守NSObject的基協議,其中聲明了很多基本方法.協議中有兩個關鍵字:@required(默認)必須實現的. @optional 選擇實現;作用域類似@public等關鍵字作用域兩大作用
1. 做類型限制:程序員間交流
格式:id<協議名>obj;//表明 如果要給obj賦值,則賦值的對象必須遵守"協議"才可以賦值.不滿足會有警告;即id<協議名> obj=[類1 new],必須類1要遵守此協議;id 可換成類名,記得加*;引申的,在對象做實例變量時也可限制,例:@prperty Dog<協議> *dog2.代理設計模式使兩個不同意義的事物分離解耦.一種常見編程思想:有些事自己不好做,找代理做;傳入的對象,代替當前類完成摸個功能;原理:對象關聯關系的類型限制(A對象擁有遵守特定協議的對象B)白手套作為主子的實例變量;應用場合:
當A發生了一些行為(ˇ?ˇ) 想~告訴B或B想監聽對象A的一些行為 A無法處理某些行為的時候,讓B幫忙;思路:
先定義一個協議.定義代理類,遵守此協議.主類定義限定此協議類型的屬性.主類中要有行為 來觸發代理.設置代理.
用父類還是id<協議>
不使用父類的原因:如果抽象一個父類的話, 還是有局限性, 因為很多時候, 不同類是無法抽象出共同的父類的. , 不能多繼承3.Category
作用:在不修改原類的情況下擴展其他功能目的:對類方法進行歸類,便于分模塊開發大型類,團隊協作.聲明:@interface 類名(分類名)實現:@implementation 類名使用原類直接調用注意:1)分類不能擴展任何新的實例變量;
2)分類可以訪問原類的成員變量;(相當于原類方法調用) 3)如果分類中存在和原類同名方法,優先使用分類(功能更新); 4)如果多個分類都有同名方法,調用最后一個參與編譯的分類同名方法;在Compile Sources 文件編譯可以看到的的順序;分類的非正式協議:(面試題)
就是NSObject(和系統類框架)的類別(分類)即系統類的分類,增加了所有類的功能;類別接口中指定的方法可能會或者可能不會被框架類實際地實現,而是被子類重寫.
分類的延展(匿名分類)Extendsion
延展是分類一個特例,匿名.且新添加的方法一樣要予以實現
@interface MyClass () { float value; //可以新增實例變量 }-(void)setValue:(float)newValue;@end使用:
可以在原類.h直接聲明,原類.m中實現;不需要自己的.m文件;主要用于新增類的私有方法和私有變量.()
新聞熱點
疑難解答