category 是一個平常開發(fā)經(jīng)常會用到的一個技術(shù)點芋肠,不過大多數(shù)情況下,也只是對某一個類添加一些實例方法 或者類方法沪编。一般也足以滿足需求。
不過如果在方法中年扩,有需要傳遞變量時漾抬,僅僅靠方法就不夠了。
例如常遂,為 UIView 添加一個點擊手勢纳令,傳入一個 block。 就需要 UIView 持有一個 block克胳。
在Objective-C提供的runtime函數(shù)中平绩,確實有一個class_addIvar()函數(shù)用于給類添加成員變量,但是這個函數(shù)只能在“構(gòu)建一個類的過程中”調(diào)用漠另。一旦完成類定義捏雌,就不能再添加成員變量了。
那么在 category 的 .h 添加了 @property 的時候笆搓,只會生成對應(yīng)的 getter 和 setter 方法性湿,并不會有實例變量的產(chǎn)生。因為類分配的內(nèi)存區(qū)域在編譯時就確定了满败。
為什么可以在類別中添加方法和屬性呢肤频?
因為方法和屬性并不“屬于”類實例,而成員變量“屬于”類實例算墨。
方法定義是在objc_class中管理的宵荒,不管如何增刪類方法,都不影響類實例的內(nèi)存布局净嘀,已經(jīng)創(chuàng)建出的類實例仍然可正常使用报咳。
如果需要在 category 中添加實例變量怎么處理呢?
這時候就需要使用到黑魔法 Runtime 挖藏,因為 OC 的是一門動態(tài)語言暑刃,有運行時的特性。所以可以利用 Runtime 的關(guān)聯(lián)方法膜眠,讓兩個對象關(guān)聯(lián)起來岩臣。
代碼如下袁翁,
// UIView+TapBlock.h
#import <UIKit/UIKit.h>
@interface UIView (TapBlock)
typedef void (^TapActionBlock)(void);
- (void)bs_whenTapped:(TapActionBlock)block;
@end
// UIView+TapBlock.m
#import "UIView+TapBlock.h"
#import <objc/runtime.h>
@interface UIView (TapBlockInternal)
@property (nonatomic, copy) TapActionBlock tapBlock;
@end
@implementation UIView (TapBlock)
- (void)bs_whenTapped:(TapActionBlock)block {
self.tapBlock = block;
self.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
[self addGestureRecognizer:tap];
}
- (void)tapAction {
if (self.tapBlock) {
self.tapBlock();
}
}
static const char BSTapActionBlockKey = '\0';
- (TapActionBlock)tapBlock {
return objc_getAssociatedObject(self, &BSTapActionBlockKey);
}
- (void)setTapBlock:(TapActionBlock)tapBlock {
/*
objc_AssociationPolicy參數(shù)使用的策略:
OBJC_ASSOCIATION_ASSIGN; //assign策略
OBJC_ASSOCIATION_COPY_NONATOMIC; //copy策略
OBJC_ASSOCIATION_RETAIN_NONATOMIC; // retain策略
OBJC_ASSOCIATION_RETAIN;
OBJC_ASSOCIATION_COPY;
*/
/*
關(guān)聯(lián)方法:
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
參數(shù):
* id object 給哪個對象的屬性賦值
const void *key 屬性對應(yīng)的key
id value 設(shè)置屬性值為value
objc_AssociationPolicy policy 使用的策略,是一個枚舉值婿脸,和copy粱胜,retain,assign是一樣的狐树,手機開發(fā)一般都選擇NONATOMIC
*/
objc_setAssociatedObject(self, &BSTapActionBlockKey, tapBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
@end
文中借鑒的資料鏈接:
https://blog.csdn.net/mumuyinyin/article/details/72854579
https://github.com/CoderMJLee/MJRefresh
https://github.com/BlocksKit/BlocksKit