[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`。
![](https://img.kancloud.cn/73/67/736752e0e4d381e892f08b1d0a359dcf_1200x520.png)
### 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 自制模型训练