类(Class)
OC中的类是由Class来定义的,其实际上就是一个指向objc_class结构体的指针,其定义为
typedef struct objc_class *Class;
而objc_class又是什么样的呢,查看其定义为
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父类
const char *name OBJC2_UNAVAILABLE; // 类名
long version OBJC2_UNAVAILABLE; // 类的版本号
long info OBJC2_UNAVAILABLE; // 类信息
long instance_size OBJC2_UNAVAILABLE; // 类的实例大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 成员变量列表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法列表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法缓存
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 协议列表
#endif
} OBJC2_UNAVAILABLE;
对象(id)
OC中的万能对象用id来表示,其实际上就是一个指向 objc_object 结构体的指针,其定义如下:
typedef struct objc_object *id;
而objc_object又是什么呢,查看其定义:
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
-
isa
由类和对象的定义,我们可以看出,其成员变量中都含有isa指针,isa指向的是一个类,其指向该对象所属的类。我们可以把isa当作是一个对象的标志,因此类也是一个对象,我们称之为类对象,其isa指针,指向该类对象所属的类,我们称之为元类(metaClass)。isa指针指向的类,我们可以用[object class]来获取,但是,如果是类对象,其只能返回当前类,获取不到元类。object_getClass()函数也可以获取到isa指针指向的类,包括元类,下面是class方法和object_getClass()函数的具体实现。
- (Class)class {
return object_getClass(self);
}
+ (Class)class {
return self;
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
} super_class
指向该对象的父类,如果该对象,已经是最顶层的根类(如NSObject),则该值为null
那么isa,类,父类,元类之间的关系又是怎样的?由于元类无法打印出来,因此我们采用查看其地址的方法来探究其关系。
先看代码:
#import <Foundation/Foundation.h>
@interface Person: NSObject
@end;
@interface Student: Person
@end;
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
Student *s = [[Student alloc] init];
NSLog(@"对象 s --> isa 指向地址是 %p",[s class]);
NSLog(@"Student --> isa 指向地址是 %p",object_getClass([Student class]));
NSLog(@"Student --> isa --> isa 指向的地址是 %p",object_getClass(object_getClass([Student class])));
NSLog(@"Student --> isa --> superclass 的地址是 %p",[object_getClass([Student class]) superclass]);
NSLog(@"s --> superclass 的地址 %p",[s superclass]);
NSLog(@"============================");
NSLog(@"对象 p --> isa 指向的地址是 %p",[p class]);
NSLog(@"Person --> isa 指向的地址是 %p",object_getClass([Person class]));
NSLog(@"Person --> isa --> isa 指向的地址是 %p",object_getClass(object_getClass([Person class])));
NSLog(@"Person --> isa --> superclass 的地址是 %p",[object_getClass([Person class]) superclass]);
NSLog(@"p --> superclass 的地址 %p",[p superclass]);
NSLog(@"============================");
NSLog(@"NSObject 的地址 %p",[NSObject class]);
NSLog(@"NSObject --> isa 的地址是 %p",object_getClass([NSObject class]));
NSLog(@"NSObject --> isa --> isa 的地址是 %p",object_getClass(object_getClass([NSObject class])));
NSLog(@"NSObject --> isa --> superclass 的地址是 %p",[object_getClass([NSObject class]) superclass]);
NSLog(@"NSObject --> superclass 的地址 %p",[NSObject superclass]);
}
return 0;
}
输出结果:
对象 s --> isa 指向地址是 0x1000049a0
Student --> isa 指向地址是 0x100004978
Student --> isa --> isa 指向的地址是 0x7fff7f835118
Student --> isa --> superclass 的地址是 0x100004928
s --> superclass 的地址 0x100004950
============================
对象 p --> isa 指向的地址是 0x100004950
Person --> isa 指向的地址是 0x100004928
Person --> isa --> isa 指向的地址是 0x7fff7f835118
Person --> isa --> superclass 的地址是 0x7fff7f835118
p --> superclass 的地址 0x7fff7f8350f0
============================
NSObject 的地址 0x7fff7f8350f0
NSObject --> isa 的地址是 0x7fff7f835118
NSObject --> isa --> isa 的地址是 0x7fff7f835118
NSObject --> isa --> superclass 的地址是 0x7fff7f8350f0
NSObject --> superclass 的地址 0x0
得到关系图如下:
由图我们可以得出以下结论:
- 实例对象的isa指向该对象所属的类
- 类对象的isa指向该类对象所属的类,即元类(metaclass)
- 所有类对象的元类的isa指向该类对象的顶级父类(NSObject)的元类
- 类对象的元类的superclass指向该类对象的父类的元类
- 顶级类对象(NSObject)的元类的isa指向元类自己
- 顶级类对象的superclass指向nil(0x0)
好了,有了这个关系图,我们来小运用一下:
@interface Person: NSObject
@end;
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
BOOL isEqual = [p isKindOfClass:[Person class]];
BOOL isEqual1 = [Person isKindOfClass:[Person class]];
BOOL isEqual2 = [NSObject isKindOfClass:[NSObject class]];
BOOL isEqual3 = [p isMemberOfClass:[Person class]];
BOOL isEqual4 = [Person isMemberOfClass:[Person class]];
BOOL isEqual5 = [NSObject isMemberOfClass:[NSObject class]];
NSLog(@"%d %d %d %d %d %d",isEqual,isEqual1,isEqual2,isEqual3,isEqual4,isEqual5);
}
return 0;
}
输出结果是怎样的呢?
运行后,我们看到结果是:
1 0 1 1 0 0
为什么结果是这样的呢?我们先来看isKindOfClass:和isMemberOfClass:这两个方法是怎么实现的。
NSObject.mm
+ (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)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
不难发现,这两个方法,都是用对象的isa,去和参数cls做比较,所不同的是,如果第一次比较,两者不相同,isKindOfClass:会去寻找isa的superclass,如果一直比较到最顶层的superclas(nil),仍然没有找到相同的,才返回NO。
下面来看isEqual1的比较过程:
第一次: Person的metaClass 和 Person相比 == NO
第二次:Person的metaClass的superclass即NSObject的metaClass 和 Person相比 == NO
第三次:NSObject的metaClass的superclass 和 Person相比 == NO
最后:Person 的superClass == nil,退出比较,直接返回NO
isEqual2的比较过程:
第一次: NSObject的metaClass 和 NSObject相比 == NO
第二次:NSObject的metaClass的superclass 即NSObject 和 NSObject相比 == YES
最后,退出比较,返回YES
其他的比较类似,这里不做累述。
动态创建类
说了这么多,我们来动态创建个类,在动态创建之前,先介绍几个函数:
objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
该方法用来动态创建一个类,
superclass:为要创建的类的父类,如果传nil,则表示要创建根类。
name: 要创建的新类
extraBytes: 分配给类和元类尾部索引变量的字节数,通常为0void objc_disposeClassPair(Class cls)
该方法用于销毁一个类和其关联的元类,注意:如果该类的实例或其任何一个子类存在的时候,不要调用该方法。void objc_registerClassPair(Class cls)
注册一个类BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
给类动态添加方法
name: 要增加的方法名称的映射
imp:方法的实现,该方法必须至少包含两个参数,self 和 _cmd
types: 类型编码,用一个C字符串来表示方法的返回值和参数,列举几个常用的类型编码:
Code | 含义 |
---|---|
v | 表示void |
@ | 表示一个对象 |
: | 表示一个方法选择器 SEL |
由于方法的实现必须包含self和_cmd两个参数,因此types的第二个和第三个字符必须是"@:"(第一个是返回值类型)
-
BOOL class_addIvar(Class cls, const char *name, size_t size, uint8_t alignment, const char *types)
给类添加成员变量, 该方法只能在动态创建类的时候使用,不能向已存在的类中添加成员变量
cls: 要添加成员变量的类,该类不能是元类
name: 变量名称
size:变量类型大小,通常可用sizeof来获取
alignment: 偏移量,实例变量的最小偏移量是1<<align,如果是指针类型,我们可以传log2(sizeof(pointer_type)).size和alignment我们也可以通过如下方式来得到,如
NSUInteger size;
NSUInteger alignment;
NSGetSizeAndAlignment("*", &size, &alignment);types: 类型编码。
动态创建的类必须从objc_allocateClassPair开始,直到 class_addMethod、 class_addIvar等方法都执行完之后,最后再用objc_registerClassPair注册该类。
代码实践
本例子动态创建了一个类,并向其中添加了方法和成员变量,最后,执行了新添加的方法并获取了新添加的变量的值。
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
void myMethodIMP(id self, SEL _cmd)
{
NSLog(@"我是新类----%@",[self class]);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
Class cls = objc_allocateClassPair([NSObject class],"myClass", 0);
class_addMethod(cls, NSSelectorFromString(@"myMethodIMP"),(IMP)myMethodIMP, "v@:");
BOOL flag = class_addIvar(cls, "name", sizeof(NSString *), log2(sizeof(NSString *)), "@");
objc_registerClassPair(cls);
id instance = [[cls alloc] init];
[instance setValue:@"百客" forKey:@"name"]; // 这里用kvc的时候,最好判断下该变量是否存在,或者用下面的这种方法
// object_setIvar(instance, class_getInstanceVariable(cls, "_age"), @"10");
[instance performSelector:@selector(myMethodIMP)];
Ivar v = class_getInstanceVariable(cls, "name");
id o = object_getIvar(instance, v);
NSLog(@"动态添加的成员变量name的值为---%@",o);
}
return 0;
}
输出结果:
我是新类----myClass
动态添加的成员变量name的值为---百客
附:OC Runtime库地址: http://opensource.apple.com/tarballs/objc4/