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

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底层(十): 类相关: 成员变量与属性)