block妻熊,注冊監(jiān)聽夸浅,委托協(xié)議和KVO

總結(jié)

IOS中關(guān)于信息傳遞過程中經(jīng)常會使用block,委托代理扔役,NSNotification帆喇,KVO進(jìn)行調(diào)用,但是查找資料后有些資料不是很全亿胸。

四者總體比較然后進(jìn)行一一詳解進(jìn)行講解:

block:一對一進(jìn)行通信坯钦,比起其他三者更加簡潔,但是事件比較多時可以使用delegate侈玄。
委托代理:和block一樣是一對一婉刀,我們使用時要先進(jìn)行協(xié)議方法然后實現(xiàn)協(xié)議代理,如果需要通信就需要實現(xiàn)代理序仙。
NSNotification:在進(jìn)行注冊監(jiān)聽時突颊,可以進(jìn)行一對多的情況,一個進(jìn)行注冊可以多種情況下進(jìn)行監(jiān)聽潘悼。
KVO:就是我們所說的鍵值監(jiān)聽模式律秃,其主要是在KVC基礎(chǔ)上完后才能。

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

block的來龍去脈:

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

block在沒有傳入?yún)?shù)情況

$ 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的具體代碼榄檬,根據(jù)上面代碼我們可以看到block實際是struct結(jié)構(gòu)∠瓮常看出_mian_block_impl_0 其中傳入其中也是block_impl&mian_block_desc_0兩個block鹿榜,下面是_mian_block_impl_0的構(gòu)造函數(shù)。

block在有傳入?yún)?shù)情況

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進(jìn)行初始化perperson舱殿,在block構(gòu)造時作為block的其中
一個成員初始。在訪問時通過__cself->perperson指針進(jìn)行訪問险掀。
</pre>

block在有傳入?yún)?shù)情況

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計算重新復(fù)制沪袭,和上面在block僅僅在block中使用外面的參數(shù)。兩者經(jīng)過對比可以看出:在block中對于外部值進(jìn)行復(fù)制樟氢,會出現(xiàn)下面的情況冈绊。使用clang對于block進(jìn)行編碼侠鳄,可以看書對于perperson進(jìn)行_block的修飾后編碼生成Block_byref_perperson_0的struct結(jié)構(gòu)體,其中__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的使用方法~傳入?yún)?shù):

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

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

};
</code>
</pre>
上面我們定義一個block代碼段伟恶,代碼的實現(xiàn)功能是利用遞歸計算n的基乘。代碼可以作為整個代碼塊進(jìn)行調(diào)用毅该,而且在調(diào)用過程中我們只需isInputEven()函數(shù)名和傳入?yún)?shù)即可實現(xiàn)知押。

block的使用方法~使用外部參數(shù)參數(shù):

<pre>
<code>
float perperson = 34.5;

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

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

block的使用方法~使用外部參數(shù)參數(shù):

<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的開發(fā)到今天差不多已經(jīng)有7年之久畏线,block在開發(fā)中使用廣度越來越廣泛:
<ul>
<li>枚舉--來過去對象静盅。例如:NSDictionary的枚舉過程</li>
<li>UIView--對于UIView的動畫設(shè)置,后面有機會會對動畫專門講述...</li>
<li>通知--在本文講述的就是在調(diào)用過程中與其他通知不同點所在</li>
<li>完成處理--在相應(yīng)程序結(jié)束后寝殴,需要對于程序結(jié)果的處理蒿叠。例如:AFNetWork在訪問網(wǎng)絡(luò)后,對于成功和失敗進(jìn)行處理</li>
<li>GCD--在GCD的過程中都是含有block使用的方法</li>
<li>排序--平時我們使用的一些簡單算法均可以block進(jìn)行相關(guān)處理</li>
</ul>

委托代理詳解

在發(fā)C++過程中經(jīng)常見到多組繼承蚣常,但是在IOS開發(fā)過程中只能進(jìn)行單繼承市咽,很多方法我們需要實現(xiàn)接口的形式。正如我們在使用UITableView時抵蚊,經(jīng)常會用到UITableViewDataSource和UITableViewDelegate施绎。
如果要使用使用委托代理,需要知道協(xié)議和委托兩者關(guān)系贞绳。

協(xié)議

協(xié)議一般分為兩種:

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

委托

常見的一種設(shè)計模式谷醉,身為老板一般負(fù)責(zé)管理員工、打電話冈闭、發(fā)薪水俱尼,這樣由于公司業(yè)務(wù)發(fā)展就請一個秘書負(fù)責(zé):打電話和發(fā)薪水。

首先進(jìn)行協(xié)議提任堋(需要員工所得事情):(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>

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

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

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

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

注冊監(jiān)聽小試

NSNotificationCenter就相當(dāng)于廣播一樣遇八,可以對對象進(jìn)行一次注冊然后有多個監(jiān)聽。就像我們知道如果有注冊的話耍休,就需要我們對于對象進(jìn)行釋放(remove)刃永。

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

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

在上面的代碼可以看出我們在使用注冊監(jiān)聽時可以進(jìn)行相關(guān)內(nèi)容傳遞,需要傳遞的內(nèi)容放在object(為id類型)羹应。
在remove監(jiān)聽的方法揽碘,經(jīng)過查找資料:
<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簡史

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

下列對于界面的UILabel進(jìn)行監(jiān)聽:

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

一旦UILabel發(fā)生變化就會調(diào)用方法:

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

KVC:通過設(shè)置key值,進(jìn)行標(biāo)記掖桦。然后通過key值找到進(jìn)行重新設(shè)置本昏,說的通俗點點就是鍵值對。類似NSDictionary相似枪汪。

以前經(jīng)秤磕拢看些大牛寫的博客,在看博客過程中也學(xué)到很多知識雀久。心向往之宿稀,開始動手寫關(guān)于自己在IOS成長路上見證。

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

我們知道blockdelegation是關(guān)于通信方面赖捌,在開發(fā)的過程中我們也經(jīng)常會使用兩者祝沸。面對具體情況我們需要怎么樣做出選著呢?

1.當(dāng)在一個方法的參數(shù)中需要多個對象時使用delegation

我們在開發(fā)過程中經(jīng)常使用UITableView越庇,這時我們就要實現(xiàn)接口協(xié)議UITableViewDataSource, UITableViewDelegate兩者是我們在使用UITableView的相關(guān)數(shù)據(jù)協(xié)議和相關(guān)協(xié)議罩锐。從tableView角度出發(fā):實現(xiàn)展示需要數(shù)據(jù)、對一些展示過程的控制等卤唉,這些可以使用block來進(jìn)行實現(xiàn)涩惑。

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

上面我們定義告訴過程需要參數(shù)在NSIndexPath,所以第三個參數(shù)應(yīng)該是 block形式。我們上面知道block在編譯過程會被編譯為struct結(jié)構(gòu)桑驱,在相互嵌套過程中使用delegation更加方便竭恬。

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

由于一個對象只能有一個delegate,而且它只能與這個delegate通信碰纬。讓我們看看CLLocationManager 這個類萍聊,當(dāng)發(fā)現(xiàn)地理位置后问芬,location manager 只會通知一個對象(有且只有一個)悦析。當(dāng)然,如果我們需要更多的對象去知道這個更新此衅,我們最好創(chuàng)建其他的location manager强戴。

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

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

因此,我們可以得出另一個結(jié)論:“如果一個對象是單例翘魄,不要使用delegation”鼎天。

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

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

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

4.delegation偏向于使用的過程 VS block偏向于使用結(jié)果

如果查看NSURLConnectionDelegate 以及 NSURLConnectionDataDelegate光羞,我們在可以protocol中看到這樣的消息:我將要做什么(如: willSendRequest绩鸣,將要發(fā)送請求)、到目前為止我知道的信息(如:canAuthenticateAgainstProtectionSpace)纱兑、我已經(jīng)完成這些啦( didReceiveResponse呀闻,收到請求的回復(fù),即完成請求)潜慎。這些消息組成一個流程捡多,而那些對流程感興趣的delegate將會在每一步得到相應(yīng)的通知。

當(dāng)我們觀察handler和完整的方法時铐炫,我們發(fā)現(xiàn)一個block包含一個響應(yīng)對象和一個錯誤對象垒手。顯然這里沒有任何有關(guān)“我在哪里,我正在做什么的”的交互倒信。

因此我們可以這樣認(rèn)為科贬,delegate的回調(diào)更多的面向過程,而block則是面向結(jié)果的鳖悠。如果你需要得到一條多步進(jìn)程的通知榜掌,你應(yīng)該使用delegation。而當(dāng)你只是希望得到你請求的信息(或者獲取信息時的錯誤提示)乘综,你應(yīng)該使用block憎账。(如果你結(jié)合之前的3個結(jié)論,你會發(fā)現(xiàn)delegate可以在所有事件中維持state卡辰,而多個獨立的block確不能)胞皱。
</br>

參考:

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末邪意,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子反砌,更是在濱河造成了極大的恐慌抄罕,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件于颖,死亡現(xiàn)場離奇詭異呆贿,居然都是意外死亡,警方通過查閱死者的電腦和手機森渐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門做入,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人同衣,你說我怎么就攤上這事竟块。” “怎么了耐齐?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵浪秘,是天一觀的道長。 經(jīng)常有香客問我埠况,道長耸携,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任辕翰,我火速辦了婚禮夺衍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘喜命。我一直安慰自己沟沙,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布壁榕。 她就那樣靜靜地躺著矛紫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪牌里。 梳的紋絲不亂的頭發(fā)上颊咬,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機與錄音二庵,去河邊找鬼贪染。 笑死,一個胖子當(dāng)著我的面吹牛催享,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播哟绊,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼因妙,長吁一口氣:“原來是場噩夢啊……” “哼痰憎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起攀涵,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤铣耘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后以故,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蜗细,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年怒详,在試婚紗的時候發(fā)現(xiàn)自己被綠了炉媒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡昆烁,死狀恐怖吊骤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情静尼,我是刑警寧澤白粉,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站鼠渺,受9級特大地震影響鸭巴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拦盹,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一奕扣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧掌敬,春花似錦惯豆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至华临,卻和暖如春芯杀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背雅潭。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工揭厚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人扶供。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓筛圆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親椿浓。 傳聞我的和親對象是個殘疾皇子太援,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容