美文网首页
ObjC 学习笔记(六):NSObject协议

ObjC 学习笔记(六):NSObject协议

作者: zevwings | 来源:发表于2019-07-21 12:08 被阅读0次

在阅读了类的结构、属性、方法交换、对象关联之后,我们可以看到,这些所有的所有操作都与object相关,在我们开发过程中,我们也可以看到很多类都继承自NSObject,比如:UIResponderUINavigationItem等。

我们接下来看看NSObject的代码有些什么内容呢?

首先是NSObject协议,包含了NSObject相关的抽象,都是我们平常会使用到的一些方法。

@protocol NSObject

- (BOOL)isEqual:(id)object;
@property (readonly) NSUInteger hash;

@property (readonly) Class superclass;
- (Class)class OBJC_SWIFT_UNAVAILABLE("use 'type(of: anObject)' instead");
- (instancetype)self;

- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

- (BOOL)isProxy;

- (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)isMemberOfClass:(Class)aClass;
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;

- (BOOL)respondsToSelector:(SEL)aSelector;

- (instancetype)retain OBJC_ARC_UNAVAILABLE;
- (oneway void)release OBJC_ARC_UNAVAILABLE;
- (instancetype)autorelease OBJC_ARC_UNAVAILABLE;
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;

- (struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;

@property (readonly, copy) NSString *description;
@optional
@property (readonly, copy) NSString *debugDescription;

@end    

NSObject协议定义了Objective-C对象公用的相关方法,我们大多时候都会将NSObject作为我们的基类, 其实我们也可以使用NSObject自己实现一个基类,现在我们跟着NSObject协议去看看NSObject怎么实现的基类。

首先我们看看对象相关的方法。

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

- (id)self {
    return self;
}

+ (Class)class {
    return self;
}

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

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

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

这些方法方法我们可以看出来都是获取对象自己及父类class相关的实现。我们先定义一个SubClass来输出这几个方法获取的结果。

@interface SubClass : NSObject

@end

@implementation SubClass

@end

然后初始化SubClass并打印相关方法的结果,可以获得如下结果。

[obj self] : <SubClass: 0x600001a980a0>
[obj class] : SubClass
[SubClass self] : SubClass
[SubClass class] : SubClass
[obj superclass] : NSObject
[SubClass superclass] : NSObject

从这些方法中我们可以得出结果:

[obj self]获取当前对象,查找当前对象的引用地址;

[obj class]获取当前对象的引用类型,获取当前类的的引用类型;

[SubClass self]和``[SubClass class] `都是获取当前的引用类型,查找当前类的引用类型对象;

[obj superclass][SubClass superclass]都是获取父类的引用类型。

我们再看看我们常用来判断子类父类、是否为当前类的成员对象的方法,由于这些方法十分简单,我就将对应的描述放到注释里面。

// 判断当前类是否等于目标类
+ (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}

// 判断当前对象的引用类型是否是目标类的成员对象
- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

// 遍历判断当前类是否是目标类的子类
+ (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;
}

// 判断当前类是否是目标类的子类
+ (BOOL)isSubclassOfClass:(Class)cls {
    for (Class tcls = self; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

// 判断当前类是否是目标对象的父类
+ (BOOL)isAncestorOfObject:(NSObject *)obj {
    for (Class tcls = [obj class]; tcls; tcls = tcls->superclass) {
        if (tcls == self) return YES;
    }
    return NO;
}

我们有时会在代码中判断某个类是否实现某一个协议会使用到conformsToProtocol去判断是否实现了某个协议,会循环遍历父类,然后调用class_conformsToProtocol去判断是否实现了这个协议。

+ (BOOL)conformsToProtocol:(Protocol *)protocol {
    if (!protocol) return NO;
    for (Class tcls = self; tcls; tcls = tcls->superclass) {
        if (class_conformsToProtocol(tcls, protocol)) return YES;
    }
    return NO;
}

- (BOOL)conformsToProtocol:(Protocol *)protocol {
    if (!protocol) return NO;
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (class_conformsToProtocol(tcls, protocol)) return YES;
    }
    return NO;
}

BOOL class_conformsToProtocol(Class cls, Protocol *proto_gen)
{
    protocol_t *proto = newprotocol(proto_gen);
    
    if (!cls) return NO;
    if (!proto_gen) return NO;

    mutex_locker_t lock(runtimeLock);

    checkIsKnownClass(cls);
    
    assert(cls->isRealized());
    
    for (const auto& proto_ref : cls->data()->protocols) {
        protocol_t *p = remapProtocol(proto_ref);
        if (p == proto || protocol_conformsToProtocol_nolock(p, proto)) {
            return YES;
        }
    }

    return NO;
}

我们经常会在使用代理的时候看到一段代码:

if ([_delegate respondsToSelector:@selector(xxx:)]{
    [_delegate xxx];
}   

然后我们再看看NSObject里面respondsToSelector默认实现是什么样的呢?

+ (BOOL)instancesRespondToSelector:(SEL)sel {
    if (!sel) return NO;
    return class_respondsToSelector(self, sel);
}

+ (BOOL)respondsToSelector:(SEL)sel {
    if (!sel) return NO;
    return class_respondsToSelector_inst(object_getClass(self), sel, self);
}

- (BOOL)respondsToSelector:(SEL)sel {
    if (!sel) return NO;
    return class_respondsToSelector_inst([self class], sel, self);
}

调用respondsToSelector会转发给class_respondsToSelector_inst方法去查找这个SelectorIMP来判断当前类是否实现xxx方法,instancesRespondToSelector也是通过class_respondsToSelector转发给class_respondsToSelector_inst方法。

// inst is an instance of cls or a subclass thereof, or nil if none is known.
// Non-nil inst is faster in some cases. See lookUpImpOrForward() for details.
bool class_respondsToSelector_inst(Class cls, SEL sel, id inst)
{
    IMP imp;

    if (!sel  ||  !cls) return NO;

    // Avoids +initialize because it historically did so.
    // We're not returning a callable IMP anyway.
    imp = lookUpImpOrNil(cls, sel, inst, 
                         NO/*initialize*/, YES/*cache*/, YES/*resolver*/);
    return bool(imp);
}

对于performSelector这个用来执行方法的函数我们也经常回事用到,这个函数主要调用objc_msgSend进行消息转发,具体实现在objc_msgSend部分再详细学习。

+ (id)performSelector:(SEL)sel {
    if (!sel) [self doesNotRecognizeSelector:sel];
    return ((id(*)(id, SEL))objc_msgSend)((id)self, sel);
}

+ (id)performSelector:(SEL)sel withObject:(id)obj {
    if (!sel) [self doesNotRecognizeSelector:sel];
    return ((id(*)(id, SEL, id))objc_msgSend)((id)self, sel, obj);
}

+ (id)performSelector:(SEL)sel withObject:(id)obj1 withObject:(id)obj2 {
    if (!sel) [self doesNotRecognizeSelector:sel];
    return ((id(*)(id, SEL, id, id))objc_msgSend)((id)self, sel, obj1, obj2);
}

- (id)performSelector:(SEL)sel {
    if (!sel) [self doesNotRecognizeSelector:sel];
    return ((id(*)(id, SEL))objc_msgSend)(self, sel);
}

- (id)performSelector:(SEL)sel withObject:(id)obj {
    if (!sel) [self doesNotRecognizeSelector:sel];
    return ((id(*)(id, SEL, id))objc_msgSend)(self, sel, obj);
}

- (id)performSelector:(SEL)sel withObject:(id)obj1 withObject:(id)obj2 {
    if (!sel) [self doesNotRecognizeSelector:sel];
    return ((id(*)(id, SEL, id, id))objc_msgSend)(self, sel, obj1, obj2);
}

我们经常会使用到isEqualXX来对比两个对象是否相等,为什么不用isEqual或则==呢,我们来看看isEqual的实现,从实现来看我们可以发现方法是对比两个对象是否相等,而对于NSString或者NSArray的成员对象来说,我们需要判断它们的值是否相等而不是地址是否相等,否则很容易出现地址一致而值不一致的问题。

+ (BOOL)isEqual:(id)obj {
    return obj == (id)self;
}

- (BOOL)isEqual:(id)obj {
    return obj == self;
}

我们经常会使用hash判断两个对象是否相等,在NSObject里面默认返回当前类的地址hash值。

+ (NSUInteger)hash {
    return _objc_rootHash(self);
}

- (NSUInteger)hash {
    return _objc_rootHash(self);
}

uintptr_t
_objc_rootHash(id obj)
{
    return (uintptr_t)obj;
}

我们经常都会使用NSLog(@"obj : %@", obj);来输出某个对象查看相关的属性,我们可以通过重写description方法来自定义对象的输出。

// Replaced by CF (returns an NSString)
+ (NSString *)description {
    return nil;
}

// Replaced by CF (returns an NSString)
- (NSString *)description {
    return nil;
}

+ (NSString *)debugDescription {
    return [self description];
}

- (NSString *)debugDescription {
    return [self description];
}

至此,NSObject协议的默认实现基本上就介绍完了,NSObject剩下的大多数方法都是作为父类方法,用于后面方法实现,就不再一一介绍了。

更好的阅读体验可以参考个人网站:https://zevwings.com

相关文章

网友评论

      本文标题:ObjC 学习笔记(六):NSObject协议

      本文链接:https://www.haomeiwen.com/subject/soumlctx.html