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

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

  • 我是一只绝望的菜鸟, 只学过Java, 半路出家, 自学iOS.
  • 以下是我读完《Objective - C 编程》(第2版)的读书笔记
  • 博客中出现任何差错, 遗漏, 还请您在评论中告诉我
  • 群号:653592167, 欢迎自学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文件 里的那种 属性, 而是利用 “运行时” 里的函数, 通过 类别 动态的向 类 里添加的 “属性”.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值