半路出家, 我的iOS自学之路-3-属性, @property, @synthesize, @dynamic, 用类别动态添加”属性”
只学过Java, 半路出家, 自学iOS. 以下是我读完《Objective - C 编程》(第2版)的读书笔记博客中出现任何差错, 遗漏, 还请您在评论中告诉我群号:(空), 欢迎自学iOS的人加入, 一起交流, 共同成长
以”@”开头, “@”是OC语言的标志, 表示你正在使用OC语言
跟 属性 有关的3个”@”指令
@property 属性: 提醒编译器自动帮你生成 getter/setter 和 成员变量.
- 如果没有手动定义 成员变量, @property 指令将自动生成和 属性 同名的 成员变量, 并且 成员变量 的名字以下划线"_"开头, 例如 :
@property (nonatomic, strong) NSString *name; // 将自动生成 成员变量 NSString *_name;
- 如果已经手动声明了以下划线"_"开头的 成员变量, 并且 成员变量名 和 属性名 除下划线以外都相同, 则 @property指令只生成 getter/setter, 不再生成 成员变量, 而是将两者进行关联,例如 :
@interface A { NSString *_name; } @property (nonatomic, strong) NSString *name; // 只生成 getter/setter , 不会重复生成 _name; @end
@synthesize 合成: 提醒编译器自动帮你生成等号 (“=”) 右边的 成员变量, 并且指定这个 成员变量 的 getter/setter 的名称, 为等号 (“=”) 的左边的 属性的名称.
- 如果已经手动定义了 成员变量, 这个指令的作用就是: 关联 成员变量 和 getter/setter, 例如 :
@syntheseize name = gaga; // 编译器自动生成 成员变量 gaga, 并且 成员变量gaga 的 getter/setter 是 name;
- 如果指令后面只写了一个 属性名, 那么将自动生成跟 属性同名 的 成员变量, 例如 :
@synthesize name; // 此时 属性的 getter/setter 是 name, 成员变量 是 name; // 这个时候 成员变量 和 属性 就傻傻分不清楚了 // 这也是为什么OC语法规定, 成员变量 必须以下划线”_”开头, 就是为了避免出现这个情况. // 当我们用以下划线”_”开头的变量的时候,说明我们正在使用 成员变量, 当我们用 self.name 的时候, 说明我们正在用 属性, 一目了然;
@dynamic 动态合成: 告诉编译器, getter/setter 将由程序员手动去实现, 编译器不再报错.
- 这里举一个例子: 用 类别 实现"动态"向 类 添加 属性, 来帮助理解@dynamic指令的用法
.h文件
//
"类别" 只能添加方法, 不能添加
"实例变量"
// 本章将详细讲解如何利用
"runtime" 实现向
"类别" 添加
"实例变量"
@interface NSString (CNNSString)
@property (nonatomic) NSString *age; // 提醒编译器自动生成 getter/setter 方法
@end
.m文件
@implementation NSString (CNNSString)
//
@dynamic(中文:动态), 修饰@property, 仅仅是告诉 编译器 age 的 getter/setter 由程序员 "手动添加", 或者是在程序 "运行时(runtime)" 生成;
@dynamic age;
/*
查看方法原型, 里面的 key值 必须是一个
"对象" 级别的
"唯一" "常量";
只要满足上述
"三个条件" 即可成为key, 即:
1."对象" 级别(一级指针);
2.唯一;
3.常量.
<举例:三种不同的key值>
1. static void *kAssociatedObjectKey = &kAssociatedObjectKey;
2. 用 selector ,使用 getter 方法的名称作为 key 值.
3. static char kAssociatedObjectKey; objc_getAssociatedObject(self, &kAssociatedObjectKey);
4. 只要满足
"三个条件"(
1."对象" 级别;
2.唯一;
3.常量.) 都可以;
实现原理:
1. 引入头文件:
2. 在
"堆内存" 中
"指定" 一块
"内存区域", 并将该
"内存区域" 的起点(地址) 即
"指针" 保存在有该
"类别" 中, 并提供
"方法" 去调用, 这样就实现了利用
"类别" 的
"动态" 添加
"方法" 这一特点, 实现了 向
"类" "动态" 添加
"实例变量".
PS:
"类别" 中只能添加
"方法", 不能添加
"实例变量".
*/
//static const void *externAge = &externAge;
- (void)setAge:(NSString *)age
{
objc_setAssociatedObject(self,
@selector(age), age, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)age
{
return objc_getAssociatedObject(self,
@selector(age));
}
@end
这样一来, 只要 ” #import “NSString+CNNSString.h” ” 这个头文件的所有 类, 类 里的 NSString 对象 都有了一个叫 “age” 的 “属性”, 当然这里的 “属性” 肯定不是定义在 类 的 .h文件 里的那种 属性, 而是利用 “运行时” 里的函数, 通过 类别 动态的向 类 里添加的 “属性”.