iOS 趣談設(shè)計模式——通知

【前言介紹】

iOS的一種設(shè)計模式盾计,觀察者Observer模式(也叫發(fā)布/訂閱搞糕,即Publich/Subscribe模式)钙皮。
觀察者模式蜂科,包含:

在這本文中,我們將介紹在日常項目當中經(jīng)常使用到的通知機制這一種設(shè)計模式短条。

通知機制
委托機制是代理“一對一”的對象之間的通信导匣,而通知機制是廣播“一對多”的對象之間的通信;


一茸时、是什么贡定?【生活問題例子】

“短信天氣預(yù)報”


當A類發(fā)送一條信息給通知中心時,注冊為用戶(觀察者)的B類群就會收到相應(yīng)的通知可都,并作出反應(yīng)缓待。


二、有什么用渠牲?【代碼中的應(yīng)用】

在不同類之間如何傳遞數(shù)據(jù)旋炒?
有幾種方法:屬性傳遞、代理協(xié)議签杈,另外就是通知瘫镇。
通知:在A類中創(chuàng)建的方法,B類中執(zhí)行,且可以使用該通知攜帶數(shù)據(jù)傳遞給對方汇四;


三接奈、有什么不同?【與其他“通知”的不同通孽?】

經(jīng)常提到的通知序宦,有“廣播通知”、“本地通知”背苦、“推送通知”互捌。
本文所介紹的就是廣播通知,是實現(xiàn)觀察者模式的一種機制行剂,可以在一個應(yīng)用中的多個對象之間進行通信傳遞數(shù)據(jù)秕噪。
而本地通知和推送通知主要是給用戶發(fā)送“通知提示”,例如警告提示厚宰、聲音腌巾、震動以及如圖標上的紅色數(shù)字提示。
第一種由“本地發(fā)送通知”給用戶铲觉,第二種由第三方應(yīng)用發(fā)送給蘋果官方的遠程服務(wù)器澈蝙,然后再由服務(wù)器“推送通知”給用戶。


四撵幽、產(chǎn)品經(jīng)理:老規(guī)矩灯荧,代碼拿來~【具體實現(xiàn)】

過程
在通知機制中,需要(或者說感興趣)接收某個通知的信息的所有對象都可以成為接收者盐杂,首先注冊成為觀察者逗载。
進行注冊后,通知中心就會把發(fā)布者發(fā)送的通知信息链烈,廣播給注冊過該通知的觀察者厉斟。且觀察者只能接收到通知中心的信息,不能知道通知是誰投送的测垛。
最后捏膨,接受者不想再對關(guān)注該通知的信息時,可以給通知中心發(fā)生解除注冊的信息食侮,之后都不再接收到通知了号涯。

1.獲取通知中心(NSNotificationCenter)對象:(就像獲取移動營運商短信中心的權(quán)限,作為媒介)

發(fā)布锯七、注冊链快、解除通知都需要使用通知中心,負責(zé)協(xié)助不同對象眉尸、不同類之間的消息通信域蜗。

[NSNotificationCenter  defaultCenter];  //需要注意的是巨双,通知中心也是一個單例
2.發(fā)布(A類)和接收(B類)

a.做為發(fā)布者的A類發(fā)送通知
可以使用一下三個方法:

- (void)postNotification:(NSNotification *)notification;
- (void)postNotificationName:(NSString *)aNameobject:(id)anObject;
- (void)postNotificationName:(NSString *)aNameobject:(id)anObject userInfo:
(NSDictionary *)aUserInfo;
  • postNotificationName:指定消息名稱;
  • object:指定發(fā)消息者霉祸;
  • userInfo:通知中用于傳遞參數(shù)的載體筑累,傳遞的方法是把參數(shù)放在NSDictionary類型的userInfo中。例如:NSDictionary *dict = [notification userInfo];

一般使用第三個方法丝蹭,如果參數(shù)不需要的慢宗,可以設(shè)置為nil.

b.注冊通知,加入觀察者:
做為觀察者B類注冊通知奔穿,進行監(jiān)聽:

- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSString *)aName object:(nullable id)anObject;
//@selector中為回調(diào)方法镜沽,在本類中對通知進行相應(yīng)的處理,name為通知名稱贱田、object為對象缅茉;

剖析

  • object==nil,那么客戶對象(self)將收到任何對象發(fā)出NSWindowDidBecomeMainNotification的通知消息男摧;
  • name==nil,那么觀察者將接收到object對象的所有消息蔬墩,但是確定不了接收這些消息的順序。
  • object==nil彩倚,name==nil筹我,那么該觀察者將收到所有對象的所有消息。

對于一個任意的觀察者observer帆离,如果不能保證其對應(yīng)的selector有本類自定義的方法:(例如,MyMethod),可采用[observer respondsToSelector:@selector(MyMethod:)]]進行檢查结澄。
所以完整的添加觀察者過程為:

if([observer respondsToSelector:@selector(MyMethod:)]) {
[[NSNotificationCenter defaultCenter] addObserver:observer selector:
@selector(MyMethod:) name:NSWindowDidBecomeMainNotification object:nil];
}

當然在蘋果API中也有另外一個注冊觀察者的方法:

- (id )addObserverForName:(nullable NSString *)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block

此方法是支持在該方法中進行block回調(diào)的哥谷,而queue參數(shù)就是表示此模塊在queue隊列中進行。
但是這方法一般不采用麻献,因為在此方法的 block 中们妥,稍微不注意調(diào)用 self 的話,會引起循環(huán)引用勉吻,造成內(nèi)存泄露监婶,所以還是建議使用第一種方法進行觀察者的創(chuàng)建。

c.移除通知
注冊過的對象必須在釋放之前注銷掉齿桃,如果不這樣的話惑惶,當該通知再次出現(xiàn)時,通知中心可能會向已釋放的觀察者對象發(fā)送消息短纵,從而導(dǎo)致應(yīng)用崩潰带污。
在ARC下,系統(tǒng)會自動回收無用的通知對象內(nèi)存香到,但是由于系統(tǒng)回收機制ARC有一定的延遲性鱼冀,所以即使不會出錯报破,也建議養(yǎng)成習(xí)慣,對通知進行手動釋放無用的通知千绪。
移除有2種方法:

//釋放所有通知
- (void)removeObserver:(id)observer;

//釋放名稱為aName的通知
- (void)removeObserver:(id)observer name:(nullable NSString *)aName object:(nullable id)anObject;

一般在視圖控制器中充易,可以在“didReceiveMemoryWarning:”中發(fā)送解除消息:【這只是參考,建議還是在 :-(void)dealloc ){} 中進行移除荸型。 】

-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
//移除觀察者
[[NSNotificationCenter defaultCenter]removeObserver:self];
}

(by:從 iOS 9 開始通知中心會對觀察者進行 weak 弱引用盹靴,所以不需要在觀察者對象釋放之前從通知中心移除。即使不對通知進行手動移除帆疟,指針也會在注冊者被回收后自動置空鹉究,向空指針 nil 發(fā)送消息是不會有問題的。
但是踪宠,通過 - (id )addObserverForName: object: queue: usingBlock: 方法注冊的觀察者依然需要手動的釋放自赔,因為通知中心對它們持有的是強引用。)


五柳琢、那些年我們用過的系統(tǒng)通知名稱~

系統(tǒng)自帶的也有許多有用的通知绍妨,我們只需要注冊為相應(yīng)的通知接收對象,就能根據(jù)通知狀態(tài)的變化發(fā)生相應(yīng)的數(shù)據(jù)改變柬脸。
部分系統(tǒng)通知名稱如下:

UIApplicationDidFinishLaunchingNotification   // 應(yīng)用程序啟動后
UIApplicationDidBecomActiveNotification       //進入前臺
UIApplicationWillResignActiveNotification     //應(yīng)用將要進入后臺
UIApplicationDidEnterBackgroundNotification   //進入后臺
UIKeyboardWillShowNotification       // 鍵盤即將顯示
UIKeyboardDidShowNotification        // 鍵盤顯示完畢
UIKeyboardWillHideNotification       // 鍵盤即將隱藏
UIKeyboardDidHideNotification        // 鍵盤隱藏完畢

六他去、舉個栗子:“??”
本文有2個例子:

  • 一個是完整的通知發(fā)布、接收倒堕、解除過程灾测;
  • 一個是系統(tǒng)通知名稱的應(yīng)用(以第三個:UIApplicationWillResignActiveNotification
    為例);

(by:覺得文章太長不想看這段的童鞋垦巴,也可以到github上下載啊左的demo媳搪,:Mydemo1Mydemo2骤宣。自己琢磨琢磨秦爆。
點擊“DownLoad ZIP”按鈕就可以了。一般使用Safari瀏覽器下載得了憔披,啊左用QQ瀏覽器貌似下載不了...囧)

【本次開發(fā)環(huán)境: Xcode:7.2 iOS Simulator:iphone6 By:啊左】

1.完整的通知發(fā)布等限、接收、解除過程:

UI控件擺放如下芬膝,視圖望门、控件的背景可以自己設(shè)置成比較明顯的顏色,便于觀察:


  • A視圖創(chuàng)建一個textView用于顯示B視圖傳遞過來的信息蔗候,一個按鈕用于切換到B視圖怒允;
  • B視圖創(chuàng)建一個文本框用于更新信息,一個按鈕用于把文本框的信息更新并返回到視圖A锈遥。

然后纫事,點擊A類的按鈕勘畔,并且按住control拖拽到B視圖的控制器后松開鼠標,在彈出的選擇框(如下圖)選擇:“Present Modally”用于創(chuàng)建A丽惶、B控制器之間的模態(tài)類型的Segue炫七。


接下來,我們需要在新建一個視圖控制器B類SeocndViewController:


回到故事板中钾唬,選擇B視圖控制器万哪,打開標識檢查器(下圖第一排第三個選項),選擇class為:SeocndViewController抡秆。這就使代碼與故事板中的視圖控制器對應(yīng)起來奕巍。(A視圖默認對應(yīng)ViewController,如果有錯誤儒士,可以檢查一下的止。)


然后我們打開輔助編輯器,按住control着撩,拖拽A視圖中的文本連接到對應(yīng)的輸出口诅福,這里我們命名為“myLabel”.


以此方式,繼續(xù)為B類中的文本框連接到代碼中拖叙,并命名為:“MyTextView”氓润,
為B類的按鈕添加行為,方法名為:“saveBtn:”薯鳍,
啊左還是覺得上代碼實在點:
(ViewController.h類)

#import
@interface ViewController : UIViewController
//每次視圖打開后咖气,監(jiān)聽B類的數(shù)據(jù)是否發(fā)生變化,如有變化挖滤,在這個文本視圖中顯示更新采章。
@property (weak, nonatomic) IBOutlet UITextView *myLabel;  
@end

(ViewController.m類)

#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//1.注冊為觀察者,監(jiān)聽B視圖中的通知
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(AMethod:) name:@"MyNotificationName" object:nil];
}

//回調(diào)方法:AMethod:
-(void)AMethod:(NSNotification *)notification
{
//2.獲取通知攜帶的數(shù)據(jù)壶辜,更新label的文本信息
NSDictionary *dictData = [notification userInfo];
NSString *str = [dictData objectForKey:@"MyUserInfoKey"];
self.myLabel.text = str;
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
//3.移除所有通知
[[NSNotificationCenter defaultCenter]removeObserver:self];
}
@end

(SecondViewController.h)

#import
@interface SecondViewController : UIViewController
 //文本框,用于更新傳遞給ViewController視圖的數(shù)據(jù)
@property (weak, nonatomic) IBOutlet UITextField *MyTextView; 
- (IBAction)saveBtn:(UIButton *)sender;        //保存返回按鈕事件
@end

(SecondViewController.m)

#import "SecondViewController.h"
@interface SecondViewController ()
@end
@implementation SecondViewController

- (void)viewDidLoad {
[super viewDidLoad];
}

- (IBAction)saveBtn:(UIButton *)sender {

//返回視圖A并在塊中發(fā)布通知
[self dismissViewControllerAnimated:YES completion:^{
//1.創(chuàng)建userInfo攜帶的信息
NSString *str = self.MyTextView.text;
NSDictionary *dictData = [NSDictionary dictionaryWithObject:str forKey:@"MyUserInfoKey"];

//2.發(fā)布信息
[[NSNotificationCenter defaultCenter]postNotificationName:@"MyNotificationName" object:nil userInfo:dictData];
}];

}
@end

驗證:
第一次A視圖的文本視圖中沒有顯示數(shù)據(jù)担租,點擊按鈕“確定切換頁面”砸民,打開視圖B,在文本框中輸入信息(例如123)奋救,點擊“保存返回”按鈕岭参,在A視圖的文本視圖中看到更新的信息:123。
by:有需要的童鞋可以到github上下載啊左的demo:Mydemo1尝艘。

2.系統(tǒng)通知名稱的應(yīng)用(以UIApplicationWillResignActiveNotification為例):

UIApplicationWillResignActiveNotification的意思是應(yīng)用即將進入后臺的這個時刻演侯。
首先,創(chuàng)建UI界面如下背亥,相比第一個例子秒际,這個會簡單很多:一個按鈕+一個顯示顏色的UIView視圖悬赏。


創(chuàng)建一個命名為“myView”的UIView控件,一個方法為“changeColorBtn:”的按鈕行為即可娄徊,關(guān)聯(lián)ViewController控制器闽颇。
代碼如下:
(ViewController.h類)

#import
@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIView *myView;
- (IBAction)changeColorBtn:(UIButton *)sender;
@end

(ViewController.m類)

#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController

- (void)viewDidLoad {    
[super viewDidLoad];       
 
//1. 注冊為觀察者    
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(applicationWillResignActiveNotification:) name:UIApplicationWillResignActiveNotification object:nil];
}

//2.當應(yīng)用即將進入后臺時,調(diào)用通知回調(diào)方法:
-(void)applicationWillResignActiveNotification:(NSNotification *)notification{  
//返回后臺的過程寄锐,把視圖背景改為紅色兵多;    
self.myView.backgroundColor = [UIColor redColor];
}

- (IBAction)changeColorBtn:(UIButton *)sender {        
//按鈕把視圖背景改為黃色;    
self.myView.backgroundColor = [UIColor yellowColor];
}
@end

視圖第一次打開橄仆,視圖為默認白色:
點擊按鈕剩膘,視圖變?yōu)辄S色:


>【按鈕事件】

按住“command+shift”,雙擊H盆顾,進入iOS多任務(wù)欄怠褐;
或者按住“command+shift”,單擊H椎扬,回到模擬器主界面惫搏。


>【iOS多任務(wù)欄】

發(fā)現(xiàn),以上2種情況都可以看到視圖變?yōu)榧t色蚕涤。
且回到應(yīng)用后筐赔,顏色仍然是紅色。


>【回到前臺】

也就是揖铜,當應(yīng)用從活躍的狀態(tài)進入非活躍狀態(tài)的時候茴丰,系統(tǒng)自動發(fā)送“UIApplicationWillResignActiveNotification”這個通知,如有注冊監(jiān)聽者(觀察者)天吓,則執(zhí)行回調(diào)方法贿肩。
by:有需要的童鞋可以到github上下載啊左的demo:Mydemo2



(轉(zhuǎn)載請標明原文出處龄寞,謝謝支持 ~ - ~)
? by:啊左~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末汰规,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子物邑,更是在濱河造成了極大的恐慌溜哮,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件色解,死亡現(xiàn)場離奇詭異茂嗓,居然都是意外死亡,警方通過查閱死者的電腦和手機科阎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門述吸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锣笨,你說我怎么就攤上這事蝌矛〉琅” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵朴读,是天一觀的道長屹徘。 經(jīng)常有香客問我,道長衅金,這世上最難降的妖魔是什么噪伊? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮氮唯,結(jié)果婚禮上鉴吹,老公的妹妹穿的比我還像新娘。我一直安慰自己惩琉,他們只是感情好豆励,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瞒渠,像睡著了一般良蒸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伍玖,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天嫩痰,我揣著相機與錄音,去河邊找鬼窍箍。 笑死串纺,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的椰棘。 我是一名探鬼主播纺棺,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼邪狞!你這毒婦竟也來了祷蝌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤帆卓,失蹤者是張志新(化名)和其女友劉穎杆逗,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鳞疲,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年蠕蚜,在試婚紗的時候發(fā)現(xiàn)自己被綠了尚洽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡靶累,死狀恐怖腺毫,靈堂內(nèi)的尸體忽然破棺而出癣疟,到底是詐尸還是另有隱情,我是刑警寧澤潮酒,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布睛挚,位于F島的核電站,受9級特大地震影響急黎,放射性物質(zhì)發(fā)生泄漏扎狱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一勃教、第九天 我趴在偏房一處隱蔽的房頂上張望淤击。 院中可真熱鬧,春花似錦故源、人聲如沸污抬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽印机。三九已至,卻和暖如春门驾,著一層夾襖步出監(jiān)牢的瞬間射赛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工猎唁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留咒劲,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓诫隅,卻偏偏與公主長得像腐魂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子逐纬,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

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

  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,124評論 29 470
  • 翻譯自“View Controller Programming Guide for iOS”蛔屹。 1 彈出視圖控制器...
    lakerszhy閱讀 3,462評論 2 20
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)豁生,斷路器兔毒,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • 天涯為客已堪傷,春風(fēng)攜雨潤客腸甸箱。三月北國春正好育叁,清明時節(jié)好還鄉(xiāng)。 已是人間三月芍殖,春色漸好豪嗽。再過幾日就是清明節(jié),是該...
    風(fēng)林火閱讀 444評論 0 0
  • 今天星期比較煩悶不這么喜歡工作隐锭,幸好東東回來,晚上跟他聊了很多计贰,談著夢想钦睡,談這工作上的不如意,生活總是有非常多的...
    一葉秋濃閱讀 635評論 1 3