建议先看下
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的指令,可以将具体的类型表示成字符串编码。
- @encode实际上是编译器指令其中的一种。
- @encode能够返回一个Objective-C 类型编码(Objective-C Type Encodings)。
- @encode是一种编译器内部表示的字符串,方便识别,类似于 ANSI C 的 typeof 操作。
在Objective-C中,用@encode指令的方式来表示,可以方便Runtime内部利用类型编码帮助加快消息分发。

当然我们也可以自己尝试打印一下
#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;
}

那么以这个为例子, 我们读一下"@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方法读取


例如读取下{{"name","T@\"NSString\",C,N,V_name"},
-
T: type -
@: 变量类型 -
C: copy -
N: nonatomic -
V: variable 变量,即下划线变量 _Name