【Tutorial】Target & Action, 另外一種響應事件的方法

我們知道在Cocoa程序中, 如果你想處理一個窗口的事件或者應用程序的事件, 你可以使用Delegate的方法來實現響應的事件處理函數,但是如果你要處理一個button控件或者一個窗口裏面很多的類似Button的這種有一種 缺省行爲的控件的時候, 是不是要爲他們每個控件都來實現這個Delegate呢?答案是否定的,因爲針對這些具有指定的缺省行爲的控件,NSControl已經幫我們進行了一部 分的處理(這個control要求繼承自NSActionCell, 比如你添加一個Button到窗口中,那麼對Button的Click事件,NSControl是有自己的處理的,這就是Target-Action模 式,當Button的click事件被觸發是,NSControl就會去檢查這個Control中是否有對應的Target,這個Target針對這個事 件處理的Action是什麼,如果Target和Action都存在,那麼這個Click事件就會被NSControl直接處理,類似的處理代碼如下(假 設的實現)
-(void) mouseClicked()
{
    if (target != nil && action != nil && [target respondsToSelector:action])
    {
        [target performSelector:action withObject:self];
    }
}
下面我們通過具體的代碼來展示一下這個技術。
#import <Cocoa/Cocoa.h>

@interface MyController : NSObject
{
}
- (void) onButtonClicked:(id)sender;
@end

@implementation MyController
- (void) onButtonClicked:(id)sender
{
    [NSApp terminate:NSApp];
}
@end

int main(int argc, char *argv[])
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    [NSApplication sharedApplication];
   
    //Create main window
    NSRect rc = NSMakeRect(0, 0, 800, 600);
    NSUInteger uiStyle = NSTitledWindowMask | NSResizableWindowMask | NSClosableWindowMask;
    NSBackingStoreType backingStoreStyle = NSBackingStoreBuffered;
    NSWindow* win = [[NSWindow alloc] initWithContentRect:rc styleMask:uiStyle backing:backingStoreStyle defer:NO];
    [win setTitle:@"HelloWin Test"];
    //Create a button and set it as content view
    NSButton* button = [[NSButton alloc] initWithFrame:NSMakeRect(200, 200, 60, 40)];
    [button setTitle:@"Quit Application"];
    [win setContentView:button];
    //Set target and action
    MyController* controller = [[MyController alloc] init];
    [button setTarget:controller];
    [button setAction:@selector(onButtonClicked:)];
    [win center]; //Center main Window
    [win makeKeyAndOrderFront:win];
    [win makeMainWindow];
   
    [NSApp run];
    [button release];
    [win release];
    [pool drain];
   
    return 0;
}

上 面這段代碼首先創建了一個窗口,然後在窗口內創建了一個Button,這個Button作爲窗口的ContentView呈現,因爲NSButton是從 NSView繼承來的,所以也是NSView的一個實例。在創建了Button對象之後,我們就爲這個Button對象設置了用來處理缺省事件的 Target對象,然後又通過setAction來設置處理這個事件的方法, 這裏要注意的是這個Action方法一定是可以被Target響應的,也就是說符合respondsToSelector:的調用。

這個 Target對象的實現被放在一個叫做MyController的類裏面,它實現了一個方法叫做onButtonClicked:,這個方法被作爲一個 Action方法,所以它有一個參數(id)sender,這是因爲Action方法被調用的時候,調用者會將觸發這個Action的對象通過這個參數傳 遞給Action方法。這個例子的實現很簡單,直接調用NSApp對象的Terminate:方法來退出應用程序。

這個例子結束的地方和 以前有所不同, 我分別調用了Button和Window的release方法,這是因爲Window和Button對象都是通過引用形式分配的,所以當不再需要使用的時 候需要將他們所佔用的資源釋放掉。Obj-C 2.0中提供了Garbage Colletion的功能,你分配的對象會在不被使用的時候自動釋放,不過如果能瞭解Cocoa對象的分配和釋放模式,對我們理解整個Cocoa框架的實 現還是大有益處的。

上面這個例子還有一個地方需要注意的是Button的大小,你可以看到在代碼中我爲button分配的Rect的大小 是60x40,但是運行的時候你會發現這個Button充滿了整個Windows的Content區域,這是因爲這個Button作爲Window的ContentView,而Window的ContentView的Size Policy是隨父窗口改變高度和寬度的。如果我們將

[win setContentView:button];

這句代碼註釋掉,換成下面的這句

[[win contentView] addSubview:button];

那麼這個Button就在窗口中被正確定位了, 但是當你改變窗口大小的時候,這個Button的位置和大小都沒有改變,這個是由它的Sizing Policy決定的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章