美文网首页
iOS底层-类的探索分析之类的属性及类的方法

iOS底层-类的探索分析之类的属性及类的方法

作者: 似水流年_9ebe | 来源:发表于2021-06-23 15:19 被阅读0次

iOS底层-类的探索分析之isa及继承链中,我们介绍了isa继承链类的结构,现在我们接着分析OC的类。

类的属性与变量

我们运行一下iOS底层-类的探索分析之isa及继承链中的Demo工程,lldb调试下,如图:

1 2
从上图中,我们拿到了三个ivars,iOS底层-类的探索分析之isa及继承链中我们说过,subject是没有取到,现在取到了,是通过在ro(class_ro_t结构体)里面找到了,那么subjecthobby,name有什么区别呢,我们通过它的定义可以看到,subject是成员变量,而hobby,name是属性,那么它们又有什么不同呢,我们接着往下分析。
我们先贴下代码:
@interface RoPerson : NSObject
{
    NSString *hobby;
    NSObject *objc;
}

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

@end

@implementation RoPerson

@end

@interface RoTeacher : NSObject

@end

@implementation RoTeacher

@end

上述代码中,在RoPerson中,我们定义了nickNamenameaname的属性和hobby(字符串类型,基本数据类型是成员变量)的成员变量以及objc(对象类型,也是成员变量,是特殊的一种,叫实例变量)实例变量。
接着我们,执行clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-14.5.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.5.sdk main.m命令编译成c++代码看下底层代码结构,我们打开main.cpp并搜索RoPerson,如图所示:

3
4

这些属性在底层代码中全部被注释掉了,而属性除了生成了成员变量以外还生成了setter和getter方法。从上图中,我们可以看到nickName的getter方法用的是内存平移,setter方法用的是objc_setProperty,而name使用的也不同,这又是为什么,我们等下再分析。
我们在man.cpp再搜下static struct 找到如下图的代码:


5

我们看下**{{(struct objc_selector )"nickName", "@16@0:8", (void )_I_RoPerson_nickName}代码

我们解释下@16@0:8(编码,文档参考[Type Encodings]
这个
1 @ id类型
2 16:占用内存
3 @:参数 id
4 0:从0号位置
5 :代表SEL
6 8:从8号位置

void RoObjc_copyIvar_copyProperies(Class pClass){
    
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList(pClass, &count);
    for (unsigned int i=0; i < count; i++) {
        Ivar const ivar = ivars[i];
        //获取实例变量名
        const char*cName = ivar_getName(ivar);
        NSString *ivarName = [NSString stringWithUTF8String:cName];
        LGLog(@"class_copyIvarList:%@",ivarName);
    }
    free(ivars);

    unsigned int pCount = 0;
    objc_property_t *properties = class_copyPropertyList(pClass, &pCount);
    for (unsigned int i=0; i < pCount; i++) {
        objc_property_t const property = properties[i];
        //获取属性名
        NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
        //获取属性值
        LGLog(@"class_copyProperiesList:%@",propertyName);
    }
    free(properties);
}

上述代码可以用来区分哪些是属性,哪些是成员变量,不再赘述。
现在我们来分析下为什么nickName的属性是objc_setProperty(抽象方法)和内存平移,
先贴下main.m的代码

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        RoPerson *person = [RoPerson alloc];
        person.name = @"Ro";
        RoTeacher *teacher = [RoTeacher alloc];
        Class pClass     = object_getClass(person);        
  
    }
    return 0;
}

属性&成员变量&实例变量的区别
属性 = 带下划线成员变量 + setter + getter ⽅法
实例变量 : 特殊的成员变量 (类的实例化)

上文我们说到nickName使用的是objc_setProperty和内存平移,而name不是,我们通过llvm源码分析(这里源码比较难懂,不贴出来了),是因为copy与strong的区别,copy做了特殊处理,默认是strong

类方法归属分析

iOS底层-类的探索分析之isa及继承链中的我们知道类的方法+ (void)say666没找到,我们通过MachoView分析下Demo的macho文件,如图:

6

我们从这里可以看到+ (void)say666这个类方法,- (void)sayNB是实例方法,也就是对象方法,是在类里面,避免浪费内存,类方法存储在元类中,比如+ (void)say666,我在定义一个- (void)say666,这时候怎么处理,这也是为什么存在元类中,我们验证下,如图:


7 8
我们从以下两幅图中,可以看出,类方法就是存储在元类中
void RoObjc_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 RoInstanceMethod_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 RoClassMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getClassMethod(pClass, @selector(sayHello));
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));

//    - (void)sayHello;
//    + (void)sayHappy;
    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);
}

以上代码(objc的Api)也可以获取对象方法,类方法的存储。

结语
这篇文章补充了iOS底层-类的探索分析之isa及继承链中的两个疑问点,如有错误,敬请批评指正。

相关文章

网友评论

      本文标题:iOS底层-类的探索分析之类的属性及类的方法

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