Objective-C中扩展方法、属性和数据成员的解决方案以及方法拦截

最近在项目中使用一个XML文件描述应用程序的界面,内容类似如下:
<UIView
    id="View_sendmoney"
    frame="0,0,320,480">
    <UITextField
        id="account"
        frame="10,10,300,35"
    />
    <UITextField
        id="amount"
        frame="10,55,300,35"
    />
    <UIButton
        id="submit"
        frame="10,100,300,40"
    />
</UIView>


2. 扩展属性
上面的内容经过layout inflate后会生成view树,其中每个view有一个id属性,这个属性在后续的视图处理中会用到。因此我们需要给UIView扩展一个属性:

@interface UIView (DOMExtensions)
@property(nonatomic, copy) NSString *viewId;
@end

3. 扩展数据成员
OC中的每个对象,在运行时期都有一个对象关联表,我们可以操作该对象关联表来实现动态扩展一个对象的数据成员。使用下面的函数向表中添加或删除一个关联对象:

void objc_setAssociatedObject(
    id object, 
    void *key, 
    id value, 
    objc_AssociationPolicy policy);

注意第二个参数指定的key值是(void*)类型,表示其接收一个内存地址,不能传递一个临时的字符串给该参数。
第三个参数value是需要关联的对象,由于这个参数的类型是id类型,因此它只接收类型为NSObject或其子类的对象。如果关联一个整数或浮点型的数据,要先转换成NSNumber再进行关联。该参数为 nil 时,则从关联表中删除一个已存在的对象。

@implementation UIView (DOMExtensions)
static const NSInteger kViewIdKey = 1;
- (void)setViewId:(NSString *)aViewId {
    objc_setAssociatedObject(self, &kViewIdKey, aViewId, OBJC_ASSOCIATION_COPY);
}
- (NSString *)viewId {
    return (NSString *)objc_getAssociatedObject(self, &kViewIdKey);
}
@end

因为我们在属性声明中指定的参数是”copy”,所以最后一个参数应为:OBJC_ASSOCIATION_COPY。第二个参数为键值,可以用来获取关联对象。


4. 方法拦截
上述的代码中,为UIView的对象动态的关联了另外一个对象,关联对象在使用完成之后,需要从关联表中移除,否则会有内存泄露。移除关联对象最好的方法是在UIView的dealloc方法中。

@implementation UIView (DOMExtensions)
- (void)dealloc {
    objc_removeAssociatedObjects(self);
    [super dealloc];
}
@end

但是请注意,上面的写法是极其错误的。
在OC中扩展一个类时,如果定义了一个与原生类同样的方法,那么原生类中的方法会被覆盖掉。上面的代码,在UIView的扩展中实现了dealloc方法,那么在UIView的对象被释放时,只会调用这个扩展方法,而原生UIView类的dealloc方法是无论如何也不会再被调到的,这会导致更加严重的内存问题。解决方法:
1)在扩展UIView时,不能定义dealloc函数。
2)先找到原生UIView的dealloc方法的函数指针并保存。
2)将dealloc替换为一个自定义的函数。
3)在自定义的函数中能过保存的函数指针调用dealloc。

// 函数指针,指向原生UIView的dealloc方法。
static IMP UIView_dealloc_old;
// 替换后的dealloc函数,注意函数名必需以两个"$"符开始。
static void $$UIView_dealloc(UIView *self, SEL op) {
	// 移除所有关联对象。
        objc_removeAssociatedObjects(self);
        // 调用dealloc方法。
	UIView_dealloc_old(self, op);
}
- (void)setViewId:(NSString *)aViewId {
    // 获取dealloc函数指针。
    UIView_dealloc_old = class_getMethodImplementation([UIView class], @selector(dealloc));
    // 替换dealloc方法。
    class_replaceMethod([UIView class], @selector(dealloc), (IMP)$$Shape_dealloc, "v@:");
    // 设置关联对象。
    objc_setAssociatedObject(self, "viewId", aViewId, OBJC_ASSOCIATION_COPY);
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值