美文网首页
IOS底层(十): 类相关: 成员变量与属性

IOS底层(十): 类相关: 成员变量与属性

作者: ShawnAlex | 来源:发表于2021-05-11 11:28 被阅读0次

    OC底层源码/原理合集

    建议先看下
    IOS底层(九): 类相关: 类结构分析
    IOS底层(八): alloc相关: isa与类关联源码分析

    @interface ViewController (){
        
        NSString *name ; // 成员变量, 实例变量
        int age;   // 成员变量, 基本数据类型变量
        id data;   // 成员变量, 实例变量
    }
    
    @property (nonatomic, strong) NSString *hobby;  //属性
    
    @end
    

    成员变量

    • 通常在.h/.m文件@interface以{ } 形式定义的变量
    成员变量的访问权限
    @interface ViewController (){
        
        NSString * A; 
        
    @public
        NSString * B;
        
    @protected
        NSString * C;
        
    @private
        NSString * D;
        
        @package
        NSString * E;
    }
    
    • @public:在任何地方都能直接访问对象的成员变量

    • @private:只能在当前类的对象方法中直接访问, 如果子类要访问需要调用父类的get/set方法

    • @protected:可以在当前类及其子类对象方法中直接访问,变量默认的访问权限就是 protected

    • @package:只能在framework内部的类是@protected的权限,对于外部的类是@private,相当于框架级的保护权限,适合使用在静态库.a中。

    实例变量

    • 如果成员变量是一个(类的实例化), 则这个变量为实例变量, 例如上面例子name, data (id 是 OC特有的类型。从本质上讲, id 等同于 (void *))都是实例变量, 而age是int型, 是基础数据类型变量
    • 实例变量 + 基础数据类型变量 = 成员变量

    属性 (属性变量)

    • 一般用 @property表示

    • 编译器会自动为属性生成set, get方法, 以及生成成员变量_documentsDirectory(即成员变量名前加下划线)

    • 成员属性包含了成员变量

    • 可以通过点语法访问属性,编译器会把点语法转换为对存取方法的调用 (使用“点语法”的效果与直接调用存取方法相同)。self.调用,即self.documentsDirectory,如果想用self->调用成员属性就只能self->_documentsDirectory, 这样调用太麻烦, 一般会再用@synthesize对带底杠的成员属性名重新定名

    @synthesize fileName, documentsDirectory

    这样就可以直接访问成员属性名self->documentsDirectory

    • 属性是用于与其他对象交互的变量, 正因为要与其他对象交互, 就有了属性修饰符或者叫属性特质, 如:nonatomic, readwrite, copy 等等

    属性/成员变量本质 (底层)

    首先建立一个main项目, 添加些成员变量, 属性如图(创建只有main.m项目)

    属性成员变量例子

    clang一下生成cpp文件

    clang -rewrite-objc main.m -o main.cpp
    

    建议先看下
    IOS底层(八): alloc相关: isa与类关联源码分析 前面的Clang那里

    通过我们通过TestObj查找 (因为属性/成员变量都是TestObj的), 来到这里

    属性成员变量底层

    ① 首先可看到TestObj 来自于 NSObject的继承,
    属性在底层会被编译成成员变量, 区别是带下划线 _
    属性在底层会自动生成set, get方法, 而成员变量不会有

    ④ 同时还有我们之前得到的结论

    • 通过@interface XXXX {}定义的成员变量,会存储在类的bits属性中,通过bits --> data() -->ro() --> ivars获取成员变量列表,除了包括成员变量,还包括属性的成员变量

    • 通过@property定义的属性,也会存储在bits属性中,通过bits --> data() --> properties() --> list获取属性列表,其中只包含property属性


    接下来往后看

    属性成员变量底层

    可看到每一个方法都有一个sel, imp

    • sel : 方法编号, 可以理解成一本书的目录, 可通过对应名称找到页码

    • imp : 函数指针地址, 可以理解成书的页码, 方便找到具体实现的函数

    • T, @, v在底层是一些签名, Type Encodings里面有详细介绍

    iOS中提供了一个叫做@encode的指令,可以将具体的类型表示成字符串编码。

    1. @encode实际上是编译器指令其中的一种。
    2. @encode能够返回一个Objective-C 类型编码(Objective-C Type Encodings)。
    3. @encode是一种编译器内部表示的字符串,方便识别,类似于 ANSI C 的 typeof 操作。

    在Objective-C中,用@encode指令的方式来表示,可以方便Runtime内部利用类型编码帮助加快消息分发。

    Type Encodings 苹果官方

    Property Type String 苹果官方

    Type Encodings

    当然我们也可以自己尝试打印一下

    #pragma mark - 各种类型编码
    void lgTypes(){
        NSLog(@"char --> %s",@encode(char));
        NSLog(@"int --> %s",@encode(int));
        NSLog(@"short --> %s",@encode(short));
        NSLog(@"long --> %s",@encode(long));
        NSLog(@"long long --> %s",@encode(long long));
        NSLog(@"unsigned char --> %s",@encode(unsigned char));
        NSLog(@"unsigned int --> %s",@encode(unsigned int));
        NSLog(@"unsigned short --> %s",@encode(unsigned short));
        NSLog(@"unsigned long --> %s",@encode(unsigned long long));
        NSLog(@"float --> %s",@encode(float));
        NSLog(@"bool --> %s",@encode(bool));
        NSLog(@"void --> %s",@encode(void));
        NSLog(@"char * --> %s",@encode(char *));
        NSLog(@"id --> %s",@encode(id));
        NSLog(@"Class --> %s",@encode(Class));
        NSLog(@"SEL --> %s",@encode(SEL));
        int array[] = {1,2,3};
        NSLog(@"int[] --> %s",@encode(typeof(array)));
        typedef struct person{
            char *name;
            int age;
        }Person;
        NSLog(@"struct --> %s",@encode(Person));
        
        typedef union union_type{
            char *name;
            int a;
        }Union;
        NSLog(@"union --> %s",@encode(Union));
    
        int a = 2;
        int *b = {&a};
        NSLog(@"int[] --> %s",@encode(typeof(b)));
    }
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // insert code here...
            lgTypes();
            NSLog(@"Hello, World!");
        }
        return 0;
    }
    
    
    image.png

    那么以这个为例子, 我们读一下"@16@0:8"

    {(struct objc_selector *)"nickname", "@16@0:8", (void *)_I_TestObj_nickname}
    
    static NSString * _I_TestObj_nickname(TestObj * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_TestObj$_nickname)); }
    
    • @: 为返回值
    • 16: 为总共占用字节16字节
    • @: 为第一个参数 id统配类型占8字节(系统自动生成的typedef struct objc_object *id)
    • 0: 从0开始
    • 冒号: : sel, 占8字节(系统自动生成的sel _cmd)
    • 8: 从位置8开始

    clang编译输出了属性的attribute ,同样也可以通过property_getAttributes方法读取

    property type string attribute

    例如读取下{{"name","T@\"NSString\",C,N,V_name"},

    • T: type
    • @: 变量类型
    • C: copy
    • N: nonatomic
    • V: variable 变量,即下划线变量 _Name

    相关文章

      网友评论

          本文标题:IOS底层(十): 类相关: 成员变量与属性

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