一、前言
寫該篇文章的緣由是:我想手動控制macOS的主程序循環,用來添加自己想要的處理。
正文 I ~ IV 部分 講解我理解macOS應用結構。
正文 V 部分 仿寫[NSApp run],添加自己想要的處理
二、正文
Ⅰ、我理解的應用
我把OS應用抽象出兩個概念:
1、視圖顯示(當然是對于有界面的應用)
2、事件處理機
Ⅱ、視圖顯示
先從窗口(NSWindow)開始談起。窗口可以分為標題頭,和內容。標題頭包括幾個通用按鈕和標題頭。分別可以通過如下方式進行設置:
NSWindowStyleMask style = NSWindowStyleMaskClosable |
NSWindowStyleMaskTitled |
NSWindowStyleMaskMiniaturizable |
NSWindowStyleMaskResizable;
//在窗口初始化的時候 設置窗口坐標和大小、窗口類型、緩沖類型、是否延遲創建窗口。
id window = [[NSWindow alloc]initWithContentRect :
NSMakeRect(10, 10, 100, 100)
styleMask : style
backing:NSBackingStoreBuffered
defer:NO];
//設置標題
[window setTitle:[NSString stringWithUTF8String : "hello world"]];
窗口被看成一種特殊的視圖,意思是它還是一個視圖,只不過它是基本視圖。
這也就意味著你把窗口全關了也不會導致應用的終止。
Ⅲ、事件處理機
一個應用的事件可以分為很多種。具體可以在NSEvent里面找到。
一個應用離不開事件的接收和處理。我覺的事件的最大作用體現在人機交互方面。鼠標、鍵盤等等輸入設備被相應的時候,會形成一個事件包,由底層逐步向應用層傳遞,并且存放在應用的事件隊列中。接下來我們應用層開發者只需要獲取到相應的事件,并處理響應它。這樣一個事件的生命就結束了。
理基本就這么一個理。至于代碼,就接著看吧。
Ⅳ、代碼實現
[NSApplication sharedApplication]
首先必須創建初始化一個應用對象。
應用的初始化的過程中,會創建一個事件池,用來存放事件。有了事件池還是不夠的,需要一個事件接收端口(port),用來接收從底層傳來的事件。(其實是從Window Server那傳來的)
接下來就創建一個窗口,假如你的應用需要窗口。
窗口的創建十分簡單,只要調用NSWindow 的 初始化方法就行了,在初始化方法中進行各種窗口設置。個人認為窗口在初始化時會自動與創建的應用進行關聯。不需要將它添加進NSApplication中。
同樣也可以給窗口添加一個其他視圖(NSView)
//設置window的內容視圖。
[window setContentView : cView];
//將該視圖設置為第一響應者。
[window makeFirstResponder : cView];
關于事件響應部分我也做了一點點的記錄
Cocoa Event Overview
萬事俱備,只欠一個run了。
調用[NSApp run],讓程序跑起來。這個方法里面會有一個循環,因為這個循環程序才會一直運行,不會結束。除非調用終止方法,才會有返回值。繼續進行之后的語句。
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
int main(int argc , const char* argv[])
{
[NSApplication sharedApplication];
//設置應用的顯示策略(ActivationPolicy)。可以在我的另一篇文章中找到具體說明。
[NSApp setActivationPolicy : NSApplicationActivationPolicyRegular];
NSWindowStyleMask style = NSWindowStyleMaskClosable |
NSWindowStyleMaskTitled |
NSWindowStyleMaskMiniaturizable |
NSWindowStyleMaskResizable;
id window = [[NSWindow alloc]initWithContentRect :
NSMakeRect(10, 10, 100, 100)
styleMask : style
backing:NSBackingStoreBuffered
defer:NO];
[window setTitle:[NSString stringWithUTF8String:"hello world"]];
//如下沒有這句話,會導致窗口不會顯示出來
[window makeKeyAndOrderFront : nil];
//使得應用打開時就獲取到用戶焦點
[NSApp activateIgnoringOtherApps:YES];
[NSApp run];
}
代碼中提及的顯示策略鏈接 Activation Policy
對于一個“正常”的應用來說這已經足夠了。但是如果想在程序的每次循環中干點自己想要的事,那么就需要寫一個類似[NSApp run]的循環,然后在每個循環中添加自己的處理。
Ⅴ、仿寫[NSApp run]
首先得有個循環,使得程序一直跑下去。用以下代碼,代替[NSApp run];
while(true)
{
PollEvents();
//do something you like . :D
}
之前說了事件是至關重要的,所以在循環中需要接收每個事件。然后對事件進行相應的處理。這樣就達到了[NSApp run]的基本要求。
void PollEvents()
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc]init];
for(;;)
{
NSEvent* event = [NSApp nextEventMatchingMask : NSEventMaskAny
untilDate : [NSDatedistantPast] inMode : NSDefaultRunLoopMode
dequeue:YES];
if(event == nil)
break;
[NSApp sendEvent : event];
}
[pool release];
}
三、傳送門
閱讀導向
筆者相關文檔
Cocoa 事件處理大綱 Event Overview
應用顯示策略Activation Policy