| 角色 | 術語 | 型別 | 說明 |
| 接收者 | Target | id | 不確定型別,使用 id 通用 |
| 動作 | Action | SEL | 方法選擇器,編譯期解析為唯一 ID |
| 觸發者 | Control | 元件本身 | 負責在事件發生時呼叫 [target performSelector:action] |
SEL 型別與 @selector()
SEL action = @selector(methodName:); // 將方法名轉為 SEL
// 等價於 runtime 中的 sel_registerName("methodName:")
虛擬元件實戰:開關控制燈泡
元件介面定義
// Light.h
@interface Light : NSObject
- (void)turnOn;
- (void)turnOff;
@end
// Switch.h
typedef NS_ENUM(NSUInteger, SwitchState) {
SwitchStateOff, SwitchStateOn
};
@interface Switch : NSObject
@property (nonatomic, assign, readonly) SwitchState currentState;
- (void)addChangeStateTarget:(id)target action:(SEL)action;
@end
房間類別整合
// Room.h
@interface Room : NSObject @end
// Room.m
@interface Room ()
@property (nonatomic, strong) Light *aLight;
@property (nonatomic, strong) Switch *aSwitch;
- (void)switchStateChanged:(Switch *)sender; // Action 方法
@end
@implementation Room
- (instancetype)init {
self = [super init];
if (self) {
self.aLight = [[Light alloc] init];
self.aSwitch = [[Switch alloc] init];
// 關鍵:註冊 Target-Action
[self.aSwitch addChangeStateTarget:self
action:@selector(switchStateChanged:)];
}
return self;
}
- (void)switchStateChanged:(Switch *)sender {
if (sender.currentState == SwitchStateOff) {
[self.aLight turnOn];
} else {
[self.aLight turnOff];
}
}
@end
Target-Action 運作流程
- 使用者操作開關 → 開關內部狀態變更
- 開關呼叫
[target performSelector:action withObject:self]
- Runtime 根據 SEL 找到 target 的方法實作並執行
- Action 方法接收 sender 參數,透過 sender 取得狀態資訊
三種回調模式比較
| 維度 | Target-Action | 委託回調 | Block 回調 |
| 參數傳遞 | 單一 sender,其他靠屬性取得 | 協定定義豐富參數 | Block 簽名自訂參數 |
| 多動作區分 | 不同 Selector | 不同委託方法 | 不同 Block |
| 型別安全 | 低 (Selector 無參數檢查) | 高 (協定強制) | 高 (Block 簽名檢查) |
| 多播支援 | 支援 (可註冊多組) | 單播 (一個 delegate) | 單播 (一個 Block) |
| 程式碼局部性 | 分離 | 分離 | 高 (內聯) |
| 適用場景 | 簡單 UI 控制項、按鈕、手勢 | 複雜組件、資料源、多回調 | 非同步完成、動畫、簡單單一回調 |
實務注意事項
- Target 不被保留:
UIControl 對 target 為 weak 參考,需確保 target 生命週期 ≥ control
- Action 簽名規範:三種合法形式
- (void)action
- (void)action:(id)sender
- (void)action:(id)sender forEvent:(UIEvent*)event
- Selector 存在性:若 target 未實作 selector → Crash (unrecognized selector)
- 多目標註冊:同一事件可註冊多組 target-action,按註冊順序呼叫
進階:自訂控制項實作 Target-Action
// CustomControl.m
- (void)touchUpInside:(UITouch *)touch {
[self sendActionsForControlEvents:UIControlEventTouchUpInside];
// 內部會遍歷所有註冊的 target-action 並執行
}
結語
Target-Action 是 iOS/UIKit 事件處理的基石,以極簡介面 (id + SEL) 實現極致解耦。雖然參數傳遞能力較弱,但配合 sender 參數取得上下文資訊,足以應對絕大多數 UI 交互場景。掌握其註冊流程、Selector 機制、記憶體管理與三大回調模式選型邏輯,是 iOS 開發者的必修課。
- 上一篇文章: Objective-C 教程 3:類別擴展、類別延展與協定機制深度解析
- 下一篇文章: C 語言 printf() 函數完整指南:格式字串、轉換說明符、修飾符與實戰技巧
Copyright ©
程式碼之家 All Rights Reserved