iOS中通知、代理、Block、KVO

在一個復(fù)雜的,有狀態(tài)的系統(tǒng)中,當(dāng)一個對象的狀態(tài)發(fā)生改變,如何通知系統(tǒng),并對狀態(tài)改變做出相應(yīng)的行為是必需考慮的一個問題,在iOS中為這類問題提供了4種解決方法:

1、NSNotifiactaionNSNotificationCenter:通知中心
2、Delegate:代理
3、Block:回調(diào)(Callback)
4、KVO(Key-Value Observing):鍵值觀察

一、NSNotification 和 NSNotificationCenter

每個運(yùn)行中的application都有一個NSNotificationCenter的成員變量,它的功能就類似公共欄。對象注冊關(guān)注某個確定的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è)計模式,簡單來說就是一個委托方的類讓另外一個代理方的類具體實現(xiàn)其定義的方法。怎樣寫一個代理設(shè)計模式

1、你要明確你的協(xié)議名稱,一般來講名稱都是:控件類名 + Delegate
2、代理方法中一般都是聲明為@optional(程序默認(rèn)情況下是@required)
3、代理方法名一般以控件開頭
4、代理方法至少有一個參數(shù)

舉例:來個保姆和嬰兒之間是怎樣利用代理協(xié)議來實現(xiàn)一個簡單的找過過程,嬰兒類要坐的事委托保姆類做。
1、首先,我們創(chuàng)建一個嬰兒類,繼承自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)建一個保姆類同樣繼承自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))

直接舉例:一個Block回調(diào)修改上一界面的背景顏色。

ViewController1 控制器1,ViewController2 控制器2。控制器1跳轉(zhuǎn)到控制器2,然后在控制器2觸發(fā)事件回調(diào)修改控制器1的背景顏色為紅色。

1、ViewController2實現(xiàn):

#import <UIKit/UIKit.h>
@interface ViewController2 : UIViewController
/**
 *  定義了一個changeColor的Block。這個changeColor必須帶一個參數(shù),這個參數(shù)的類型必須為id類型的
 *  無返回值
 *  @param id
 */
typedef void(^changeColor)(id);
/**
 *  用上面定義的changeColor聲明一個Block,聲明的這個Block必須遵守聲明的要求。
 */
@property (nonatomic, copy) changeColor backgroundColor;
@end


-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //聲明一個顏色
    UIColor *color = [UIColor redColor];
    //用剛剛聲明的那個Block去回調(diào)修改上一界面的背景色
    self.backgroundColor(color);
}

1、ViewController1實現(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中定義的一個通知機(jī)制,其定義了一種對象間監(jiān)控對方狀態(tài)的改變,并做出反應(yīng)的機(jī)制。對象可以為自己的屬性注冊觀察者,當(dāng) 這個屬性的值發(fā)生了改變,系統(tǒng)會對這些注冊的觀察者做出通知。其用途十分廣泛,比方說,你的下載進(jìn)度條是根據(jù)下載百分比決定的,那么,可以通過觀察下載百 分比的改變,刷新進(jìn)度條的樣式,來直觀的反應(yīng)下載進(jìn)度等等。

A.注冊觀察者:

//第一個參數(shù)observer:觀察者 (這里觀察self.myKVO對象的屬性變化)
//第二個參數(shù)keyPath: 被觀察的屬性名稱(這里觀察self.myKVO中num屬性值的改變)
//第三個參數(shù)options: 觀察屬性的新值、舊值等的一些配置(枚舉值,可以根據(jù)需要設(shè)置,例如這里可以使用兩項)
//第四個參數(shù)context: 上下文,可以為kvo的回調(diào)方法傳值(例如設(shè)定為一個放置數(shù)據(jù)的字典)
[self.myKVO addObserver:self forKeyPath:@"num" options:
NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
注意:這里的self.myKVO是被觀察的對象,可以是self本身、也可以是定義的self.tableview等等。forKeyPath對應(yīng)的一定要是這個對象的屬性名稱。

B. 屬性(keyPath)的值發(fā)生變化時,收到通知,調(diào)用以下方法:

//keyPath:屬性名稱
//object:被觀察的對象
//change:變化前后的值都存儲在change字典中
//context:注冊觀察者時,context傳過來的值
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
   //do something...
}

其他相關(guān)鏈接:開發(fā)該選擇Blocks還是Delegates

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容