0%

OC类型自省函数

先上测试代码:

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;

#if !__OBJC2__
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;
#endif

} 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=元类对象了.

觉得文章有帮助可以打赏一下哦!