2012年11月24日 星期六

不能說的秘密,就讓NSNotificationCenter來傳遞吧

利用NSNotificationCenter產生通知,我們可以輕易地讓不同物件跨越空間時間溝通。接下來彼得潘將以電影"不能說的秘密"舉例說明,女主角小雨,男主角彼得潘,男配角葉湘倫之間不能說的秘密,就讓NSNotificationCenter來為他們傳遞吧。

1. 定義Person類別


@interface Person : NSObject

@property (nonatomic, strong) NSString *name;

@end


2. 建立故事的三個主人翁


smallRain = [[Person alloc] init];
smallRain.name = @"小雨";

peterPan = [[Person alloc] init];
peterPan.name = @"彼得潘";

noBody = [[Person alloc] init];
noBody.name = @"葉湘倫";

3.  設計App畫面



畫面上加入按鈕(UIButton),按鈕的圖片上顯示小雨在大雨中對著天空吶喊的問題。


4. 傳送"不能說的秘密"通知

將按鈕的Touch Up Inside event連結至buttonPressed: method,並定義此method。我們已經看到按鈕上小雨詢問的問題了。為了不讓她等太久,我們希望按鈕按下時,馬上回應小雨。因此buttonPressed: 的定義如下:

- (IBAction)buttonPressed:(id)sender {
         [[NSNotificationCenter defaultCenter] postNotificationName:@"不能說的秘密" object:peterPan userInfo:@{@"答案":@"我在這邊,你在那邊,愛像風箏一條線,我經過千山萬水 停在這個定點,帶我的心,到你的世界"}];
}

說明:
利用NSNotificationCenter物件的postNotificationName:object:userInfo: method,我們可以發送通知讓對方知道,此method的定義如下:

- (void)postNotificationName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;

(1)aName: 通知的名稱。在這裡我們傳入"不能說的秘密"。到時候只有表明想收到"不能說的秘密"通知的物件,才會收到此通知。

(2) anObject: 發送通知的物件。 在這裡我們傳入peterPan,表明peterPan是真心回應小雨的Mr.Right。若是想要匿名也是可以,只要傳入nil,對方即無法知道通知的來源,但如此有可能造成對方收不到通知,等會我們會說明。

(3) aUserInfo: 想讓接收物件知道的額外資訊。由於是NSDictionary型別,所以基本上我們可以傳入任何的資訊。在此我們傳入彼得潘想對小雨說的話( 其實此段話為伍思凱"這邊那邊"的歌詞)。

5. 申請接收"不能說的秘密"通知

 [[NSNotificationCenter defaultCenter] addObserver:smallRain selector:@selector(get:) name:@"不能說的秘密" object:peterPan];

小雨為了要收到彼得潘的回應,她必須先申請接收"不能說的秘密"通知。利用NSNotificationCenter物件的addObserver:selector:name:object: method,小雨可以讓自己收到通知,此method的定義如下:

- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject;

(1) observer: 接收通知的物件,在此我們傳入smallRain,如此小雨即可收到通知。

(2) aSelector: 收到通知後執行的method。 小雨收到通知後,可能大哭,也可能大笑。不過別忘了我們是導演,我們傳入@selector(get:),等會再來定義get:。值得注意的,observer和aSelector是對應的,所以到時候get:將是定義在smallRain Person型別裡的method。

(3) aName: 通知的名稱,在此我們理所當然傳入"不能說的秘密"。

(4) anObject: 指定發送通知的對象。只有當通知是由此對象發送時,observer才會收到通知。在此我們傳入peterPan,所以只有當通知是由彼得潘發出時,小雨才會收到通知,get: method才會被呼叫。如果傳入nil了話,表示observer願意接收任何物件發出的"不能說的秘密"通知。因此如果傳入nil,小雨就可以收到葉湘倫發出的通知。但小雨是個專一的人,她的眼中只看得到彼得潘,看不到葉湘倫,所以我們還是傳入peterPan吧。

6. 定義小雨收到通知後執行的get: method (在Person.m裡)

-(void)get:(NSNotification*)noti
{
    Person *person = noti.object;
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:noti.userInfo[@"答案"] message:nil delegate:self cancelButtonTitle:person.name  otherButtonTitles:nil];
    [alertView show];    
}
說明:
NSNotification物件的object將等同發送通知的物件,也就是彼得潘。而NSNotification物件的userInfo則等於當初彼得潘發送通知時傳入的userInfo,因此從key"答案",我們可以取得彼得潘想說的話。當小雨看到彼得潘的回應時,感動地大聲朗誦出來,因此我們以UIAlertView在畫面上加以呈現。

執行App:

按下按鈕後,彼得潘果真回應了小雨,而小雨也收到了,因此我們在畫面上看到訴說著"我在這裡 ... "的UIAlertView。



如果當初發出通知的是代表葉湘倫的noBody,即便他很深情的以林俊傑"你要的不是我"的歌詞來回應,由於小雨早已指定只接收來自彼得潘的通知,所以小雨永遠也不會收到葉湘倫的訊息。

 [[NSNotificationCenter defaultCenter] postNotificationName:@"不能說的秘密" object:noBody userInfo:@{@"答案":@"你要的不是我,心碎的失去輪廓,曾經給你的感動,只是情緒的波動"}];

執行App:

不管我們按了幾百,幾千次按鈕,小雨都收不到來自葉湘倫的通知,因此永遠也看不到UIAlertView。



不可小看的背景陷阱:

如果當初我們呼叫postNotificationName:object:userInfo:發送通知時處在背景thread,而非main thread,到時候收到通知時所呼叫的method也將會在背景thread執行。以剛剛的例子來說,如果我們不幸在背景thread發送通知,到時候收到通知時,App將壯烈的crash死去。這是因為我們收到通知時會顯示UIAlertView,但此時卻處於背景thread,違反了UI相關程式一定要在main thread執行的規定。






沒有留言:

張貼留言