NSObject方法总结
类对象:
- 存放实例方法
- 属性
- 成员变量
- 协议列表
元类对象:
- 存放类方法
Isa & Superclass指针:
3.1 Class相关
-(Class)class
获取当前实例的类.
- (Class)class {
return object_getClass(self);
}
2. -(BOOL)isKindOfClass:(Class)aClass
判读当前receiver是否是aClass类型或其子类. 该方法有2种情况:
类对象判断:receiver(instance): 判断当前实例的类或当前实例的类的父类是否是aClass类型.
元类对象判断:receiver(class): 判断当前类的元类或当前类的元类的父类是否是aClass类型.
+ (BOOL)isKindOfClass:(Class)cls {
// 类对象的Isa指向元类
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
// 实例对象调用class获取到类对象
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
3. -(BOOL)isMemberOfClass:(Class)aClass
该方法有2种情况:
类对象判断:receiver(instance): 判断当前实例的类是否是aClass类型.
元类对象判断:receiver(class): 判断当前类的元类是否是aClass.
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
4. -(Class)class
返回当前类,即返回self.
+ (Class)class {
return self;
}
5. -(BOOL)isSubclassOfClass:(Class)aClass
判断当前类是否是aClass类或其子类.
+ (BOOL)isSubclassOfClass:(Class)cls {
for (Class tcls = self; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
3.2 Protocol相关
conformsToProtocol仅判断当前类的协议列表中有无实现某个协议.包括继承的协议:协议的继承(例如协议A继承了协议B,类A实现了协议A,判断类A有无实现协议B)、类继承下来的协议(例如类A继承自类B,类B实现了协议B,判断类A有无实现协议B).对象和类的底层结构体分别为objc_object、objc_class.其中objc_class继承于objc_object.该方法内部是会递归判断当前类或其父类的协议列表中有无指定的协议.若有则立即返回YES,否则直到遍历结束.由于每次判断都是递归并且加锁,因此该方法是耗时的,apple建议我们对其结果进行缓存或者仅判断.
建议如下:
- 使用
respondsToSelector替代该conformsToProtocol,尤其是仅判断是否实现该协议中的部分方法. - 非要使用
conformsToProtocol时,进行结果的缓存.
-(BOOL)conformsToProtocol:(Protocol *)aProtocol
判断当前实例的类是否实现了aProtocol协议,并不会判断是否实现协议中的方法,即判断当前实例对应类的协议列表中是否包含aProtocol协议.等价于+(BOOL)conformsToProtocol:(Protocol *)aProtocol类方法,不过多了个步骤:取当前实例对应类.
- (BOOL)conformsToProtocol:(Protocol *)protocol {
if (!protocol) return NO;
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
if (class_conformsToProtocol(tcls, protocol)) return YES;
}
return NO;
}
2. +(BOOL)conformsToProtocol:(Protocol *)aProtocol
判断当前类是否实现了aProtocol协议,并不会判断是否实现协议中的方法,即判断当前类的协议列表中是否包含aProtocol协议.
+ (BOOL)conformsToProtocol:(Protocol *)protocol {
if (!protocol) return NO;
for (Class tcls = self; tcls; tcls = tcls->getSuperclass()) {
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());
// cls->data()->protocols()为当前类的协议列表
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;
}
3.3 Method相关
-(BOOL)respondsToSelector:(SEL)aSelector
判断当前对象(实力对象/类对象)是否响应某个方法(包括从父类继承下来的方法);实例对象则传入实例方法,类对象则传入类方法.
该方法有2种情况:
实例对象判断:receiver(instance) -> @selector(instanceMethod):判断当前实例是否响应该方法.
类对象判断:receiver(class) -> @selector(classMethod) :判断当前实例的类是否响应该方法.
- (BOOL)respondsToSelector:(SEL)sel {
return class_respondsToSelector_inst(self, sel, [self class]);
}
+ (BOOL)respondsToSelector:(SEL)sel {
return class_respondsToSelector_inst(self, sel, self->ISA());
}
2. +(BOOL)instancesRespondToSelector:(SEL)aSelector
判断当前类是否响应aSelector方法,只能传入类对象且对应实例方法.
+ (BOOL)instancesRespondToSelector:(SEL)sel {
return class_respondsToSelector_inst(nil, sel, self);
}
3.4 Signature相关
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
返回消息接受者对应的方法签名.该方法有2种情况:
实例对象:receiver(instance) -> @selector(instanceMethod):返回当前实例对应方法的方法签名.
类对象:receiver(class) -> @selector(classMethod):返回当前类对应方法的方法签名.
+(NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector
返回当前类对应实例的方法签名,只能传入实例方法.只能传入类对象且对应实例方法.
3.5 消息三部曲
消息发送
动态方法解析
当消息发送阶段没有找到对应的方法时,会进入动态方法解析阶段.可通过resolveClassMethod或resolveInstanceMethod动态为当前类添加实例方法或类方法.
消息转发
如果动态方法解析没有处理,则进入消息转发阶段,该阶段分为2种类型:快类型、慢类型.
快转发:
通过forwardingTargetForSelector方法返回个实例对象,若返回的实例对象不为空则重新在该实例对象走一次消息发送.若返回的实例对象为空则进入慢转发阶段.
慢转发:
若methodSignatureForSelector返回的方法签名为空则crash(doesNotRecognizeSelector),否则调用forwardInvocation在该阶段可通过NSInvocation调用invoke或invokeWithTarget方法.