半路出家, 我的iOS自学之路-3-属性, @property, @synthesize, @dynamic, 用类别动态添加"属性"

xiaoxiao2021-02-28  21

半路出家, 我的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

这样一来, 只要 ” #import “NSString+CNNSString.h” ” 这个头文件的所有 类, 类 里的 NSString 对象 都有了一个叫 “age” 的 “属性”, 当然这里的 “属性” 肯定不是定义在 类 的 .h文件 里的那种 属性, 而是利用 “运行时” 里的函数, 通过 类别 动态的向 类 里添加的 “属性”.

转载请注明原文地址: https://www.6miu.com/read-2625324.html

最新回复(0)