iOS - Runtime 动态添加属性

2021-05-15  本文已影响0人  码代码的小马

我们在开发中常常使用类目Category为一些已有的类扩展功能。虽然继承也能够为已有类增加新的方法,而且相比类目更是具有增加属性的优势,但是继承毕竟是一个重量级的操作,添加不必要的继承关系无疑增加了代码的复杂度。

遗憾的是,OC的类目并不支持直接添加属性,如果我们直接在分类的声明中写入Property属性,那么只能为其生成set与get方法声明,却不能生成成员变量,直接调用这些属性还会造成崩溃。

所以为了实现给分类添加属性,我们还需借助Runtime的关联对象(Associated Objects)特性,它能够帮助我们在运行阶段将任意的属性关联到一个对象上,下面是相关的三个方法:

usr/include/runtime.h

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

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

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

注意:key与关联属性一一对应,我们必须确保其全局唯一性,通常我们使用@selector(methodName)做为key.

以下为示例代码:为UIImage添加一个分类:UIImage+Tools,并为其设置urlString(图片网络链接属性)

@interface UIImage (Tool)

@property (nonatomic, copy) NSString *urlString;

- (void)clearAssociatedObject;

@end
//
//  UIImage+Tool.m
//  LoadInitializeDemo
//
//  Created by Ternence on 2021/5/12.
//

#import "UIImage+Tool.h"
#import <objc/runtime.h>


@implementation UIImage (Tool)

- (void)setUrlString:(NSString *)urlString {
    objc_setAssociatedObject(self, @selector(urlString), urlString, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)urlString {
    return objc_getAssociatedObject(self, @selector(urlString));
}

- (void)clearAssociatedObject {
    objc_removeAssociatedObjects(self);
}

@end

测试调用:

    UIImage *image = [[UIImage alloc] init];
    image.urlString = @"http://www.baidu.com";
    NSLog(@"=====urlString: %@", image.urlString);
    [image clearAssociatedObject];
    NSLog(@"=======urlString: %@",image.urlString);

打印输出:

2021-05-12 16:26:02.880219+0800 LoadInitializeDemo[98906:3244158] =====urlString: http://www.baidu.com
2021-05-12 16:26:02.880376+0800 LoadInitializeDemo[98906:3244158] =======urlString: (null)
上一篇下一篇

猜你喜欢

热点阅读