總結(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使用場景的比較
我們知道block
和delegation
是關(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>