運行時獲取對象類型信息的能力稱為內省。Objective-C 作為動態語言,提供豐富的內省 API,是實現插件架構、序列化、依賴注入、KVO/KVC 的基石。
- (BOOL)isMemberOfClass:(Class)cls;
// 只有對象是 cls 的直接實例才返回 YES
// 不考慮繼承關係
- (BOOL)isKindOfClass:(Class)cls;
// 對象是 cls 或其子類的實例返回 YES
// 等價於:object_getClass(obj) 在 cls 的繼承鏈上
@interface Person : NSObject
@property NSString *name;
@end
@interface Teacher : Person
- (void)teach;
@end
Teacher *t = [[Teacher alloc] init];
[t isMemberOfClass:[Teacher class]]; // YES
[t isMemberOfClass:[Person class]]; // NO ← 關鍵差異
[t isMemberOfClass:[NSObject class]]; // NO
[t isKindOfClass:[Teacher class]]; // YES
[t isKindOfClass:[Person class]]; // YES ← 繼承鏈向上
[t isKindOfClass:[NSObject class]]; // YES
- (BOOL)respondsToSelector:(SEL)sel;
// 檢查對象是否實現該實例方法 (含繼承)
[t respondsToSelector:@selector(teach)]; // YES
[t respondsToSelector:@selector(setName:)]; // YES (繼承自 Person)
[t respondsToSelector:@selector(nonExistent)]; // NO
+ (BOOL)instancesRespondToSelector:(SEL)sel;
// 檢查類的實例是否響應該選子 (類方法)
[Teacher instancesRespondToSelector:@selector(teach)]; // YES
[Person instancesRespondToSelector:@selector(teach)]; // NO
// 注意:不能在實例上調用,必須用類對象
// id 定義
typedef struct objc_object *id;
// 等價於 void* 但帶有 Objective-C 對象語義
id obj = [[Teacher alloc] init];
[obj setName:@"張三"]; // 編譯器不檢查方法存在性
[obj teach]; // 運行時動態分發
// 相當於 C++ 的 void* + 手動類型擦除,但語言級支持
| 特性 | id (動態) | Teacher* (靜態) |
|---|---|---|
| 編譯期檢查 | 無 (任意消息) | 有 (方法簽名) |
| 運行期行為 | 動態分發 | 動態分發 (同樣) |
| 適用場景 | 容器、代理、回調、異構集合 | 明確類型的業務邏輯 |
| 性能 | 極微小開銷 (imp查找) | 相同 |
Class cls = [Teacher class]; // 類對象
Class meta = object_getClass(cls); // 元類
BOOL isMeta = class_isMetaClass(meta); // YES
// 類方法其實是元類的實例方法
[Teacher respondsToSelector:@selector(alloc)]; // YES (NSObject元類實現)
if ([obj conformsToProtocol:@protocol(Drawable)]) {
[(id)obj draw];
}
NSArray *strings; // 編譯期類型提示,運行期擦除
Any/AnyObject 對應 id,as? 對應 isKindOfClassObjective-C 的動態類型系統 (id, Class, SEL, 內省 API) 是其表達力核心。理解 isKindOfClass (繼承鏈) vs isMemberOfClass (精確類型)、實例 vs 類方法檢查、id 的類型擦除本質,能寫出更靈活、可擴展的 Cocoa 代碼。現代開發建議:接口用 Protocol、內部用具體類型、邊界用 id、避免手動類型檢查,讓編譯器和運行時協作完成類型安全。