美文网首页iOS DeviOS日常须知iOS 知识点
iOS 给分类(Category文件)添加属性

iOS 给分类(Category文件)添加属性

作者: 差不多先生__ | 来源:发表于2018-04-18 20:46 被阅读1005次

一直都知道分类里面添加属性,会提示黄色警告,使用的时候运行到这里会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);
}

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)。

在匿名分类里面写上上述代码后,执行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,             //关联对象的属性是弱引用    
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,   //关联对象的属性是强引用并且关联对象不使用原子性
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,     //关联对象的属性是copy并且关联对象不使用原子性
    OBJC_ASSOCIATION_RETAIN = 01401,         //关联对象的属性是copy并且关联对象使用原子性
    OBJC_ASSOCIATION_COPY = 01403            //关联对象的属性是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);
}

/**
 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

相关文章

网友评论

本文标题:iOS 给分类(Category文件)添加属性

本文链接:https://www.haomeiwen.com/subject/mekbkftx.html