meta-class in OC

用runtime创建类

下面的代码是创建了一个NSError的subClass和添加一个方法:

Class newClass = objc_allocateClassPair([NSError class], "RuntimeErrorSubClass", 0);
/*
* Class cls: 将要给添加方法的类,传的类型 [类名  class]
* SEL name: 将要添加的方法名,传的类型   @selector(方法名)
* IMP imp:实现这个方法的函数
* const char *types:表示我们要添加的方法的返回值和参数
  ”v@:”意思就是这已是一个void类型的方法,没有参数传入
*/
class_addMethod(newClass, @selector(report), (IMP)ReportFunction, "v@:");
objc_registerClassPair(newClass);

这个方法添加了ReportFunction方法,实现如下:

void ReportFunction(id self, SEL _cmd) {
    NSlog(@"This object is %p.",self);
    NSLog(@"Class is %@, and super is %@.", [self class], [self superclass]);
    
    Class currentClass = [self class];
    for (int i=1; i < 5; i++) {
        NSlog(@"Following the isa pointer %d times gives %p", i, currentClass);
        currentClass = object_getClass(currentClass);
    }
    
    NSLog(@"NSObject's class is %p", [NSObject class]);
    NSLog(@"NSObject' meta class is %p", object_getClass([NSObject class]))
}

从这里看起来,从runtime创建类只有简单的三步:
1. 给“class pair”分配内存(使用objc_allocateClassPair).
2. 给类增加方法和成员变量.
3. 注册类(使用objc_registerClassPair).

那么这个class pair是指什么呢?应该就是指类和元类。

怎样把数据结构转变成对象呢?

每个对象都是由类具体化的,这是面向对象编程的基本原理,在object-C中,数据也具有这样的属性。任何具有一个指向类的指针的数据结构都可以被看作是一个对象。

在OC中,一个对象的类是由它的isa指针决定的,isa指向对象的类。
实际上,对象的基本定义是这样的:

typedef struct objc_objec {
    Class isa;
} *id

从定义可以看出,任何一个具有指向一个类的指针的结构体都可以被看作是一个对象。

OC的一个非常重要的特性是,消息机制,如下例子:

[@"stringValue" writeToFile:@"/file.txt" atomically:YES encaoding:NSUTF8StringEncoding error:NULL];

这个工作的原理是,当你发送一个消息给一个对象(如这里的NSCFString),runtime根据isa指向对象的类(在这个例子中指的是NSCFString)。类中有所有对象的方法列表以及父类的方法列表(继承)。runtime就会遍历类和父类中的方法列表,匹配出消息中的方法(如writeToFile:atomically:encoding:error on NSString)并调用函数。

那么什么是元类(meta-class)呢?

在OC中,类同样是一个对象,这就意味着,可以直接发送消息给类,如:

NSStringEncoding defaultStringEncoding = [NSString defaultStringEncoding];

这个方法中,defaultStringEncoding就是直接调用的类方法。
类方法工作的原因是因为在OC中,每个类自身都是一个对象,也就是说每个类的结构体必须有isa指针,这样就能和对象的结构相兼容。
为了让我们能够唤起类中的方法,类的isa指针必须指向一个包含改方法的方法列表的类的结构体。这就是元类(meta-class)的定义了:元类就是类对象的类。如图表示:
objc-isa-meta-class
简单描述这个过程就是:
* 当你发送一个消息给对象的时候,这个消息会在类的方法列表中寻找对应的响应。
* 当你发送一个消息给类的时候,消息会在元类的方法列表中寻找对应的响应。

因此,换句话说,元类保存了类的类方法。

总结

  • 元类也是一个对象,元类存在一个基元类,基元类的isa指向自身。
  • 每个类都有唯一的元类
  • 元类保证了类对象的实例方法和类方法的继承

PS:
现在ObjC对象的结构已经更新了,代替isa指针的是结构体isa_t,这个结构体包含了但前对象指向的类的信息

struct objc_object {
    isa_t isa;
};

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;

    struct {
        uintptr_t indexed           : 1;
        uintptr_t has_assoc         : 1;
        uintptr_t has_cxx_dtor      : 1;
        uintptr_t shiftcls          : 44;
        uintptr_t magic             : 6;
        uintptr_t weakly_referenced : 1;
        uintptr_t deallocating      : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 8;
    };
};

大部分内容翻译自文章链接

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页