Adding Properties to a Category Using Associated Objects

Object-C中的Category

Object-C中的Category,相信大家一定不会陌生。一般来说,我们可以用它来为一个类添加新的方法:

@interface NSString (NumberUtils)
- (BOOL)isNumeric; 
@end

@implementation NSString (NumberUtils) 
- (BOOL)isNumeric
{
    NSScanner *scanner = [NSScanner scannerWithString:self];
    return [scanner scanFloat:NULL]? [scanner isAtEnd]: NO;
}
@end

我们也可以Category中定义新的数据成员,但是不能够对它们使用synthesize。这是因为一个类的结构已经在编译器决定了,而Category是在运行期定义生成的,这样就没有办法改变类结构体中的ivars。

struct objc_class {  
    Class isa  OBJC_ISA_AVAILABILITY;  

    #if !__OBJC2__  
    Class super_class                                        OBJC2_UNAVAILABLE;  
    const charchar *name                                     OBJC2_UNAVAILABLE;  
    long version                                             OBJC2_UNAVAILABLE;  
    long info                                                OBJC2_UNAVAILABLE;  
    long instance_size                                       OBJC2_UNAVAILABLE;  
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;  
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;  
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;  
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;  
#endif  

} OBJC2_UNAVAILABLE;    

可以向下面这样定义:

@interface NSString (NumberUtils)
@property (nonatomic, readonly, getter=isNumeric) BOOL numeric;
@end

如果我们想为一个类真正添加一个数据成员,要怎么办呢?当然你可能会想到使用修饰者,封装出一个对象,包含这个类和想要添加的数据成员。但是那样要麻烦一些。

使用Associated Objects为一个分类添加数据成员

使用Runtime来解决这个问题。要使用的API:

 // 使用一个key值和关联策略来为一个对象设置一个关联的值
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);

objc_setAssociatedObject:使用一个key值和关联策略来为一个对象设置一个关联的值。类似NSDictionry,一个key值,对应一个value值。

key值必须是进程的生命周期内一个唯一、不变的id值。可以使用一个NSString对象作为一个key值。但是如果试图通过一个相同的字符串值,但是不同内存地址作为key值,可能不会获得预期的结果。一个更好的选择是定义一个static的指针作为key值。

下面一个例子:

@interface UIImage (Tagged)
@property (nonatomic, copy) NSString *tag;
@end    

#import <objc/runtime.h> 

static const void *ImageTagKey = &ImageTagKey;
@implementation UIImage (Tagged)

- (void)setTag:(NSSting *)tag
{
    objc_setAssociatedObject(self, ImageTagKey, tag, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)tag
{
    return objc_getAssociatedObject(self, ImageTagKey);
}
@end

这个例子中有一点点需要主要:

  • key值使用一个指针类型static const void * 。我们必须让这个指针初始化,否则它的值就会为NULL,但是我们不关心具体指向什么,只要它是唯一的,不变的。这里面,指针指向了自己,一个唯一、不变的值。

参考文章:

Objective-C Runtime Reference

Adding Properties to a Category Using Associated Objects

Associated Objects