先上测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 ZAEImageView *img = [ZAEImageView new]; BOOL isMember1 = [img isMemberOfClass:ZAEImageView.class];BOOL isMember2 = [img isMemberOfClass:UIImageView .class];BOOL isMember3 = [ZAEImageView isMemberOfClass:ZAEImageView.class];BOOL isMember4 = [ZAEImageView isMemberOfClass:UIImageView .class];BOOL isMember5 = [ZAEImageView isMemberOfClass:object_getClass(ZAEImageView.class)];BOOL isMember6 = [ZAEImageView isMemberOfClass:object_getClass(UIImageView .class)];BOOL isKind1 = [img isKindOfClass:ZAEImageView.class];BOOL isKind2 = [img isKindOfClass:UIImageView .class];BOOL isKind3 = [ZAEImageView isKindOfClass:ZAEImageView.class];BOOL isKind4 = [ZAEImageView isKindOfClass:UIImageView .class];BOOL isKind5 = [ZAEImageView isKindOfClass:object_getClass(ZAEImageView.class)];BOOL isKind6 = [ZAEImageView isKindOfClass:object_getClass(UIImageView .class)];
结果:
OC类型自省的函数如下:
1 2 - (BOOL )isKindOfClass:(Class)aClass; - (BOOL )isMemberOfClass:(Class)aClass;
它们声明在@protocol NSObject中,NSObject类实现了NSObject协议@interface NSObject <NSObject>.
可以看到它们都是实例方法,但是为什么可以发送给类对象?和元类有关.
- (BOOL)isMemberOfClass:(Class)aClass;
Returns a Boolean value that indicates whether the receiver is an instance of a given class.
用于检测消息接收者是否是某个类的实例.
- (BOOL)isKindOfClass:(Class)aClass;
Returns a Boolean value that indicates whether the receiver is an instance of given class or an instance of any class that inherits from that class.
用于检测消息接收者是否是某个类的实例或者是其子类的实例.注意与isMemberOfClass的区别.
对实例对象进行自省 示例:[img isKindOfClass:UIImageView.class];这个很好理解,不是今天的重点.
对类对象进行自省 示例:[ZAEImageView isKindOfClass:object_getClass(UIImageView.class)];
对类对象进行自省时isKindOfClass的参数为啥是object_getClass(UIImageView.class)而不能是UIImageView.class?
其实也很简单:实例对象的类型是类对象,类对象的类型是元类对象.因此对类对象进行自省时,isKindOfClass的参数就需要传入类对象的类型,那如何获取类对象的类型呢?答案就是object_getClass().
OBJC_EXPORT Class _Nullable object_getClass(id _Nullable obj);
Returns the class of an object.
返回一个对象的类型.注意这里的参数是id.因此可以是任何OC对象—实例对象,类对象,元类对象.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 /// An opaque type that represents an Objective-C class. typedef struct objc_class *Class; /// Represents an instance of a class. struct objc_object { Class _Nonnull isa OBJC_ISA_AVAILABILITY; }; /// A pointer to an instance of a class. typedef struct objc_object *id; struct objc_class { Class _Nonnull isa OBJC_ISA_AVAILABILITY; Class _Nullable super_class OBJC2_UNAVAILABLE; const char * _Nonnull name OBJC2_UNAVAILABLE; long version OBJC2_UNAVAILABLE; long info OBJC2_UNAVAILABLE; long instance_size OBJC2_UNAVAILABLE; struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE; struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE; struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE; struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE; } OBJC2_UNAVAILABLE;
至于为什么id可以指向任意OC对象,简单点讲就是因为所有OC对象都有一个Class isa指针.因此都可以赋值给id变量.具体可以参考:iOS OC id类型为什么能指向任意类
测试:
1 2 3 4 5 id cls1 = img.class id cls2 = object_getClass(img) id cls3 = ZAEImageView.class id cls4 = object_getClass(ZAEImageView.class) id cls5 = objc_getMetaClass("ZAEImageView" )
结果:
可以看到当传入的是实例对象时,object_getClass()返回的是实例对象的类型—类对象,如果传入的是类对象,则object_getClass()返回的是类对象的类型—元类对象.所以cls4指向的地址和前面几个不同.
object_getClass()实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Class object_getClass (id obj) { return _object_getClass(obj); } static inline Class _object_getClass(id obj){ #if SUPPORT_TAGGED_POINTERS if (OBJC_IS_TAGGED_PTR (obj)) { uint8_t slotNumber = ((uint8_t ) (uint64_t ) obj) & 0x0F ; Class isa = _objc_tagged_isa_table[slotNumber]; return isa; } #endif if (obj) return obj->isa; else return Nil; }
可以看到object_getClass()就是返回对象的isa指针.
另外+ (Class)class,- (Class)class的实现分别如下:
1 2 3 4 5 6 7 8 9 //声明于NSObject类中,类方法.返回自身指针 + (Class)class { return self; } //声明于NSObject协议中,实例方法.调用object_getClass,返回isa指针 - (Class)class { return object_getClass(self); }
知道了实现就不难理解cls1=cls2=cls3=类对象不等于cls4=cls5=元类对象了.