✅作者简介:大家好我是瓜子三百克,一个非科班出身的技术程序员,还是喜欢在学习和开发中记录笔记的博主小白!
📃个人主页:瓜子三百克的主页
🔥系列专栏:OC语法
🤟格言:作为一个程序员都应该认识到,好的代码是初级工程师都可以理解的代码, 伟大的代码是可以被学习一年计算机专业的新生所理解。
💖如果觉得博主的文章还不错的话,请点赞👍+收藏⭐️+留言📝支持一下博主哦🤞
本篇文章告诉你,如果在类对象和Category对象中添加一个属性时,底层做了什么。关联对象如何给Category对象添加成员变量,关联对象的实现原理。
文章目录
一、添加属性
1.1、类中添加属性
在类中添加一个 name 属性时,具体做了哪些事呢?
1、定义一个 _name 成员变量
2、分别生成一个setter 方法和一个 getter 方法的声明
3、生成以上两个方法的具体实现。
如在 Person.h 添加一个 age 属性:
@interface Person : NSObject
@property (assign, nonatomic) int age;
@end
编译后,代码会直接帮我们生成如下结构:
Person.h :
@interface Person : NSObject {
int _age;
}
- (void)setAge:(int)age;
- (int)age;
@end
Person.m :
#import "Person.h"
@implementation Person
- (void)setAge:(int)age {
_age = age;
}
- (int)age {
return _age;
}
@end
1.2、Category对象中添加属性
那么在Category中添加属性呢?他只会帮我们生成方法的声明,类似如下:
- (void)setAge:(int)age;
- (int)age;
没有变量和方法的实现。看来,在Category中是无法直接通过声明属性的方式记录成员变量。
那么就无法在Category中添加属性了吗?
在默认情况下,因为Category底层结构的限制,不能添加成员变量到Category中。但可以通过关联对象来间接实现。
二、关联对象
2.1、关联对象API
关联对象提供了以下API:
// 添加关联对象
// object:对象,即我们要给哪个对象关联属性的对象。
// key:定义的检索键
// value:关联的属性值,即 setter 方法传进来的值
// policy:关联策略,见下表
void objc_setAssociatedObject(id object, const void * key,
id value, objc_AssociationPolicy policy)
// 获得关联对象
id objc_getAssociatedObject(id object, const void * key)
// 移除所有的关联对象
void objc_removeAssociatedObjects(id object)
2.2、key的多种赋值方式
通过关联对象来间接实现Category也能添加成员变量。
key有多种赋值方式,我们来看下key的几种常见用法:
1、赋值变量key为自己的地址值
// static:设置变量的作用域为当前文件
static void *MyKey = &MyKey;// MyKey对象必须赋值,否者默认为0,当设置多个成员变量会有问题。
objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, MyKey)
2、定义key为 char
类型(1个字节),减小占用字节
static char MyKey;// 定义变量时,即生成一个地址值
objc_setAssociatedObject(obj, &MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, &MyKey)
3、使用属性名作为key
objc_setAssociatedObject(obj, @"property", value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_getAssociatedObject(obj, @"property");
注意:因为直接写出来的字符串,其地址是是放在数据常量区的,这个地址和值是不变的。即使是多次使用,其地址和值都是不变的。
4、使用get方法的@selecor作为key
objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, @selector(getter))
// _cmd:表示当前方法 @selector
// _cmd == @selector(getter)
objc_getAssociatedObject(obj,_cmd)
2.3、实战引用
新建 Person
类 和 Person+Test
分类
1、 Person.h:
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (assign, nonatomic) int age;
@end
2、 Person.h:
#import "Person.h"
@implementation Person
@end
3、Person+Test.h:
#import "Person.h"
@interface Person (Test)
@property (assign, nonatomic) int weight;
@property (copy, nonatomic) NSString* name;
@end
4、Person+Test.m:
#import "Person+Test.h"
#import <objc/runtime.h>
@implementation Person (Test)
- (void)setName:(NSString *)name {
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
// 隐式参数
// _cmd == @selector(name)
return objc_getAssociatedObject(self, _cmd);
}
- (void)setWeight:(int)weight {
objc_setAssociatedObject(self, @selector(weight), @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (int)weight {
// _cmd == @selector(weight)
return [objc_getAssociatedObject(self, _cmd) intValue];
}
@end
5、引用赋值
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Person+Test.h"
#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
person.age = 10;
person.name = @"lisa";
person.weight = 110;
Person *person2 = [[Person alloc] init];
person2.age = 20;
person2.name = @"tony";
// person2.name = nil;
person2.weight = 220;
NSLog(@"person>>> age:%d, name:%@, weight:%d", person.age, person.name, person.weight);
NSLog(@"person2>>> age:%d, name:%@, weight:%d", person2.age, person2.name, person2.weight);
}
return 0;
}
三、关联对象的原理
实现关联对象技术的核心对象有:
AssociationsManager
AssociationsHashMap
ObjectAssociationMap
ObjcAssociation
objc4源码解读:objc-references.mm,关键代码如下:
通过源码解析,我们可以知道,在调用关联对象api的:
void objc_setAssociatedObject(id object, const void * key,
id value,
objc_AssociationPolicy policy)
各个核心对象的关系图如下:
总结:
1、关联对象并不是存储在被关联对象本身内存中。
2、关联对象存储在全局的统一的一个AssociationsManager
中。
3、设置关联对象属性为 nil
,就相当于是移除 ObjectAssociationMap
中对应属性的关联对象
4、当调用 void objc_removeAssociatedObjects(id object)
时,是移除 object 对象中的所有关联对象。即移除 AssociationsHashMap
对象中,以 DISGUISE(object)
为键的 ObjectAssociationMap
值。
四、拓展
1、Category能否添加成员变量?如果可以,如何给Category添加成员变量?
不能直接给Category添加成员变量,但是可以间接实现Category有成员变量的效果。
使用关联对象的方式实现给Category添加成员变量。
**🏆结束语🏆 **
最后如果觉得我写的文章对您有帮助的话,欢迎点赞✌,收藏✌,加关注✌哦,谢谢谢谢!!