在一個(gè)復(fù)雜的然爆,有狀態(tài)的系統(tǒng)中,當(dāng)一個(gè)對象的狀態(tài)發(fā)生改變徐许,如何通知系統(tǒng),并對狀態(tài)改變做出相應(yīng)的行為是必需考慮的一個(gè)問題卒蘸,在iOS中為這類問題提供了4種解決方法:
1雌隅、NSNotifiactaion和NSNotificationCenter:通知中心
2、Delegate:代理
3缸沃、Block:回調(diào)(Callback)
4恰起、KVO(Key-Value Observing):鍵值觀察
一、NSNotification 和 NSNotificationCenter
每個(gè)運(yùn)行中的application都有一個(gè)NSNotificationCenter的成員變量,它的功能就類似公共欄趾牧。對象注冊關(guān)注某個(gè)確定的notification检盼,我們把這些注冊對象叫做 observer。其它的一些對象會給center發(fā)送notifications翘单,center將該notifications轉(zhuǎn)發(fā)給所有注冊對該notification感興趣的對象吨枉,我們把這些發(fā)送notification的對象叫做 poster。
1哄芜、在要發(fā)出通知消息的地方:
[[NSNotificationCenter defaultCenter] postNotificationName:@"Notification_isHaveBanner" object:nil userInfo:@{@"ishavebanner":@"1"}];
2貌亭、在要接收通知消息的地方:
//對象注冊,并關(guān)連消息
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reciveBannernNotice:) name:@"Notification_isHaveBanner" object:nil];
#pragma mark 接收通知處理傳遞消息
-(void) reciveBannernNotice:(NSNotification *)notification
{
NSString *bannerStr = [notification.userInfo objectForKey:@"ishavebanner"];
if ([bannerStr isEqualToString:@"0"]) {
isHaveBanner = NO;
}else if([bannerStr isEqualToString:@"1"])
{
isHaveBanner = YES;
}else{
isHaveBanner = NO;
}
}
一认臊、Delegate(代理)
delegate(代理)是iOS編程的一種設(shè)計(jì)模式圃庭,簡單來說就是一個(gè)委托方的類讓另外一個(gè)代理方的類具體實(shí)現(xiàn)其定義的方法。怎樣寫一個(gè)代理設(shè)計(jì)模式
1失晴、你要明確你的協(xié)議名稱剧腻,一般來講名稱都是:控件類名 + Delegate
2、代理方法中一般都是聲明為@optional(程序默認(rèn)情況下是@required)
3涂屁、代理方法名一般以控件開頭
4书在、代理方法至少有一個(gè)參數(shù)
舉例:來個(gè)保姆和嬰兒之間是怎樣利用代理協(xié)議來實(shí)現(xiàn)一個(gè)簡單的找過過程,嬰兒類要坐的事委托保姆類做拆又。
1蕊温、首先袱箱,我們創(chuàng)建一個(gè)嬰兒類,繼承自NSObject 义矛,接下來在Baby.h文件中創(chuàng)建下面的代碼
#import <Foundation/Foundation.h>
// 定義一份代理協(xié)議
@protocol BabyDelegate <NSObject>
- (void)babyWantEat:(Baby *)baby;
- (void)babyWantSleep:(Baby *)baby;
@end
@interface Baby : NSObject
/** 吃了多少東西 */
@property (nonatomic, assign) int food;
/** 睡意 */
@property (nonatomic, assign) int sleep;
/** 餓了 */
- (void)wantEat;
/** 困了 */
- (void)wantSleep;
/** 代理對象 */
@property (nonatomic, weak) id<BabyDelegate> delegate;
@end
2发笔、接下來是Baby.m文件中創(chuàng)建下面的文件
#import "Baby.h"
@implementation Baby
- (void)wantEat
{
NSLog(@"嬰兒想吃東西");
// 通知保姆喂嬰兒
if ([self.deleate respondsToSelector:@selector(babyWantEat:)]){
[self.delegate babyWantEat:self];
}
}
- (void)wantSleep
{
NSLog(@"嬰兒想睡覺");
// 通知保姆哄嬰兒睡覺
if ([self.deleate respondsToSelector:@selector(babyWantSleep:)]){
[self.delegate babyWantSleep:self];
}
}
@end
3、下面創(chuàng)建一個(gè)保姆類同樣繼承自NSObject,下面是Nurse.h文件中的代碼
#import "Baby.h"
#import <Foundation/Foundation.h>
@interface Nurse : NSObject <BabyDelegate>
@end
4凉翻、接下來是Nurse.m文件中保姆需要在代理委托方法中做她的工作
#import "Nurse.h"
@implementation Nurse
- (void)babyWantEat:(Baby *)baby
{
//baby是代理委托方傳過來的值
baby.food += 20;
NSLog(@"Nurse喂嬰兒吃東西--現(xiàn)在的食量是%d", baby.food);
}
- (void)babyWantSleep:(Baby *)baby
{
baby.sleep += 20;
NSLog(@"Nurse哄嬰兒睡覺--現(xiàn)在的睡意是%d", baby.sleep);
}
@end
三了讨、Block(回調(diào))
直接舉例:一個(gè)Block回調(diào)修改上一界面的背景顏色。
ViewController1 控制器1制轰,ViewController2 控制器2前计。控制器1跳轉(zhuǎn)到控制器2垃杖,然后在控制器2觸發(fā)事件回調(diào)修改控制器1的背景顏色為紅色男杈。
1、ViewController2實(shí)現(xiàn):
#import <UIKit/UIKit.h>
@interface ViewController2 : UIViewController
/**
* 定義了一個(gè)changeColor的Block调俘。這個(gè)changeColor必須帶一個(gè)參數(shù)伶棒,這個(gè)參數(shù)的類型必須為id類型的
* 無返回值
* @param id
*/
typedef void(^changeColor)(id);
/**
* 用上面定義的changeColor聲明一個(gè)Block,聲明的這個(gè)Block必須遵守聲明的要求。
*/
@property (nonatomic, copy) changeColor backgroundColor;
@end
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//聲明一個(gè)顏色
UIColor *color = [UIColor redColor];
//用剛剛聲明的那個(gè)Block去回調(diào)修改上一界面的背景色
self.backgroundColor(color);
}
1彩库、ViewController1實(shí)現(xiàn):
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
ViewController2 *vc =[[ViewController2 alloc]init];
// 回調(diào)修改顏色
vc.backgroundColor = ^(UIColor *color){
self.view.backgroundColor = color;
};
[self.navigationController pushViewController:vc animated:YES];
}
四肤无、KVO(Key-Value Observing)
KVO是Object-C中定義的一個(gè)通知機(jī)制,其定義了一種對象間監(jiān)控對方狀態(tài)的改變骇钦,并做出反應(yīng)的機(jī)制宛渐。對象可以為自己的屬性注冊觀察者,當(dāng) 這個(gè)屬性的值發(fā)生了改變眯搭,系統(tǒng)會對這些注冊的觀察者做出通知窥翩。其用途十分廣泛,比方說鳞仙,你的下載進(jìn)度條是根據(jù)下載百分比決定的鳍烁,那么,可以通過觀察下載百 分比的改變繁扎,刷新進(jìn)度條的樣式幔荒,來直觀的反應(yīng)下載進(jìn)度等等。
A.注冊觀察者:
//第一個(gè)參數(shù)observer:觀察者 (這里觀察self.myKVO對象的屬性變化)
//第二個(gè)參數(shù)keyPath: 被觀察的屬性名稱(這里觀察self.myKVO中num屬性值的改變)
//第三個(gè)參數(shù)options: 觀察屬性的新值梳玫、舊值等的一些配置(枚舉值爹梁,可以根據(jù)需要設(shè)置,例如這里可以使用兩項(xiàng))
//第四個(gè)參數(shù)context: 上下文提澎,可以為kvo的回調(diào)方法傳值(例如設(shè)定為一個(gè)放置數(shù)據(jù)的字典)
[self.myKVO addObserver:self forKeyPath:@"num" options:
NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
注意:這里的self.myKVO是被觀察的對象姚垃,可以是self本身、也可以是定義的self.tableview等等盼忌。forKeyPath對應(yīng)的一定要是這個(gè)對象的屬性名稱积糯。
B. 屬性(keyPath)的值發(fā)生變化時(shí)掂墓,收到通知,調(diào)用以下方法:
//keyPath:屬性名稱
//object:被觀察的對象
//change:變化前后的值都存儲在change字典中
//context:注冊觀察者時(shí)看成,context傳過來的值
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
//do something...
}
其他相關(guān)鏈接:開發(fā)該選擇Blocks還是Delegates