日常の開発では-isKindOfClass
、オブジェクトがクラスであるかその親クラス(継承チェーン上のクラス)であるかを判断するためによく使用されますが、使用されることはめったにありません-isMemberOfClass
。それらの違いは何ですか。この記事ではobjc源码
常にそれらの違いを分析します。
1.ケース
で新しいクラスを作成し(この記事objc源码
を参照してください)、から継承し、実装を空にし、次のように関数を変更します。Person
NSObjec
main
// main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
Class objCls = [NSObject class];
BOOL ret1 = [(id)objCls isKindOfClass:[NSObject class]];
BOOL ret2 = [(id)objCls isMemberOfClass:[NSObject class]];
NSLog(@"\nret1 = %d\nret2 = %d", ret1, ret2);
Class pCls = [Person class];
BOOL ret3 = [(id)pCls isKindOfClass:[Person class]];
BOOL ret4 = [(id)pCls isMemberOfClass:[Person class]];
NSLog(@"\nret3 = %d\nret4 = %d", ret3, ret4);
NSObject *obj = [[NSObject alloc] init];
BOOL ret5 = [obj isKindOfClass:[NSObject class]];
BOOL ret6 = [obj isMemberOfClass:[NSObject class]];
NSLog(@"\nret5 = %d\nret6 = %d", ret5, ret6);
Person *person = [[Person alloc] init];
BOOL ret7 = [person isKindOfClass:[Person class]];
BOOL ret8 = [person isMemberOfClass:[Person class]];
NSLog(@"\nret7 = %d\nret8 = %d", ret7, ret8);
}
return 0;
}
复制代码
プログラムを実行した後のコンソール出力は次のとおりです。
2020-02-08 20:27:45.681409+0800 objc-debug[1378:33202]
ret1 = 1
ret2 = 0
2020-02-08 20:27:45.682017+0800 objc-debug[1378:33202]
ret3 = 0
ret4 = 0
2020-02-08 20:27:45.682176+0800 objc-debug[1378:33202]
ret5 = 1
ret6 = 1
2020-02-08 20:27:45.682305+0800 objc-debug[1378:33202]
ret7 = 1
ret8 = 1
Program ended with exit code: 0
复制代码
この結果は少し奇妙に見えるかもしれませんので、見てください...
第二に、ソースコードの解釈
上記のコードは、実際には4つのメソッド、クラスメソッドを呼び出します。
+isKindOfClass:
+isMemberOfClass:
およびインスタンスメソッド:
-isKindOfClass:
-isMemberOfClass:
1+isKindOfClass:
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
复制代码
object_getClass(self)
つまりself->isa
、ソースコードからわかるように、[NSObject class]
それはNSObject
クラスそのものであり、そのisa
方向はであり、から継承される継承チェーンにNSObject元类
沿ってNSObject元类
段階的に判断されます。したがって、NSObject元类
NSObject
BOOL ret1 = [(id)objCls isKindOfClass:[NSObject class]]
复制代码
このコード行の判断パスはNSObject元类
->NSObject
であり、これはそれ自体と同じであるため、ret1 = YES
。
Person->isa
はいPerson元类
、Person元类
から継承されNSObject元类
、からNSObject元类
継承されNSObject
、その後
BOOL ret3 = [(id)pCls isKindOfClass:[Person class]];
复制代码
このコード行の判断パスはPerson元类
-> NSObject元类
-> NSObject
、つまりPerson != NSObject
、ret3 = NO
です。
2+isMemberOfClass:
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
复制代码
直接判断のポイントがisa
入力パラメータと等しいかどうかはNSObject元类
当然等しくないNSObject
ので、ret2 = NO
。、にPerson元类
等しくありません。Person
ret4 = NO
3-isKindOfClass:
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
复制代码
これとクラスメソッドの唯一の違いは、ループ条件の初期値がではtcls = [self class]
なく、であることです。tcls = object_getClass(self)
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
复制代码
class
メソッドは、メソッドとインスタンスメソッドも分類します。クラスメソッドは直接self
戻り、インスタンスメソッドはself->isa
ポインタを返します。ここでは、インスタンスオブジェクトを使用して呼び出しているため、インスタンスメソッドが実行され-class
、実際にはクラスメソッドの実装と同じになり+isKindOfClass:
ます。
BOOL ret5 = [obj isKindOfClass:[NSObject class]];
复制代码
obj->isa
つまりNSObject
、このコード行の判断パスはNSObject
->nil
であり、最初のステップは、をヒットすることret5 = YES
です。
BOOL ret7 = [person isKindOfClass:[Person class]];
复制代码
person->isa
つまりPerson
、このコード行の判断パスはPerson
-> NSObject
->nil
です。ret7 = YES
4-isMemberOfClass:
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
复制代码
それは、それがself->isa
入力パラメータと等しいかどうかを判断することです。
BOOL ret6 = [obj isMemberOfClass:[NSObject class]];
复制代码
このコード行の翻訳は、実際にはobj->isa
== NSObject
、ret6 = YES
です。
BOOL ret8 = [person isMemberOfClass:[Person class]];
复制代码
同様に、person->isa
== Person
、。ret8 = YES
3.まとめ
+isKindOfClass:
合計はポインタ-isKindOfClass:
から判断され、それらのいずれかが入力パラメータと等しくなるまで継承チェーンをたどり、その後、を返します。そして、それが入力パラメータと等しいかどうかを直接判断します。最後に、この古典的なポインティングと継承の関係図を配置します。self->isa
self->isa
NSObject
YES
+isMemberOfClass:
-isMemberOfClass:
self->isa
isa