建议先看下
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