Study harmoniously! Don’t be impatient!! I’m your old friend, Xiao Qinglong

As an iOS developer, we often use ==, isKindOfClass, isMemberOfClass, but do you know how to use them? And how is it implemented at the bottom? OK, let’s start with the case:

[friendship tip, the front of the source code analysis to dig a hole ~]

Explore – – – – – – – – – – – –= =

Create two classes first:

#pragma mark -- Create a Person class that inherits from Nsobject@interface Person: Nsobject@property (nonatomic,strong) NSString *name; - (void)eatFood;
@end

@implementation Person
-(void)eatFood{} @end #pragma mark -- create a Student class, Inherit from person@interface Student: Person@property (nonatomic,strong) NSString *age; - (void)run;
@end

@implementation Student
-(void)run{}
@end
Copy the code

Then compare:

#pragma mark -------- == compare ---------void compareWithDD(){
    NSObject *n1 = [[NSObject alloc] init];
    NSObject *n2 = [[NSObject alloc] init];
    
    Person *p1 = [[Person alloc] init];
    Person *p2 = [[Person alloc] init];
    
    Student *s1 = [[Student alloc] init];
    Student *s2 = [[Student alloc] init];
    / / the = = comparison
    NSLog(@"\n-------- == print --------");
    NSLog(@"n1 == n2 -->%d",n1 == n2);
    NSLog(@"p1 == p2 -->%d",p1 == p2);
    NSLog(@"s1 == s2 -->%d",s1 == s2);
    NSLog(@"");
    NSLog(@"n1.class == n2.class -->%d",n1.class == n2.class);
    NSLog(@"p1.class == p2.class -->%d",p1.class == p2.class);
    NSLog(@"s1.class == s2.class -->%d",s1.class == s2.class); } #pragma mark -- main entry intmain(int argc, char * argv[]){ compareWithDD(); . }Copy the code

Print result:

We find that when different objects of the same class are compared, == results in false. Why is that? Let’s print the addresses of n1, n2, and the values they point to.



Come to the conclusion:A comparison of the == symbol is a comparison of the addresses of two Pointers.

Explore – – – – – – – – – – – –isKindOfClass

# Pragma Mark -------- isKindOfClass comparison ---------void compareWithIsKindOfClass(void){
    NSObject *n1 = [[NSObject alloc] init];
    NSObject *n2 = [[NSObject alloc] init];
    Person *p1 = [[Person alloc] init];
    Student *s1 = [[Student alloc] init];
    Class objectClass = NSObject.class;
    Class studentClass = Student.class;
    / / isKindOfClass comparison
    NSLog(@"\n\n-------- isKindOfClass print --------");
    NSLog(@"n1 isKindOfClass n2.class -->%d",[n1 isKindOfClass:n2.class]);
    NSLog(@"n1 isKindOfClass p1.class -->%d",[n1 isKindOfClass:p1.class]);
    NSLog(@"n1 isKindOfClass s1.class -->%d",[n1 isKindOfClass:s1.class]);
    NSLog(@"p1 isKindOfClass s1.class -->%d",[p1 isKindOfClass:s1.class]);
    NSLog(@"objectClass isKindOfClass objectClass -->%d",[objectClass isKindOfClass:objectClass]);
    NSLog(@"studentClass isKindOfClass studentClass -->%d",[studentClass isKindOfClass:studentClass]); } #pragma mark -- main entry intmain(int argc, char * argv[]){ compareWithIsKindOfClass(); . }Copy the code

Print result:

We find that n1 and n2.class address are different, but the result is YES; NSObject and NSObject are compared and the result is true,Student and Student are compared and the result is false. Why is that? Next, let’s explore the underlying implementation of isKindOfClass. As usual, open objC4-818.2 source code, search “isKindOfClass”, find:

1. This is a for loop; If TCLS is equal to the ISA reference of self (i.e., the metaclass), if TCLS is equal to the CLS class passed in, return YES. If TCLS is equal to the CLS class passed in, return YES. If TCLS is equal to the parent of TCLS, return YES. If TCLS is equal to the parent of TCLS, return YES. CLS class = CLS class = CLS class = CLS class * /
+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}

1. This is a for loop; If TCLS is equal to the CLS class passed in, return YES. If TCLS is equal to the CLS class passed in, return YES. If TCLS is equal to the parent of TCLS, return YES. If TCLS is equal to the parent of TCLS, return YES. If TCLS is equal to the parent, return YES. 4. If NO CLS class is matched, return NO. * /
- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}

Copy the code

Back to the question

  • N1 and n2.class address is different, but the result is YES;
TCLS = [n1 class], same as n2.class, so return YES; N1 and n2.class addresses are different, but the result is YES.Copy the code
  • NSObject and NSObject are compared and the result is true,Student and Student are compared and the result is false.
TCLS = NSObject root metaclass, NSObject root metaclass is not equal to NSObject class; Second time --> TCLS = NSObject The root metaclass ISA points to (that is, NSObject), so return YES; TCLS = Student; TCLS = Student; TCLS = Student metaclass ISA (NSObject root metaclass); Third time --> TCLS = NSObject ISA reference to the root metaclass (that is, 'NSObject'), unequal; Fourth time --> TCLS = NSObject ISA pointing to the root metaclass (that is, 'nil'), loop ends, returns NO;Copy the code

Explore – – – – – – – – – – – –isMemberOfClass

# Pragma Mark -------- isMemberOfClass comparison ---------void compareWithIsMemberOfClass(void){
    NSObject *n1 = [[NSObject alloc] init];
    Person *p1 = [[Person alloc] init];
    Student *s1 = [[Student alloc] init];
    Class objectClass = NSObject.class;
    Class studentClass = Student.class;
    / / isMemberOfClass comparison
    NSLog(@"\n\n-------- isMemberOfClass print --------");
    NSLog(@"n1 isMemberOfClass NSObject.class -->%d",[n1 isMemberOfClass:NSObject.class]);
    NSLog(@"p1 isMemberOfClass NSObject.class -->%d",[p1 isMemberOfClass:NSObject.class]);
    NSLog(@"s1 isMemberOfClass NSObject.class -->%d",[s1 isMemberOfClass:NSObject.class]);
    NSLog(@"");
    NSLog(@"n1 isMemberOfClass Person -->%d",[n1 isMemberOfClass:Person.class]);
    NSLog(@"p1 isMemberOfClass Person -->%d",[p1 isMemberOfClass:Person.class]);
    NSLog(@"s1 isMemberOfClass Person -->%d",[s1 isMemberOfClass:Person.class]);
    NSLog(@"");
    NSLog(@"n1 isMemberOfClass Student -->%d",[n1 isMemberOfClass:Student.class]);
    NSLog(@"p1 isMemberOfClass Student -->%d",[p1 isMemberOfClass:Student.class]);
    NSLog(@"s1 isMemberOfClass Student -->%d",[s1 isMemberOfClass:Student.class]);
    NSLog(@"");
    NSLog(@"objectClass isMemberOfClass objectClass -->%d",[objectClass isMemberOfClass:objectClass]);
    NSLog(@"studentClass isMemberOfClass studentClass -->%d",[studentClass isMemberOfClass:studentClass]); } #pragma mark -- main entry intmain(int argc, char * argv[]){ compareWithIsMemberOfClass(); . }Copy the code

Print result:

Erase the underlying implementation –> OpenObjc4-818.2 -Source code, search”isMemberOfClass“, find:

/** Returns the comparison between the self metaclass and the CLS class */
+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}

/** Returns the comparison between self and CLS */
- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
Copy the code

As you can see, isMemberOfClass is similar to isKindOfClass, except that isMemberOfClass does not loop and is executed once.

Conclusion – – – – – – – – – – – –

  • If you're just comparing the values of a and b, use ==
  • For isMemberOfClass, if it is an instance method, it follows the getSuperclass inheritance chain. If it's a class method, it's an ISA pointing chain;
  • If you want to determine if instance object A is an instance object of class B, use isKindOfClass

Baidu cloud: objc4-818.2 source: pan.baidu.com/s/1qWbB4c7l… Password :vkh4 Demo link :pan.baidu.com/s/1mdlJXc_6… Password: 9 PDS

Note – – – – – – – – – – – –

Don’t rush off. You’ve got a hole to fill. After a meal of operation, there seems to be no problem, and the conclusion can also explain the printed results of our case. However, we know that the OC code goes down to the bottom and the final implementation is assembly, so we should double the insurance by opening assembly debugging first –> main method to add the test code:

int main(int argc, const char * argv[]){.../// Explore the underlying isKindof implementation
    BOOL res = [NSObject.class isKindOfClass:NSObject.class];
    NSLog(@"%d",res); .return 0;
}
Copy the code

In the BOOL res = [NSObject class isKindOfClass: NSObject. Class]; Xcode -> Debug -> Debug Workflow -> Always show Disassembly Run the code to see:





What a shock to my little friend! The underlying implementation is not isKindOfClass:



OK, let’s search objc source codeobjc_opt_isKindOfClass“, find:

// 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);
}

CLS = obj = isKindOf (); CLS = obj = isKindOf (); If TCLS is equal to the otherClass passed in, it returns YES. If TCLS is equal to the otherClass passed in, it returns YES. If TCLS is equal to the parent of TCLS, return YES if TCLS is equal to the otherClass passed in. OtherClass = otherClass = otherClass = otherClass * /
Copy the code

Blah blah – – – – – – – – – – – –

In fact, xiaobian also took it for granted at the beginning, the bottom is isKindOf:. After all, in objC source engineering, there does exist isKindOf: class method and instance direction, according to the ISA pointing chain and class inheritance chain can really explain the general, until was -> library manager a sentence denied the analysis idea, I clearly remember his old man that “to dare to question everything”. The last or honest go assembly debugging.