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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容