美文网首页
Objective-C runtime(二)

Objective-C runtime(二)

作者: 羽裳有涯 | 来源:发表于2020-09-10 07:24 被阅读0次

    Ivar

    Ivar 是一种代表类中实例变量的类型。

    typedef struct ivar_t *Ivar;
    

    ivar_t在上面的成员变量列表中也提到过:

    struct ivar_t {
        int32_t *offset;
        const char *name;
        const char *type;
        // alignment is sometimes -1; use alignment() instead
        uint32_t alignment_raw;
        uint32_t size;
    
        uint32_t alignment() const {
            if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
            return 1 << alignment_raw;
        }
    };
    

    可以根据实例查找其在类中的名字,也就是“反射”:

    -(NSString *)nameWithInstance:(id)instance {
        unsigned int numIvars = 0;
        NSString *key=nil;
        Ivar * ivars = class_copyIvarList([self class], &numIvars);
        for(int i = 0; i < numIvars; i++) {
            Ivar thisIvar = ivars[i];
            const char *type = ivar_getTypeEncoding(thisIvar);
            NSString *stringType =  [NSString stringWithCString:type encoding:NSUTF8StringEncoding];
            if (![stringType hasPrefix:@"@"]) {
                continue;
            }
            if ((object_getIvar(self, thisIvar) == instance)) {//此处若 crash 不要慌!
                key = [NSString stringWithUTF8String:ivar_getName(thisIvar)];
                break;
            }
        }
        free(ivars);
        return key;
    }
    

    class_copyIvarList函数获取的不仅有实例变量,还有属性。但会在原本的属性名前加上一个下划线。

    objc_property_t

    @property标记了类中的属性,这个不必多说大家都很熟悉,它是一个指向objc_property结构体的指针:

    
    typedef struct property_t *objc_property_t;
    

    可以通过 class_copyPropertyList 和 protocol_copyPropertyList 方法来获取类和协议中的属性:

    objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
    objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
    
    

    返回类型为指向指针的指针,哈哈,因为属性列表是个数组,每个元素内容都是一个 objc_property_t 指针,而这两个函数返回的值是指向这个数组的指针。

    举个栗子,先声明一个类:

    @interface Lender : NSObject {
        float alone;
    }
    @property float alone;
    @end
    

    你可以用下面的代码获取属性列表:

    id LenderClass = objc_getClass("Lender");
    unsigned int outCount;
    objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
    
    

    你可以用 property_getName函数来查找属性名称:

    const char *property_getName(objc_property_t property)
    
    

    你可以用class_getProperty 和 protocol_getProperty通过给出的名称来在类和协议中获取属性的引用:

    objc_property_t class_getProperty(Class cls, const char *name)
    objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty)
    
    

    你可以用property_getAttributes函数来发掘属性的名称和@encode类型字符串:

    const char *property_getAttributes(objc_property_t property)
    
    

    把上面的代码放一起,你就能从一个类中获取它的属性啦:

    id LenderClass = objc_getClass("Lender");
    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
    }
    

    对比下class_copyIvarList 函数,使用 class_copyPropertyList 函数只能获取类的属性,而不包含成员变量。但此时获取的属性名是不带下划线的。

    protocol_t

    虽然 Objective-CCategoryprotocol 拓展能力有限,但也得为了将就 Swift 的感受,充个胖子。
    flags 32 位指针最后两位是给加载 Mach-O 的 fix-up 阶段使用的,前 16 位预留给 Swift 用的。
    protocol 主要内容其实是(可选)方法,其次就是继承其他 protocol。Swift 还支持 protocol 多继承,所以需要 protocols 数组来做兼容。

    struct protocol_t : objc_object {
        const char *mangledName;
        struct protocol_list_t *protocols;
        method_list_t *instanceMethods;
        method_list_t *classMethods;
        method_list_t *optionalInstanceMethods;
        method_list_t *optionalClassMethods;
        property_list_t *instanceProperties;
        uint32_t size;   // sizeof(protocol_t)
        uint32_t flags;
        // Fields below this point are not always present on disk.
        const char **_extendedMethodTypes;
        const char *_demangledName;
        property_list_t *_classProperties;
        ... 省略一些封装的便捷 get 方法
    }
    
    IMP

    IMP在objc.h中的定义是:

    typedef void (*IMP)(void /* id, SEL, ... */ );
    
    

    它就是一个函数指针,这是由编译器生成的。当你发起一个 ObjC 消息之后,最终它会执行的那段代码,就是由这个函数指针指定的。而 IMP这个函数指针就指向了这个方法的实现。既然得到了执行某个实例某个方法的入口,我们就可以绕开消息传递阶段,直接执行方法,这在后面会提到。

    你会发现 IMP 指向的方法与 objc_msgSend 函数类型相同,参数都包含 id 和 SEL 类型。每个方法名都对应一个 SEL 类型的方法选择器,而每个实例对象中的 SEL 对应的方法实现肯定是唯一的,通过一组 id 和 SEL 参数就能确定唯一的方法实现地址;反之亦然。

    相关文章

      网友评论

          本文标题:Objective-C runtime(二)

          本文链接:https://www.haomeiwen.com/subject/vqtbxktx.html