block,注冊監聽,委托協議和KVO

總結

IOS中關于信息傳遞過程中經常會使用block,委托代理,NSNotification,KVO進行調用,但是查找資料后有些資料不是很全。

四者總體比較然后進行一一詳解進行講解:

block:一對一進行通信,比起其他三者更加簡潔,但是事件比較多時可以使用delegate。
委托代理:和block一樣是一對一,我們使用時要先進行協議方法然后實現協議代理,如果需要通信就需要實現代理。
NSNotification:在進行注冊監聽時,可以進行一對多的情況,一個進行注冊可以多種情況下進行監聽。
KVO:就是我們所說的鍵值監聽模式,其主要是在KVC基礎上完后才能。

以前讀過一個砍柴的故事,如果我們想要追求搞得效率就要對所用到的工具了解,下面我們就開始磨我們搜中的刀。

block的來龍去脈:

block是IOS SDK 4.0中引入的,block在IOS中實際就是一個代碼塊,有點像C++中內聯函數inline有點相似,最讓人驚奇的是我們還可以向其傳遞參數。閑來沒事想要知道block在C++中的具體實現就是用Clang編輯^{printf"Hello, World!"}(),此時我們在block僅僅是輸出一個語句。
//hello.c是我們把想要編輯的內容放置的文件名稱

block在沒有傳入參數情況

$ clang -rewrite-objc hello.c

<p>在編譯后再文件后會生成 hello.cpp文件</p>
<pre>
<code>
struct __mian_block_impl_0

{
struct __block_impl impl;
struct __mian_block_desc_0* Desc;

__mian_block_impl_0(void *fp, struct __mian_block_desc_0 *desc, int flags=0)

{ impl.isa = &_NSConcreteStackBlock;

      impl.Flags = flags;
      impl.FuncPtr = fp;
      Desc = desc;

}

};

static void __mian_block_func_0(struct __mian_block_impl_0 *__cself)

{
printf("Hello, World!\n");

}

static struct __mian_block_desc_0 {

size_t reserved;
size_t Block_size;

}

__mian_block_desc_0_DATA = { 0, sizeof(struct __mian_block_impl_0)
};

int mian(){

 ((void (*)())&__mian_block_impl_0((void *)

__mian_block_func_0, &__mian_block_desc_0_DATA)) ();

return 0;

}

static struct IMAGE_INFO
{ unsigned version; unsigned flag; }

_OBJC_IMAGE_INFO = { 0, 2 };
</code>
</pre>
上面的代碼既是我們通過編譯截取的hello.cpp的具體代碼,根據上面代碼我們可以看到block實際是struct結構。看出_mian_block_impl_0 其中傳入其中也是block_impl&mian_block_desc_0兩個block,下面是_mian_block_impl_0的構造函數。

block在有傳入參數情況

clang -rewrite-objc hello1.c

同樣在我們編譯過后會生成hello.cpp

<pre>
<code>
struct __mian_block_impl_0 {

struct __block_impl impl;

struct __mian_block_desc_0* Desc;

float perperson;
__mian_block_impl_0(void *fp, struct __mian_block_desc_0

*desc, float _perperson, int flags=0) : perperson(_perperson)
{

impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;

}
};

static float __mian_block_func_0(struct __mian_block_impl_0 *__cself,

int totalNum) {

float perperson = __cself->perperson; // bound by copy

    return perperson * totalNum;
}

static struct __mian_block_desc_0 {

size_t reserved;
size_t Block_size;
}

__mian_block_desc_0_DATA = { 0, sizeof(struct __mian_block_impl_0)};

int mian(){

float perperson = 34.5;
float (*sunClassFee)(int) = ((float (*)(int))

&__mian_block_impl_0((void *)__mian_block_func_0, &__mian_block_desc_0_DATA,

perperson));

return 0;

}

static struct IMAGE_INFO { unsigned version; unsigned flag; }

_OBJC_IMAGE_INFO = { 0, 2 };

</code>
</pre>
可以看出在block中使用外部變量perperson時,block初始化時這樣的:
<pre>
是在__mian_block_impl_0進行初始化perperson,在block構造時作為block的其中
一個成員初始。在訪問時通過__cself->perperson指針進行訪問。
</pre>

block在有傳入參數情況

clang -rewrite-objc hello2.c

<pre>
<code>
struct __Block_byref_perperson_0 {

void *__isa;

__Block_byref_perperson_0 *__forwarding;
int __flags;

int __size;
float perperson;

};

struct __mian_block_impl_0 {

struct __block_impl impl;
struct __mian_block_desc_0* Desc;

__Block_byref_perperson_0 *perperson; // by ref
__mian_block_impl_0(void *fp, struct __mian_block_desc_0 *desc,

__Block_byref_perperson_0 *_perperson, int flags=0) :

perperson(_perperson->__forwarding) {

impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;

}
};

static float __mian_block_func_0(struct __mian_block_impl_0 *__cself,

int totalNum) {

__Block_byref_perperson_0 *perperson = __cself->perperson; // bound by ref
 (perperson->__forwarding->perperson) = (perperson->__forwarding->

perperson) + 6;

    return (perperson->__forwarding->perperson) * totalNum;
}

static void __mian_block_copy_0(struct __mian_block_impl_0*dst,

struct __mian_block_impl_0*src)

{_Block_object_assign((void*)&dst->perperson,

(void)src->perperson, 8/BLOCK_FIELD_IS_BYREF*/);}

static void __mian_block_dispose_0(struct __mian_block_impl_0*src) {

_Block_object_dispose((void)src->perperson, 8/BLOCK_FIELD_IS_BYREF*/);

}

static struct __mian_block_desc_0 {
size_t reserved;
size_t Block_size;

void (copy)(struct __mian_block_impl_0, struct __mian_block_impl_0*);

void (dispose)(struct __mian_block_impl_0);
} __mian_block_desc_0_DATA = {

0, sizeof(struct __mian_block_impl_0), __mian_block_copy_0,

__mian_block_dispose_0};
int mian(){

__attribute__((__blocks__(byref))) __Block_byref_perperson_0 perperson = {

(void*)0,(__Block_byref_perperson_0 *)&perperson, 0,

sizeof(__Block_byref_perperson_0), 34.5
};

float (*sunClassFee)(int) = ((float (*)(int))&__mian_block_impl_0((void *)

__mian_block_func_0, &__mian_block_desc_0_DATA,

(__Block_byref_perperson_0 *)&perperson, 570425344));
return 0;

}

static struct IMAGE_INFO { unsigned version; unsigned flag; }

_OBJC_IMAGE_INFO = { 0, 2 };
</code>
</pre>
我們在對于block外的perperson計算重新復制,和上面在block僅僅在block中使用外面的參數。兩者經過對比可以看出:在block中對于外部值進行復制,會出現下面的情況。使用clang對于block進行編碼,可以看書對于perperson進行_block的修飾后編碼生成Block_byref_perperson_0的struct結構體,其中__Block_byref_perperson_0的指針指向在block中perperson的地址。
<pre>
struct __Block_byref_perperson_0 {
void *__isa;
__Block_byref_perperson_0 *__forwarding; int __flags;
int __size; float perperson;
};
</pre>

block的使用方法~傳入參數:

<pre>
<code>
int (^isInputEven)(int) = ^(int n){

    if (n>1) {
           return n*isInputEven(n-1);
        }else{
           return 1;
      }

};
</code>
</pre>
上面我們定義一個block代碼段,代碼的實現功能是利用遞歸計算n的基乘。代碼可以作為整個代碼塊進行調用,而且在調用過程中我們只需isInputEven()函數名和傳入參數即可實現。

block的使用方法~使用外部參數參數:

<pre>
<code>
float perperson = 34.5;

float (^sunClassFee)(int) = ^(int totalNum){
    return perperson * totalNum;

};
</code>
</pre>
使用外部參數perperson,讓我們感覺block可以想一個類一樣使用socpe里面的變量目前無法做到。
<pre>
主要原因是因為:在block的編譯過程中我們看到,在block中進行訪問其中元素時需要是
block默認構造函數對于block其中的元素訪問。
</pre>

block的使用方法~使用外部參數參數:

<pre>
<code>
__block float perperson = 34.5;

float (^sunClassFee)(int) = ^(int totalNum){
    perperson = perperson + 6;
    return perperson * totalNum;

};
</code>
</pre>

block的使用方法~在(UIView)的動畫中使用:

<pre>
<code>
[UIView animateWithDuration:3.0f animations:^{

    view.alpha = 0;

}];
</code>
</pre>
IOS的開發到今天差不多已經有7年之久,block在開發中使用廣度越來越廣泛:
<ul>
<li>枚舉--來過去對象。例如:NSDictionary的枚舉過程</li>
<li>UIView--對于UIView的動畫設置,后面有機會會對動畫專門講述...</li>
<li>通知--在本文講述的就是在調用過程中與其他通知不同點所在</li>
<li>完成處理--在相應程序結束后,需要對于程序結果的處理。例如:AFNetWork在訪問網絡后,對于成功和失敗進行處理</li>
<li>GCD--在GCD的過程中都是含有block使用的方法</li>
<li>排序--平時我們使用的一些簡單算法均可以block進行相關處理</li>
</ul>

委托代理詳解

在發C++過程中經常見到多組繼承,但是在IOS開發過程中只能進行單繼承,很多方法我們需要實現接口的形式。正如我們在使用UITableView時,經常會用到UITableViewDataSource和UITableViewDelegate。
如果要使用使用委托代理,需要知道協議和委托兩者關系。

協議

協議一般分為兩種:

@ required: //是我們在繼承過程中必須實現的
@optional : //在實現接口中需要進行選擇性實現的方式

委托

常見的一種設計模式,身為老板一般負責管理員工、打電話、發薪水,這樣由于公司業務發展就請一個秘書負責:打電話和發薪水。

首先進行協議提取(需要員工所得事情):(boss.h文件中聲明)

<pre>
@protocol protocol <NSObject>
-(void)payoff;
-(void)tel;
@end
@interface boss : NSObject
@property(nonatomic,strong) id<protocol> delegate;
-(void)manage;
-(void)payoff;
-(void)tel;
@end
</pre>

對于老板在具體工作的實現:(boss.m文件中實現)

<pre>
@implementation boss
-(void)manage{
NSLog(@"boss-->manage");
}
-(void)payoff{
[self.delegate payoff];
}
-(void)tel{
[self.delegate tel];
}
@end
</pre>

委托秘書所要做的事物:(在sec.m的文件中實現完成boss的代理)

<pre>
@interface Sec ()<protocol>
@end
@implementation Sec
-(void)payoff{
NSLog(@"sec-->payoff");
}
-(void)tel{
NSLog(@"sec-->tel");
}
</pre>

注冊監聽小試

NSNotificationCenter就相當于廣播一樣,可以對對象進行一次注冊然后有多個監聽。就像我們知道如果有注冊的話,就需要我們對于對象進行釋放(remove)。

[[NSNotificationCenter defaultCenter] postNotificationName:@"clickbt" object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(click) name:@"clickbt" object:nil];

在上面的代碼可以看出我們在使用注冊監聽時可以進行相關內容傳遞,需要傳遞的內容放在object(為id類型)。
在remove監聽的方法,經過查找資料:
<pre>
<code>
-(void)viewWillAppear:(BOOL)animated
{

[super viewWillAppear:animated];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"clickbt" object:nil];
}

-(void)viewWillDisappear:(BOOL)animated
{

[super viewWillDisappear:animated];

[[NSNotificationCenter defaultCenter] removeObserver:self name:@"clickbt" object:nil];
}
</code>
</pre>

KVO簡史

當我們說起KVO(key-value-observe)時不免想起他的孿生大哥KVC(key-value-coding)。KVO是觀察者模式的繼承者,基于鍵值變化監聽者,基于KVC基礎完成之一。

下列對于界面的UILabel進行監聽:

<pre>
[self.label addObserver:self forKeyPath:@"narcotics" options:
NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:nil];
</pre>

一旦UILabel發生變化就會調用方法:

<pre>
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context{
//一旦有所變化調用方法
}
</pre>
附帶:

KVC:通過設置key值,進行標記。然后通過key值找到進行重新設置,說的通俗點點就是鍵值對。類似NSDictionary相似。

以前經常看些大牛寫的博客,在看博客過程中也學到很多知識。心向往之,開始動手寫關于自己在IOS成長路上見證。

簡述block和delegation使用場景的比較

我們知道blockdelegation是關于通信方面,在開發的過程中我們也經常會使用兩者。面對具體情況我們需要怎么樣做出選著呢?

1.當在一個方法的參數中需要多個對象時使用delegation

我們在開發過程中經常使用UITableView,這時我們就要實現接口協議UITableViewDataSource, UITableViewDelegate兩者是我們在使用UITableView的相關數據協議和相關協議。從tableView角度出發:實現展示需要數據、對一些展示過程的控制等,這些可以使用block來進行實現。

void (^ showTableView)(NSData *data, NSInteger number, CGFloat heightForRowAtIndexPath);

上面我們定義告訴過程需要參數在NSIndexPath,所以第三個參數應該是 block形式。我們上面知道block在編譯過程會被編譯為struct結構,在相互嵌套過程中使用delegation更加方便。

2. 一個對象只能有一個delegation

由于一個對象只能有一個delegate,而且它只能與這個delegate通信。讓我們看看CLLocationManager 這個類,當發現地理位置后,location manager 只會通知一個對象(有且只有一個)。當然,如果我們需要更多的對象去知道這個更新,我們最好創建其他的location manager。

這里有的人可能想到,如果CLLocationManager是個單例呢?如果我們不能創建CLLocationManager的其他實例,就必須不斷地切換delegate指針到需要地理數據的對象上(或者創建一個只有你理解的精密的廣播系統)。因此,這樣看起來,delegatetion在單例上沒有多大意義。

關于這點,最好的印證例子就是UIAccelerometer。在早期版本的iOS中,單例的 accelerometer 實例有一個delegate,導致我們必須偶爾切換一下。這個愚蠢的問題在之后的IOS版本被修改了,現在,任意一個對象都可以訪問CMMotionManager block,而不需要阻止其他的對象來接收更新。

因此,我們可以得出另一個結論:“如果一個對象是單例,不要使用delegation”。

3. 一般delegation都有自己的而返回值

還是拿上面的例子說明:
<pre><code>- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
</code>
</pre>上面是UITableViewDataSource協議中必須實現協議,可看出是使用返回值。而我們也可以看其他的一些協議表示情況,一般均是需要返回值的。

4.delegation偏向于使用的過程 VS block偏向于使用結果

如果查看NSURLConnectionDelegate 以及 NSURLConnectionDataDelegate,我們在可以protocol中看到這樣的消息:我將要做什么(如: willSendRequest,將要發送請求)、到目前為止我知道的信息(如:canAuthenticateAgainstProtectionSpace)、我已經完成這些啦( didReceiveResponse,收到請求的回復,即完成請求)。這些消息組成一個流程,而那些對流程感興趣的delegate將會在每一步得到相應的通知。

當我們觀察handler和完整的方法時,我們發現一個block包含一個響應對象和一個錯誤對象。顯然這里沒有任何有關“我在哪里,我正在做什么的”的交互。

因此我們可以這樣認為,delegate的回調更多的面向過程,而block則是面向結果的。如果你需要得到一條多步進程的通知,你應該使用delegation。而當你只是希望得到你請求的信息(或者獲取信息時的錯誤提示),你應該使用block。(如果你結合之前的3個結論,你會發現delegate可以在所有事件中維持state,而多個獨立的block確不能)。
</br>

參考:

<a herf ="http://www.cocoachina.com/ios/20150925/13525.html">開發該選擇Blocks還是Delegates</a>

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,247評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,520評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,362評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,805評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,541評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,896評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,887評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,062評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,608評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,356評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,555評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,077評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,769評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,175評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,489評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,289評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,516評論 2 379

推薦閱讀更多精彩內容