[toc]
開發中經常會使用 `isKindOfClass` 判斷對象是否是某個類或者是其父類(整個繼承鏈上的類),很少會用到 `isMemberOfClass` ,本文就從源碼層面來探索他們之間的關系。
## 一、準備
```objectivec
// DZPerson繼承自NSObject
@interface DZPerson : NSObject
@end
#import <Foundation/Foundation.h>
#import "DZPerson.h"
#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; // 1
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; // 0
BOOL re3 = [(id)[DZPerson class] isKindOfClass:[DZPerson class]]; // 0
BOOL re4 = [(id)[DZPerson class] isMemberOfClass:[DZPerson class]]; // 0
NSLog(@"\n re1:%hhd re2:%hhd re3:%hhd re4:%hhd",re1,re2,re3,re4);
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; // 1
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; // 1
BOOL re7 = [(id)[DZPerson alloc] isKindOfClass:[DZPerson class]]; // 1
BOOL re8 = [(id)[DZPerson alloc] isMemberOfClass:[DZPerson class]]; // 1
NSLog(@"\n re5:%hhd re6:%hhd re7:%hhd re8:%hhd",re5,re6,re7,re8);
}
return 0;
}
/**
打印:
re1:1 re2:0 re3:0 re4:0
re5:1 re6:1 re7:1 re8:1
*/
```
## 二、源碼分析
上述代碼其實調用了四個方法:
>類方法:
+isKindOfClass
+isMemberOfClass
>對象方法:
-isKindOfClass
-isMemberOfClass
### 1. +isKindOfClass 源碼
```objectivec
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
```
- **Class tcls = object_getClass((id)self);**
從源碼可以看到,`self` 是類本身,`object_getClass((id)self)` 則是獲取 `isa`,而 `isa` 是指向`元類`的,所以 `tcls` 實際上是當前類的元類。
- **for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass)**
- for循環實際上就是從`當前類的元類`開始,沿著繼承鏈中的 `superclass` 一直向上循環,在如下 `isa`指向圖 中標注部分,`NSObject`元類 的父類是 `NSObject`。所以在第二次循環的時候,`NSObject`元類 的 `superclass` 是本身`NSObject`。
- 但是 `DZPerson`元類 的繼承鏈是`DZPerson元類 -> NSObject元類 -> NSObject`,所以在 `DZPerson`元類 的繼承鏈上永遠不會有自身`DZPerson`。
- 因此 `[(id)[NSObject class] isKindOfClass:[NSObject class]] = YES` ,而 `[(id)[DZPerson class] isKindOfClass:[DZPerson class]] == NO`。

### 2. +isMemberOfClass 源碼
```objectivec
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
```
- 從源碼中可以看到,代碼是直接判斷當前`類的元類`是否等于傳入類。
- 所以 `[(id)[NSObject class] isMemberOfClass:[NSObject class]]` 和 `[(id)[DZPerson class] isMemberOfClass:[DZPerson class]]`中,`NSObject`元類 不等于 `NSObject`,`DZPerson`元類 也不等于 `DZPerson`,結果自然都是 `NO`。
### 3. -isKindOfClass 源碼
```objectivec
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
```
我們可以看到,對象方法的 for循環 初始值 變成了 [self class],也就是從當前類開始找superclass繼承鏈。
所以 [(id)[NSObject alloc] isKindOfClass:[NSObject class]] 和 [(id)[DZPerson alloc] isKindOfClass:[DZPerson class]] 都為 YES。
### 4. -isMemberOfClass 源碼
```objectivec
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
```
- `-isMemberOfClass` 對象方法更是簡單了,直接就是判斷`當前類`和傳入類是否相等。
- `[(id)[NSObject alloc] isMemberOfClass:[NSObject class]]` 和 `[(id)[DZPerson alloc] isMemberOfClass:[DZPerson class]]` 自然都是 `YES`。
## 三、總結
- `+isKindOfClass` 類方法是從`當前類的isa指向` (也就是當前類的元類) 開始,沿著 `superclass` 繼承鏈查找判斷和對比類是否相等。
- `-isKindOfClass` 對象方法是從 `[self class]` (當前類) 開始,沿著 `superclass` 繼承鏈查找判斷和對比類是否相等。
- `+isMemberOfClass` 類方法是直接判斷當前`類的isa指向` (也就是當前類的元類) 和對比類是否相等。
- `-isMemberOfClass` 對象方法是直接判斷 `[self class]` (當前類) 和對比類是否相等。
- 前言
- WebRTC知識集
- iOS 集成WebRTC各知識點小集
- iOS WebRTC集成時遇到的問題總結
- WebRTC多人音視頻聊天架構及實戰
- iOS端 使用WebRTC實現1對1音視頻實時通話
- iOS 基于WebRTC的點對點音視頻通信 總結篇
- WebRTC Native 源碼導讀 - iOS 相機采集實現分析
- OC 底層原理
- OC runtime 運行時詳解
- GCD dispatch_queue_create 創建隊列
- iOS底層 Runtime深入理解
- iOS底層 RunLoop深入理解
- iOS底層 Block的本質與使用
- iOS內存泄漏
- iOS中isKindOfClass和isMemberOfClass
- 從預編譯的角度理解Swift與Objective-C及混編機制
- 移動支付集成
- iOS 微信支付集成及二次封裝
- iOS 支付寶支付 Alipay集成及二次封裝
- iOS Paypal 貝寶支付集成及二次封裝
- iOS 微信、支付寶、銀聯、Paypal 支付組件封裝
- iOS 微信、支付寶、銀聯支付組件的進一步設計
- iOS 組件化
- iOS 組件化實施過程
- iOS 組件化的二進制化
- 使用pod package打包framework 實現組件的二進制化
- iOS 自制Framework 獲取指定bundle并讀取里面的資源
- .podSpec文件相關知識整理
- 開發并上傳靜態庫到CocoaPods
- pod引用第三方庫的幾種方式
- 如何在.podspec 文件中添加對本地庫的依賴
- lipo 命令合并真機與模擬器生成的framework
- iOS多線程
- NSOperation相關知識點
- 自定義NSOperation
- ios多個網絡請求之間的并行與串行場景的處理
- iOS動畫
- ios animation 動畫學習總結
- CABasicAnimation使用總結
- UITableView cell呈現的動效整理
- CoreAnimation動畫使用詳解
- iOS音視頻開發
- iOS 音視頻開發之AVCaptureMetadataOutput
- iOS操作本地視頻 - 獲取,壓縮,取第一幀
- 使用 GPUImage 實現一個簡單相機
- 直播App架構及思維導圖
- 如何快速的開發一個完整的iOS直播app
- iOS視頻拖動預覽及裁剪
- iOS 直播流程概述
- iOS直播:評論框與粒子系統點贊動畫
- iOS音視頻開發 - 采集
- 基于AVFoundation實現視頻錄制的兩種方式
- Swift知識集
- Swift 的枚舉、結構體和類詳解
- Swift 泛型詳解
- Swift屬性的包裝器@PropertyWrapper
- SwiftHub項目 之網絡層封裝的一點見解
- Moya+RxSwift+HandyJson 實現網絡請求及模型轉換
- Swift開發小記(含面試題)
- RxSwift 入坑手冊 - 基礎概念
- 理解 Swift 中的元類型:.Type 與 .self
- Swift HandyJSON庫中的類型相互轉換的實現
- Swift 中使用嵌套結構體定義一組相關的常量
- Swift Type-Erased(類型擦除)
- Swift中的weak和unowned關鍵字
- Swift 中的錯誤處理
- Swift中的Result 類型的簡單介紹
- Swift Combine 入門導讀
- Swift CustomStringConvertible 協議的使用
- 跨平臺
- Cordova跨平臺方案 iOS工程創建的步驟
- 使用Cordova 打包WebApp為原生應用詳解 (加殼封裝)
- RAC響應式編程
- 快速上手ReactiveCocoa之基礎篇
- RAC ReactiveCocoa 使用小集
- 優雅的 RACCommand
- 三方庫集成及使用
- 融云IM iOS sdk 集成 一篇就夠了
- iOS YYTextView使用筆記
- iOS YYLabel使用筆記
- iOS 蘋果集成登錄及蘋果圖標的制作要求
- iOS 面向切面編程 Aspects 庫的使用
- VKMsgSend庫對oc runtime的封裝
- OC Protocol協議分發器
- iOS 高德地圖實現大頭針展示,分級大頭針,自定制大頭針,在地圖上畫線,線和點共存,路線規劃(駕車路線規劃),路線導航,等一些常見的使用場景
- 工作總結
- 自定義UINavigationBar 適配iOS11, iOS15的問題
- SFSafariViewController 加載的網頁與原生oc之間的交互
- UICollectionView 設置header的二種方法
- UIPanGestureRecognizer進行視圖滑動并處理手勢沖突
- OC與Swift混編 注意事項
- UICollectionView 設置水平滑動后,調整每個Item項的排列方式
- oc 下定義字符串枚舉
- 高性能iOS應用開發中文版讀書筆記
- iOS 圖集滑動到最后時添加“顯示更多”效果的view組件 實現
- CocoaPods 重裝
- WKWebview使用二三事
- IOS電商首頁如何布局
- iOS中的投屏方案
- CGAffineTransform 介紹
- 用Block實現鏈式編程
- iOS 本地化簡明指南
- iOS 檢查及獲取相機、麥克風、相冊、位置等權限
- iOS 手勢UIGestureRecognizer詳解
- ios 編譯時報 Could not build module xxx 的解決方法嘗試
- iOS 常見編譯報錯及解決方案匯總(持續更新)
- AVMakeRectWithAspectRatioInsideRect 的使用
- graphhopper-ios 編譯過程詳解
- 算法
- iOS實現LRU緩存
- 架構
- IOS項目架構
- 其他雜項
- 推薦一個好用的Mac精品軟件下載站
- 如何能成為一位合格的職業經理人
- 零基礎怎么學習視頻剪輯?這篇初剪輯學者指南你一定不要錯過
- 免費SSL證書的制作
- 《一部手機拍全景》匯總課
- Linux下JAVA常用命令大全
- 即時通訊
- 通訊協議與即時通訊雜談
- 簡述移動端IM開發的那些坑:架構設計、通信協議和客戶端
- 基于實踐:一套百萬消息量小規模IM系統技術要點總結
- PaddleOCR 文字識別深度學習
- PaddleOCR mac 安裝指南
- PaddleOCR 標注工具PPOCRLabel的使用
- PaddleOCR 更換模型
- PaddleOCR 自制模型訓練