一些新手常常搞混的東西
bool與BOOL
- 在 64 位操作系統上,OC 的
BOOL
會直接等于定義的stdbool.h
里面的bool
,其實就是int
,但如果在 32 位操作系統上,BOOL
就會被定義成是一個char
,而BOOL
與bool
,就分別是一個byte
或是四個bytes
的差別。- 所以,在 64 位操作系統上,
BOOL
與bool
并沒有區別,但我們不能確定我們寫的代碼只會在這種環境下執行,當然,在其他環境下應該也沒什么影響。
NULL
nil
Nil
NSNull
NSNotFound
- NULL
NULL
其實并不算是 OC 的東西,而是屬于 C 語言。NULL
就是 C 語言當中的空指針,是指向 0 的指針。nil
、Nil
與NULL
可以代替使用,但在語意上,當某個 API 想要傳入某個指針(void *)
時,而不是id
類型時,雖然你可以在這種狀態下也可以傳入 OC 對象指針nil
,但是傳入NULL
意義會比較清楚。- 總結:
id
用nil
,(void *)
用NULL
。
- nil
- 是空的 OC 對象指針,也一樣是指向 0.如果我們創建了一個 OC 對象的變量,當我們不想要使用這個對象的時候,便可以將這個變量指向
nil
;我們可以對nil
調用任何的 OC 對象,都不會產生問題。- 在
NSArray
和NSDictionary
中使用nil
,會被當成是最后一個參數,出現在nil
之后的參數都會被忽略。另外在對字典和數組插入nil
的時候,程序會崩潰。
- Nil
nil
是空的實例,而開頭大寫的Nil
則是指空的類。當我們要判斷某個類是不是空的,語意上應該用Nil
而不是nil
。- 比較可能的應用場合,就是判斷在新的 iOS 系統上出現的新類,如果無法向下兼容,則執行其他代碼。
Class cls = NSClassFromString(@"Abcdefg");
if (cls != Nil) {
// Do something.
}
- NSNull
NSNull
是 OC 對象,在數組和字典中不可以插入nil
,但可以通過插入NSNull
對象表示沒有東西。- 在 JSON 文件里,轉換成 OC 對象時,JSON 里面的
null
則會轉變成NSNull
對象。
- NSNotFound
NSNotFound
所代表的是找不到這個東西的index
。NSNotFound
是整數的最大值,通常不會建立這么大的數組。在 64 位操作系統和 32 位操作系統上整數的最大值是不一樣的。如下面這段代碼,在 64 位操作系統下是有問題的:int x = [@[@1, @2, @3] indexOfObject:@4];
if (x != NSNotFound) {
NSLog(@"Found!");
}
* 該代碼中,`NSNotFound`在 64 位操作系統上整數的最大值,但 `x` 被轉變成 32 位整數的最大值,所以`x`就無法等于`NSNotFound`了。
Responder(響應者)
事件的傳遞
- 傳遞過程:
- 硬件把事件傳到我們的 App 中,交由
UIApplication
對象分配事件UIApplication
把事件傳送到key Window
中,接著由key Window
負責分派事件key Window
開始尋找在視圖層次中最上面的控制器與視圖,然后發現在上面的視圖是我們的按鈕- 觸發按鈕的點擊事件。(如果沒有事件處理,事件又會回到
application
上)![]()
- 從
application
到window
到view
,每一層中可以處理事件的對象,都叫做響應者。最終處理事件的對象,叫做第一響應者,而這種事件一層層傳遞的尋找處理事件的鎖鏈,叫做響應者鏈條。Run loop
main.m
文件里面的做法:
- 建立
auto-release pool
- 調用
UIApplicationMain
,創建UIApplication
單利對象- 執行
run loop
- 調用
UIApplication
代理方法Application
- 硬件事件會被傳遞到
window
上,而其他系統事件,包括軟件的開啟和關閉,前臺和后臺等,都有轉發給application
的代理方法中。- 由于
application
位于相應者鏈條的最底層,每個視圖與window
都不處理的時候,才會對給application
處理,所以如果我們希望處理一些會影響整個App
行為的事情時,就會由application
這一層處理。- 通過藍牙耳機或數據線上的按鈕切換歌曲,在 iOS7.1 之前,是通過在
application
代理方法中實現remoteControlReceivedWithEvent:
方法,- (void)remoteControlReceivedWithEvent:(UIEvent *)theEvent
{
if (theEvent.type == UIEventTypeRemoteControl) {
switch(theEvent.subtype) {
case UIEventSubtypeRemoteControlPlay:
break;
case UIEventSubtypeRemoteControlPause:
break;
case UIEventSubtypeRemoteControlStop:
break;
case UIEventSubtypeRemoteControlTogglePlayPause:
break;
case UIEventSubtypeRemoteControlNextTrack:
break;
case UIEventSubtypeRemoteControlPreviousTrack:
break;
... default:
return;
}
}
}
@end
* 當然,如果想要開始接收來自耳機的事件,還要對`application`單利對象調用`beginReceivingRemoteControlEvents`方法。 * 在 iOS7.1以后,推出來`MPRemoteCommandCenter`這個類。之前想要開始播放,會在`remoteControlReceivedWithEvent:`里面處理`UIEventSubtypeRemoteControlPlay`的狀態。現在會改成向`MPRemoteCommandCenter`要求`playCommand`,然后指定`target/action`,如下: ```swift [[MPRemoteCommandCenter sharedCommandCenter].playCommand addTarg
et:self action:@selector(play:)];
####Window * `application`在收到觸控等硬件事件之后,會把事件轉發給`key window`。 * 自己創建一個`window`對象,調用`makeKeyAndOrderFront`方法,顯示該`window`,`makeKeyAndOrderFront`方法不但會讓這個`window`顯現,同時也會使該對象成為`key window`,所有的事件都會往這個`window`送,所以,如果該`wondow`使用完畢,必須對原來的`key window`再調用一次`makeKeyAndOrderFront`,把事件處理的權限交還回去。 ####View * `application`通過`sendEvent:`將事件送到`window`,`window`也一樣通過`sendEvent:`將事件送到`view`上,而`view`里面,則是通過`hitTest:withEvent:`方法,在一層又一層的子視圖中查找應該處理事件的子視圖。 ####View Controller `view Controller`本身也是個相應者,因此也實現了`UIResponder`協議。當觸控事件發生的時候,如果某個控制器的視圖都不處理傳來的事件,那么就會轉向詢問這個視圖的控制器本身是否處理這個事件。 ####UITouch * 起初是個非常單純的對象,頂多只會使用`locationInView:`判斷觸控事件發生在視圖的哪個位置上,用`tapCount`知道觸屏了幾下,用`timestamp`知道觸摸的時間。 * iOS9添加了一些新的接口`coalescedTouchesForTouch`,獲取比往常一輪`run loop`收到一次`touch`對象更快的刷新頻率的觸控事件對象,使反應更靈敏。 * `predictedTouchesForTouch:`預測下一個觸控事件可能出現的位置,讓畫面看起來即時更新。