# 08.isKindOfClass的底层实现

```cpp
BOOL result1 = [[NSObject class] isKindOfClass:[NSObject class]];
BOOL result2 = [[NSObject class] isMemberOfClass:[NSObject class]];
BOOL result3 = [[RYModel class] isKindOfClass:[RYModel class]];
BOOL result4 = [[RYModel class] isMemberOfClass:[RYModel class]];

NSLog(@"Class\n %hhd \n %hhd \n %hhd \n %hhd", result1, result2, result3, result4);

BOOL result5 = [[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL result6 = [[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL result7 = [[RYModel alloc] isKindOfClass:[RYModel class]];
BOOL result8 = [[RYModel alloc] isMemberOfClass:[RYModel class]];

NSLog(@"Instacne\n %hhd \n %hhd \n %hhd \n %hhd", result5, result6, result7, result8);
```

思考一下，会输出什么？

结果：

```cpp
2021-06-27 08:29:10.567002+0800 IsKindOf[62355:691675] Class
 1 
 0 
 0 
 0
2021-06-27 08:29:10.567836+0800 IsKindOf[62355:691675] Instacne
 1 
 1 
 1 
 1
```

如果仅从字面上去理解代码，那么你的答案应该会是不一样的的。我们从源码层面来理解一下吧，源码不长，很容易理解。

## 一、isKindOfClass

```cpp
+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}
```

### 1.1 流程图-Class

![Class-isKindOfClass](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MI8JgbGh3U6X_oedqkm%2Fsync%2F5e5399bb81d101d165bb4832cbcfcb3640676cde.png?generation=1624756609805584\&alt=media)

* \[NSObject class] 即 `RootClass`
  * `RootClass` -(ISA)> `RootMetaClass` -(superClass)> `RootClass`
  * true
* \[RYModel class]
  * `[RYModel class]` -(ISA)> `RYModelMetaClass` -(superClass)> `RootMetaClass` -(superClass)> `RootClass` -(superClass)> `nil`
  * false

### 1.2 流程图-Instance

![Instance-isKindeOfClass](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MI8JgbGh3U6X_oedqkm%2Fsync%2Fac5879c9a711763ad925a51c52884a8f22960cb0.png?generation=1624756608712419\&alt=media)

* NSObjectInstance
  * `NSObjectInstance` -(class)> `RootClass`
  * true
* RYModelInstance
  * `RYModelInstance` -(class)> `RYModelClass`
  * true

## 二、isMemberOfClass

```cpp
+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
```

这里就很简单了，就不展开说了。

## 三、你以为这就完了？

> 如果你看到这里觉得没啥问题？那就有问题了。。。

我们运行下OC源码，两个断点。

![IsKindofClass01](https://github.com/RyukieSama/RyukieDevGitBook/tree/3fa18668f891626268e2f78624a16b279bba216e/iOS/%E5%BA%95%E5%B1%82%E7%9F%A5%E8%AF%86%E6%A2%B3%E7%90%86/Images/IsKindofClass01.png)

运行之后发现 `+/- (Bool)isKindOfClass:(Class)cls` 居然根本没有调用。

### 3.1 汇编

通过断点汇编，我们在调用 `isKindOfClass` 的地方发现了这样的指令 `objc_opt_isKindOfClass`

```cpp
->  0x10000381c <+332>: mov    rdi, qword ptr [rip + 0x4f7d] ; (void *)0x0000000100008808: RYModel
    0x100003823 <+339>: call   0x100003b42               ; symbol stub for: objc_opt_class
    0x100003828 <+344>: mov    rbx, rax
    0x10000382b <+347>: mov    rdi, qword ptr [rip + 0x4f6e] ; (void *)0x0000000100008808: RYModel
    0x100003832 <+354>: call   0x100003b42               ; symbol stub for: objc_opt_class
    0x100003837 <+359>: mov    rdi, rbx
    0x10000383a <+362>: mov    rsi, rax
    0x10000383d <+365>: call   0x100003b48               ; symbol stub for: objc_opt_isKindOfClass
```

### 3.2 objc\_opt\_isKindOfClass

```cpp
// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
    // 判空
    if (slowpath(!obj)) return NO;
    Class cls = obj->getIsa();
    if (fastpath(!cls->hasCustomCore())) {
        for (Class tcls = cls; tcls; tcls = tcls->getSuperclass()) {
            if (tcls == otherClass) return YES;
        }
        return NO;
    }
#endif
    // 这里可以忽略
    return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
```

在这里下断点，我们发现的确进到了这里。并且 `Class` 和 `Instance` 的方法都走到了这里。

![IsKindOfClass02](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MI8JgbGh3U6X_oedqkm%2Fsync%2F641331546d5f45409e5b838d20fbb59a6e722813.png?generation=1624758760634212\&alt=media)

编译器对这里进行的优化，用更高效的 `objc_opt_isKindOfClass` 代替了原有的方法。

## 四、参考

> [slowpath & fastpath](https://ryukiedev.gitbook.io/wiki/ios/di-ceng/09.slowpath-and-fastpath)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ryukiedev.gitbook.io/wiki/ios/di-ceng/08.iskindofclass.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
