分类添加属性------使用篇

分类(Category)不能为类添加成员变量,因为类的空间大小在编译期就已经确定了,而分类属于运行时技术,但是在需要的时候可以增加存储属性来实现属性与对象的绑定.

常用的方法

使用分类(Category)为类增加属性大致有以下方法:

存储方法(相当于setter方法)

/** 
 * Sets an associated value for a given object using a given key and association policy.
 * 
 * @param object The source object for the association.
 * @param key The key for the association.
 * @param value The value to associate with the key key for object. Pass nil to clear an existing association.
 * @param policy The policy for the association. For possible values, see “Associative Object Behaviors.”
 * 
 * @see objc_setAssociatedObject
 * @see objc_removeAssociatedObjects
 */
OBJC_EXPORT void
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
                         id _Nullable value, objc_AssociationPolicy policy)
    OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);

 调用该方法可以保存,更新或者删除属性.需要四个参数:

  • object:需要绑定属性的源对象(需要把属性绑定到该对象上);
  • key:绑定关系的key值,用以标记绑定的属性,所以对同一个对象绑定属性时需要确保不同属性对应的key值唯一;
  • value:需要绑定的属性值,如果该值有效则会保存或者更新key对应的值;如果该值为nil,则会删除key对应的保存值;
  • policy:跟property的语义属性相似,主要是对象处理属性保存的相关细节,稍后做详细说明.

获取方法(相当于getter方法)


/** 
 * Returns the value associated with a given object for a given key.
 * 
 * @param object The source object for the association.
 * @param key The key for the association.
 * 
 * @return The value associated with the key \e key for \e object.
 * 
 * @see objc_setAssociatedObject
 */
OBJC_EXPORT id _Nullable
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
    OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);


调用该方法可以获取保存的属性值,需要两个参数:

  • object:需要获取绑定属性的源对象(也就是属性被绑定在哪个对象上);
  • key:被绑定属性的key值,要和保存时使用的key值保持一致才能正确取出存储的值.

删除绑定属性


/** 
 * Removes all associations for a given object.
 * 
 * @param object An object that maintains associated objects.
 * 
 * @note The main purpose of this function is to make it easy to return an object 
 *  to a "pristine state”. You should not use this function for general removal of
 *  associations from objects, since it also removes associations that other clients
 *  may have added to the object. Typically you should use \c objc_setAssociatedObject 
 *  with a nil value to clear an association.
 * 
 * @see objc_setAssociatedObject
 * @see objc_getAssociatedObject
 */
OBJC_EXPORT void
objc_removeAssociatedObjects(id _Nonnull object)
    OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);

调用该方法会删除对象上绑定的所有属性,需要参数:

  • object: 属性绑定的对象.

应用场景

在一些情况下,希望对象可以携带一些额外的属性,但是该对象偏偏没有这样的属性.对于自定义的类,可以通过该修源代码增加属性,但是对于没有源代码的系统类,如果单单为了增加一个属性就创建一个子类,有些得不偿失而且也没有必要,这时候就可以通过分类(Category)来实现.

例如在控制器中需要为UIButton添加一个identifier的属性:

@interface UIImageView (AddProperty)
@property (nonatomic, copy) NSString *sourceURL;

@end

@implementation UIImageView (AddProperty)
- (NSString *) sourceURL {
    return objc_getAssociatedObject(self, _cmd);
}
- (void)setSourceURL:(NSString *) sourceURL {
    objc_setAssociatedObject(self, @selector(sourceURL), identifier, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
@end



@implementation CustomerViewController
- (void)viewDidload {
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:(CGRect){.origin=CGPointZero, .size=(CGSize){60, 60}}];
    //加载网络图片
    [imageView setImageWithURL:url];
    //存储图片源
    [imageView setIdentifier:url];

    [imageView setUserInteractionEnabled:true];
    [imageView addGestureRecognizer:({
     UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(click:)];
        tap;
    })];

    [self.view addSubview:imageView];
}

- (void) click:(UITapGestureRecognizer *)tap {

    /*
        只是做个演示,提供一种思路,或许有人会说用tag也可以区分,勿喷

*/

    NSString *identifier = [(UIImageView *)tap.view identifier];
    NSLog(@"identifier == %@", identifier);
    if ([identifier isEqualToString:@"identifier_login"]) {
    //do some operations for special identifier
    }
}

@end

其中关于key值的取用方法,大概有以下两种:

  • 使用静态字符,例如SDWebImage,AFN以及MJRefresh中就大量使用了这种方式
static char AFBackgroundImageDownloadReceiptNormal;
static char AFBackgroundImageDownloadReceiptHighlighted;
static char AFBackgroundImageDownloadReceiptSelected;
static char AFBackgroundImageDownloadReceiptDisabled;

static const char * af_backgroundImageDownloadReceiptKeyForState(UIControlState state) {
    switch (state) {
        case UIControlStateHighlighted:
            return &AFBackgroundImageDownloadReceiptHighlighted;
        case UIControlStateSelected:
            return &AFBackgroundImageDownloadReceiptSelected;
        case UIControlStateDisabled:
            return &AFBackgroundImageDownloadReceiptDisabled;
        case UIControlStateNormal:
        default:
            return &AFBackgroundImageDownloadReceiptNormal;
    }
}

- (AFImageDownloadReceipt *)af_backgroundImageDownloadReceiptForState:(UIControlState)state {
    return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, af_backgroundImageDownloadReceiptKeyForState(state));
}

- (void)af_setBackgroundImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt
                                     forState:(UIControlState)state
{
    objc_setAssociatedObject(self, af_backgroundImageDownloadReceiptKeyForState(state), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
...
  • 使用@selector,腾讯云作者就比较倾向于这一种方式(如果是使用当前方法的sel,也可以使用_cmd代替):
...
- (BOOL)enableMD5Verification {
    NSNumber* number = objc_getAssociatedObject(self, @selector(enableMD5Verification));
    return [number boolValue];
}

- (void)setEnableMD5Verification:(BOOL)enableMD5Verification {
    NSNumber* number = [NSNumber numberWithBool:enableMD5Verification];
    objc_setAssociatedObject(self, @selector(enableMD5Verification), number, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

...

​​​​​​总而言之,key就是一个指针,保持存取一致就好了.

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值