【前言介紹】
iOS的一種設(shè)計模式盾计,觀察者Observer模式(也叫發(fā)布/訂閱搞糕,即Publich/Subscribe模式)钙皮。
觀察者模式蜂科,包含:
- 通知機制(notification)
- KVO機制【可參考iOS--KVO的實現(xiàn)原理與具體應(yīng)用】
在這本文中,我們將介紹在日常項目當中經(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媳搪,:Mydemo1、Mydemo2骤宣。自己琢磨琢磨秦爆。
點擊“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:啊左~