半路出家, 我的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文件
#import <Foundation/Foundation.h>
// "类别" 只能添加方法, 不能添加 "实例变量"
// 本章将详细讲解如何利用 "runtime" 实现向 "类别" 添加 "实例变量"
@interface NSString (CNNSString)
@property (nonatomic) NSString *age; // 提醒编译器自动生成 getter/setter 方法
@end
.m文件
#import "NSString+CNNSString.h"
#import <objc/runtime.h> // 必须导入 "运行时"库, 才能实现这个功能
@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. 引入头文件: #import <objc/runtime.h>
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