类方法、成员变量属性探索

301 阅读3分钟

属性、成员变量/实例变量简介

来看下代码实例

@interface CFPerson : NSObject
{
    NSString *body;
    NSString *blood;
}

@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) NSString *nickName;

编译后底层代码结构

extern "C" unsigned long OBJC_IVAR_$_CFPerson$_nickName;
extern "C" unsigned long OBJC_IVAR_$_CFPerson$_name;
struct CFPerson_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	NSString *__strong body;	NSObject *__strong blood;	
        NSString *__strong _nickName;
	NSString *__strong _name;
};

同时,属性编译会生成相应的objc_setProperty方法和当前的get方法

static NSString * _I_CFPerson_nickName(CFPerson * self, SEL _cmd) { return (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_CFPerson$_nickName)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_CFPerson_setNickName_(CFPerson * self, SEL _cmd, NSString *nickName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct CFPerson, _nickName), (id)nickName, 0, 1); }

static NSString * _I_CFPerson_name(CFPerson * self, SEL _cmd) { return (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_CFPerson$_name)); }

// 赋值
static void _I_CFPerson_setName_(CFPerson * self, SEL _cmd, NSString *name) { (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_CFPerson$_name)) = name; }
// @end

总结:{}里面的内容称为实例变量,实例变量即是特殊的成员变量【类的实例化】

属性即是带有下划线的成员变量+setter+getter方法

扩展-IMP与SEL的关系

  • IMP:函数指针地址。比如一本书目录的页码

  • SEL: 方法编号。如一本书的目录名称

    static struct /_method_list_t/ { unsigned int entsize; // sizeof(struct _objc_method) unsigned int method_count; struct _objc_method method_list[8]; } OBJC$_INSTANCE_METHODS_CFPerson attribute ((used, section ("__DATA,__objc_const"))) = { sizeof(_objc_method), 8, {{(struct objc_selector *)"nickName", "@16@0:8", (void *)_I_CFPerson_nickName}, {(struct objc_selector *)"setNickName:", "v24@0:8@16", (void *)I_CFPerson_setNickName}, {(struct objc_selector *)"name", "@16@0:8", (void *)_I_CFPerson_name}, {(struct objc_selector *)"setName:", "v24@0:8@16", (void *)I_CFPerson_setName}, {(struct objc_selector *)"nickName", "@16@0:8", (void *)_I_CFPerson_nickName}, {(struct objc_selector *)"setNickName:", "v24@0:8@16", (void *)I_CFPerson_setNickName}, {(struct objc_selector *)"name", "@16@0:8", (void *)_I_CFPerson_name}, {(struct objc_selector *)"setName:", "v24@0:8@16", (void *)I_CFPerson_setName}} };

    static struct /_prop_list_t/ { unsigned int entsize; // sizeof(struct _prop_t) unsigned int count_of_properties; struct _prop_t prop_list[2]; } OBJC$_PROP_LIST_CFPerson attribute ((used, section ("__DATA,__objc_const"))) = { sizeof(_prop_t), 2, {{"nickName","T@"NSString",C,N,V_nickName"}, {"name","T@"NSString",&,N,V_name"}} };

通过苹果官方各编码类型所指可以知道

/*
 @16@0:8
 @: 返回值
 16 参数共用字节 16
 第二个 @ :第一个参数 8
 0 : 从0 开始
 : : sel 8
*/

我们知道,在OC里面所谓的对象方法、实例方法,但在C/C++里却没有这一说。又OC在编译时底层必将趋于C/C++层,这个时候对象方法、实例方法就统称为方法。

类方法的分析

代码举例

void cfObjc_copyMethodList(Class pClass){
    unsigned int count = 0;
    Method *methods = class_copyMethodList(pClass, &count);
    for (unsigned int i=0; i < count; i++) {
        Method const method = methods[i];
        //获取方法名
        NSString *key = NSStringFromSelector(method_getName(method));

        LGLog(@"Method, name: %@", key);
    }
    free(methods);
}

void cfInstanceMethod_classToMetaclass(Class pClass){

    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);

    Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));

    Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));

    LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

void cfClassMethod_classToMetaclass(Class pClass){

    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);

    Method method1 = class_getClassMethod(pClass, @selector(sayHello));//判断pClass方法里面是否有sayHello方法
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));//判断元类metaClass方法里面是否有sayHello方法

    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));

    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));

    LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

void lgIMP_classToMetaclass(Class pClass){

    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);

    // - (void)sayHello;
    // + (void)sayHappy;
    IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));

    IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));

    NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
    NSLog(@"%s",__func__);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        // 0x0000000100000000
        // LGTeacher *teacher = [LGTeacher alloc];

        CFPerson *person = [CFPerson alloc];
        Class pClass     = object_getClass(person);
        lgObjc_copyMethodList(pClass);

        cfInstanceMethod_classToMetaclass(pClass);
        cfClassMethod_classToMetaclass(pClass);
        NSLog(@"Hello, World!");
    }
    return 0;
}

打印日志

打印结果可以看到

这两个方法并未查找到

Method method1 = class_getClassMethod(pClass, @selector(sayHello));

Method method2 = class_getClassMethod(metaClass, @selector(sayHello));

这两个方法可以查找到

Method method3 = class_getClassMethod(pClass, @selector(sayHappy));

Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));

在看下class_getClassMethod方法底层实现

Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}
得到一个类的类方法就相当于得到一个元类的实例方法

再往下走

static method_t *getMethod_nolock(Class cls, SEL sel)
{
    method_t *m = nil;

    runtimeLock.assertLocked();

    // fixme nil cls?
    // fixme nil sel?

    ASSERT(cls->isRealized());

    while (cls  &&  ((m = getMethodNoSuper_nolock(cls, sel))) == nil) {
        cls = cls->superclass;
    }

    return m;
}

如果是元类,则不会再递归下去,否则会一直循环 
 // NOT identical to this->ISA when this is a metaclass
    Class getMeta() {
        if (isMetaClass()) return (Class)this;
        else return this->ISA();
    }

isKindOfClass与isMemberOfClass源码分析

代码举例:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];       //
        BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];     //
        BOOL re3 = [(id)[CFPerson class] isKindOfClass:[CFPerson class]];       //
        BOOL re4 = [(id)[CFPerson class] isMemberOfClass:[CFPerson class]];     //
        NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);

        BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];       //
        BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];     //
        BOOL re7 = [(id)[CFPerson alloc] isKindOfClass:[CFPerson class]];       //
        BOOL re8 = [(id)[CFPerson alloc] isMemberOfClass:[CFPerson class]];     //
        NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);

    }
    return 0;
}

打印结果:

  • isKindOfClass和isMemberOfClass方法底层代码结构:

    • (BOOL)isKindOfClass:(Class)cls { // 类 vs 元类 // 根元类 vs NSObject // NSObject vs NSObject // CFPerson vs 元类 (根元类) (NSObject) for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) { if (tcls == cls) return YES; } return NO; }

    • (BOOL)isMemberOfClass:(Class)cls { return self->ISA() == cls; }

    • (BOOL)isMemberOfClass:(Class)cls { return [self class] == cls; }

isKindOfClass方法内部先获得objc——getClass的类,而object_getClass的源码实现是去调用当前类的objc—>getIsa(),ISA()方法中获得元类meta class的指针,

接下来还有一个循环,先判断class是否等于meta class,不等则继续循环是否等于super class,不等再继续取super class,如此循环下去。

isMemberOfClass的源码实现是拿到自己的isa指针与自己取比较是否相等。