iOS-Runtime-使用关联对象给Category添加成员变

2018-09-07  本文已影响41人  Simple_Code

开发问题:需要在一个类的Category中添加属性;
实际实践:通过 Category 给一个现有的类添加属性,却不能添加实例变量;
解决方案:通过runtime建立关联引用;

image.png

从上图分类在OC源码中的结构体,可以看出里面不存在成员变量列表。因此,分类里面的属性在外边是无法通过点语法调用或者get方法的获取的,但是我们可以使用关联对象给Category添加成员变量。从而达到一般类里面属性的正常调用。下面提供四种方法:

首先介绍两个Runtime相关的关联函数:

    /**
     @param object#> 第一个参数为从该object中获取关联对象
     @param key#> 第二个参数为想要获取关联对象的key
     @param value#> 第三个参数为需要和object建立关联引用对象的value
     @param policy#> 第四个参数为关联策略,等同于给property添加关键字,具体说明如下图关联策略对应图
     */
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
                         id _Nullable value, objc_AssociationPolicy policy)

    /**
     @param object#> 第一个参数为从该object中获取关联对象
     @param key#> 第二个参数为想要获取关联对象的key
     */
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
    OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
关联策略对应图.png

方法1:推荐使用

#import "Person.h"

@interface Person (Test)
@property (nonatomic, copy) NSString *name;
@end

#import "Person+Test.h"
#import <objc/runtime.h>

@implementation Person (Test)

- (void)setName:(NSString *)name {
    objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY);
}
- (NSString *)name {
    return objc_getAssociatedObject(self, @selector(name));
}

或者

#import "Person.h"

@interface Person (Test)
@property (nonatomic, copy) NSString *name;
@end

#import "Person+Test.h"
#import <objc/runtime.h>

@implementation Person (Test)

- (void)setName:(NSString *)name {
    objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY);
}
- (NSString *)name {
    return objc_getAssociatedObject(self, _cmd);
}

注:

_cmd = @selector(name) 隐式参数

方法2:

#import "Person.h"

@interface Person (Test)
@property (nonatomic, copy) NSString *name;
@end

#import "Person+Test.h"
#import <objc/runtime.h>

@implementation Person (Test)

#define SPNameKey @"name"
 - (void)setName:(NSString *)name {
 objc_setAssociatedObject(self, SPNameKey, name, OBJC_ASSOCIATION_COPY);
 }
 - (NSString *)name {
 return objc_getAssociatedObject(self, SPNameKey);
 }

注:

SPNameKey必须和该Value名称一致

方法3:

#import "Person.h"

@interface Person (Test)
@property (nonatomic, copy) NSString *name;
@end

#import "Person+Test.h"
#import <objc/runtime.h>

@implementation Person (Test)

static const char SPNameKey;
- (void)setName:(NSString *)name {
    objc_setAssociatedObject(self, &SPNameKey, name, OBJC_ASSOCIATION_COPY);
}
- (NSString *)name{
    return objc_getAssociatedObject(self, &SPNameKey);
}

方法4:

#import "Person.h"

@interface Person (Test)
@property (nonatomic, copy) NSString *name;
@end

#import "Person+Test.h"
#import <objc/runtime.h>

@implementation Person (Test)

 static const void *SPNameKey = &SPNameKey;
 
 - (void)setName:(NSString *)name {
 objc_setAssociatedObject(self, SPNameKey, name, OBJC_ASSOCIATION_COPY);
 }
 - (NSString *)name{
 return objc_getAssociatedObject(self, SPNameKey);
 }

那么如何添加Button对象?

代码如下:

@interface UIView (Test)
@property (nonatomic, strong) UIButton *hideButton;
@end

//setter
- (void)setHideButton:(UIButton *)hideButton {
    objc_setAssociatedObject(self, @selector(hideButton), hideButton, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
//getter 
- (UIButton *)hideButton {
    UIButton *_hideButton = objc_getAssociatedObject(self, @selector(hideButton));
    if (!_hideButton) {
        _hideButton = [UIButton buttonWithType:UIButtonTypeCustom];
        _hideButton.frame = CGRectMake(self.bounds.size.width/2-110, 260, 220, 44);
        _hideButton.backgroundColor = [UIColor brownColor];
        [_hideButton setTitle:@"Hide" forState:UIControlStateNormal];
        objc_setAssociatedObject(self, @selector(hideButton), _hideButton, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return _hideButton;
}

上一篇 下一篇

猜你喜欢

热点阅读