一直都知道分类里面添加属性,会提示黄色警告,使用的时候运行到这里会crash,那么就真的不能给分类里面添加属性吗?答案当然是可以的,那么怎么添加呢,那么我们先创建一个Person的分类,继承NSObject
,在.h文件里面添加一条name的属性
@property (nonatomic, copy) NSString *name;
当然也可以在.m匿名分类里面添加属性,只是这样的属性只能在这个分类里面使用,不能在类的实例中使用。.h文件中添加的在类的实例中也可以使用。然后在.m引入runtime的头文件
#import <objc/runtime.h> 或者 #import <objc/message.h> //这两种都可以
接下来先在.m文件为我们的name属性添加一个key
static NSString *nameKey = @"nameKey"; //name的key
这时候执行Xcode命令command + b,在匿名分类里面就会出现黄色警报,如下
Property 'name' requires method 'name' to be defined - use @dynamic or provide a method implementation in this category
运行会crash并提示
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSObject setName:]: unrecognized selector sent to instance 0x600000002db0'
这句崩溃的提示是找不到setName:
方法,下面就是给分类属性添加setter方法
- (void)setName:(NSString *)name {~~
objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
在setName:
里面使用了一个objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)
方法,这个方法有四个参数,分别是:
1.源对象(self)
2.关联时的用来标记是哪一个属性的key(因为你可能要添加很多属性,这里咱们填写的是&nameKey)
3.关联的对象(name)
4.一个关联策略(OBJC_ASSOCIATION_COPY_NONATOMIC)。
在匿名分类里面写上上述代码后,执行Xcode命令command+b,此时匿名分类提示黄色警报
Property 'name' requires method 'name' to be defined - use @dynamic or provide a method implementation in this category
如果运行给分类里面的name属性赋值(执行setter方法)是没有问题的
NSObject *objc = [[NSObject alloc] init];
objc.name = @"almost";
但是如果获取name的值,例如执行NSLog(@"%@", objc.name);
,那么运行到这里就会crash并提示
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSObject name]: unrecognized selector sent to instance 0x6000000064a0'
这句话的意思就是你没有实现name的getter方法,将getter方法在匿名类实现
- (NSString *)name {
return objc_getAssociatedObject(self, &nameKey);
}
在这里用到了objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
这个方法,这个方法有两个参数,填写方法参照setter方法
//利用静态变量地址唯一不变的特性
static NSString *nameKey = @"nameKey";
static void *nameKey = &nameKey;
static char nameKey;
关联策略是个枚举值,解释如下:
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, //好比assign关联对象的属性是弱引用,
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, //好比strong, nonatomic,关联对象的属性是强引用并且关联对象不使用原子性,
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, //好比copy, nonatomic,关联对象的属性是copy并且关联对象不使用原子性
OBJC_ASSOCIATION_RETAIN = 01401, //好比strong, atomic,关联对象的属性是copy并且关联对象使用原子性
OBJC_ASSOCIATION_COPY = 01403 //好比copy, atomic,关联对象的属性是copy并且关联对象使用原子性
};
那么使用runtime给分类添加属性的全部代码就是
.h文件
#import <Foundation/Foundation.h>
@interface NSObject (Person)
@property (nonatomic, copy) NSString *name;
@end
.m文件
#import "NSObject+Person.h"
#import <objc/runtime.h> /*或者 #import <objc/message.h>*/
static NSString *nameKey = @"nameKey"; //那么的key
@interface NSObject ()
@end
@implementation NSObject (Person)
/**
setter方法
*/
- (void)setName:(NSString *)name {
objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
/**
getter方法
*/
- (NSString *)name {
return objc_getAssociatedObject(self, &nameKey);
}
@end
在其他类的实例种中调用
- (void)viewDidLoad {
NSObject *objc = [[NSObject alloc] init];
objc.name = @"almost";
NSLog(@"%@", objc.name);
}
另附上代码,点击代码地址或点击链接https://github.com/173323222/CategoryProperty