NSObject对象模型解析(下)

原创文章转载请注明出处,谢谢


这一节的主要内容是解析NSObject的一些类方法和实例方法,我会挑几个比较常用的方法入手来讲。我们知道NSObject实现了NSObject协议,所以NSObject协议里所有的@required的声明,在NSObject的源码里都是有实现的。类对象在OC中是一个单例的对象,所以类对象的内存地址是不会变的,基本上所有的实例方法都会有一个对应的类方法,只是有些类方法并没有被Public出来,所以我们没有感觉到。

接下来我们就开始具体用一些常用的方法来看看它们的具体实现。

self

+ (id)self {
    return (id)self;
}

- (id)self {
    return self;
}

上面这两个方法是平时使用频率非常高的方法了,那么它们到底是什么意思呢?对于一个类对象来讲self返回的其实是一个指向objc_class对象的指针的地址;对于一个实例对象来讲self返回的其实是一个指向objc_object对象的指针地址,这弄清楚这点对于后面所有方法的理解会很有帮助。

Class

+ (Class)class {
    return self;
}

- (Class)class {
    return object_getClass(self);
}

// Unit Test
BClass *clazz = [[BClass alloc] init];
NSLog(@"%p", clazz);
NSLog(@"%p", [BClass class]);
NSLog(@"%p", [clazz class]);

// Output
2016-06-25 14:33:55.405 InitDemo[8037:116623] 0x100105de0
2016-06-25 14:33:55.406 InitDemo[8037:116623] 0x100002788
2016-06-25 14:33:55.406 InitDemo[8037:116623] 0x100002788

上面这两个方法其实是返回一个指向objc_class的对象指针,从上面的单元测试可以看出它们两个返回的地址是一样的,因为实例对象的class方法其实是调用了object_getClass方法,返回的是isa指针指向的地址也就是BClass的类对象(也就是objc_object的isa指针),而BClass的类方法class返回的其实就是它自己,所以两个地址是相等的(参考上一节的图就可以明白了)。而单单clazz的objc_object首地址是和它们不相同的。

superclass

+ (Class)superclass {
    return self->superclass;
}

- (Class)superclass {
    return [self class]->superclass;
}

// Unit Test
BClass *clazz = [[BClass alloc] init];
NSLog(@"%p", [BClass superclass]);
NSLog(@"%p", [AClass class]);
NSLog(@"%p", [clazz superclass]);

// Output
2016-06-25 14:46:59.070 InitDemo[8108:121893] 0x100002748
2016-06-25 14:46:59.071 InitDemo[8108:121893] 0x100002748
2016-06-25 14:46:59.071 InitDemo[8108:121893] 0x100002748

这两个方法其实很容易理解,就是返回对象的superclass,无论是类对象还是实例对象,它们的superclass都是同一个对象。

isMemberOfClass

+ (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}

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

// Unit Test
BClass *clazz = [[BClass alloc] init];
NSLog(@"%@", [clazz isMemberOfClass:[BClass class]]? @"YES" : @"NO");
NSLog(@"%@", [BClass isMemberOfClass:object_getClass([BClass class])]? @"YES" : @"NO");

// Output
2016-06-25 15:07:25.487 InitDemo[8187:129155] YES
2016-06-25 15:07:25.488 InitDemo[8187:129155] YES

这个方法Public出来的只有实例方法,类方法是没有的。因为实例方法其实就是判断某个实例对象是否是某个类的实例,而类方法判断的其实是某个类对象是否是某个metaclass的类对象,因为其实正常是没有必要需要这个类方法去判断metaclass的,所以才没有public出来这个方法,这是我猜测的原因,大家可以看看单元测试很容易理解,当然这里需要自己定义一个isMemberOfClass类方法。

isKindOfClass

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

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

判断是否是这个类或者这个类的子类的实例
isKindOfClass也和isMemberOfClass一样,只public出来的实例方法,没有类方法,因为实例方法是去判断某个实例对象是否是某个类或者其子类的实例,而类方法就是去判断某个类对象是否是某个metaclass或者其子类的类对象,看起来很绕,其实很容易理解的。

isSubclassOfClass

+ (BOOL)isSubclassOfClass:(Class)cls {
    for (Class tcls = self; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

// Unit Test
NSLog(@"%@", [BClass isSubclassOfClass:[AClass class]]? @"YES" : @"NO");
NSLog(@"%@", [AClass isSubclassOfClass:[AClass class]]? @"YES" : @"NO");

// Output
2016-06-25 15:23:33.464 InitDemo[8258:135774] YES
2016-06-25 15:23:33.465 InitDemo[8258:135774] YES

这个方法是没有实例方法的,因为没有这个必要,它的作用就是判断某个类对象是否是某个类对象的子类或者本身,对于实例对象可以用isKindOfClass来完成。

总结

其实讲解析方法的部分主要目的是对前面的认知做一个深入的理解,所以我并有讲NSObject里其他的那些方法,只讲了这些和前面概念有关联的这部分,主要目的就是加深理解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,814评论 0 9
  • 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的...
    西木阅读 30,650评论 33 466
  • 1.1 什么是自动引用计数 概念:在 LLVM 编译器中设置 ARC(Automaitc Reference Co...
    __silhouette阅读 5,275评论 1 17
  • 文中的实验代码我放在了这个项目中。 以下内容是我通过整理[这篇博客] (http://yulingtianxia....
    茗涙阅读 949评论 0 6
  • 你会不会在某一时刻,在做某件事或是什么也不做的时候,因为突然想到某个东西而笑出来?可能会也可能不会,我的答案是我会...
    两勺蜂蜜阅读 243评论 0 2