分类-Category
2017-02-23 本文已影响16人
川少叶
1 简介
- 主要为类添加方法:不管系统的类还是自定义的类
- 声明私有方法(扩展)
- 分类是运行期决议的;扩展是编译期决议,需要源码才能在主类添加扩展;所以系统类是无法添加扩展
- 分类的结构体,没有成员变量(ivar)列表,所以无法添加成员变量
typedef struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
} category_t;
2 Category如何加载
- 把Category的实例方法,协议以及属性添加到主类
- 把Category的类方法和协议添加到类的metaClass
添加方法的策略:
是将Category和主类的方法拼接起来,放到方法列表饭(数组):先加载主类的方法,加载分类方法时,是先把分类方法放到新数组,再把主类的方法放到新数组后
而查找方法也是按照顺序查找,所以会有Category覆盖主类方法的说法
for (uint32_t m = 0;
(scanForCustomRR || scanForCustomAWZ) && m < mlist->count;
m++)
{
SEL sel = method_list_nth(mlist, m)->name;
if (scanForCustomRR && isRRSelector(sel)) {
cls->setHasCustomRR();
scanForCustomRR = false;
} else if (scanForCustomAWZ && isAWZSelector(sel)) {
cls->setHasCustomAWZ();
scanForCustomAWZ = false;
}
}
// Fill method list array
newLists[newCount++] = mlist;
.
.
.
// Copy old methods to the method list array
for (i = 0; i < oldCount; i++) {
newLists[newCount++] = oldLists[i];
}
3 +load的加载顺序
先调用主类的load方法,再调用Category;Category之间的顺序由编译顺序决定
4 关联对象(AssociatedObject)
#import "MyClass.h"
@interface MyClass (Category1)
@property(nonatomic,copy) NSString *name;
@end
#import "MyClass+Category1.h"
#import <objc/runtime.h>
@implementation MyClass (Category1)
+ (void)load
{
NSLog(@"%@",@"load in Category1");
}
- (void)setName:(NSString *)name
{
objc_setAssociatedObject(self,
"name",
name,
OBJC_ASSOCIATION_COPY);
}
- (NSString*)name
{
NSString *nameObject = objc_getAssociatedObject(self, "name");
return nameObject;
}
@end
所有的关联对象都由AssociationsManager管理,通过Hash表管理,key是对象地址,value是Hash表,这个Hash表则保存了所有的KV对。AssociationsManager定义如下:
class AssociationsManager {
static OSSpinLock _lock;
static AssociationsHashMap *_map; // associative references: object pointer -> PtrPtrHashMap.
public:
AssociationsManager() { OSSpinLockLock(&_lock); }
~AssociationsManager() { OSSpinLockUnlock(&_lock); }
AssociationsHashMap &associations() {
if (_map == NULL)
_map = new AssociationsHashMap();
return *_map;
}
};
AssociationsManager里面是由一个静态AssociationsHashMap来存储所有的关联对象的。这相当于把所有对象的关联对象都存在一个全局map里面。而map的的key是这个对象的指针地址(任意两个不同对象的指针地址一定是不同的),而这个map的value又是另外一个AssociationsHashMap,里面保存了关联对象的kv对。而在对象的销毁逻辑里面,见objc-runtime-new.mm:
void *objc_destructInstance(id obj)
{
if (obj) {
Class isa_gen = _object_getClass(obj);
class_t *isa = newcls(isa_gen);
// Read all of the flags at once for performance.
bool cxx = hasCxxStructors(isa);
bool assoc = !UseGC && _class_instancesHaveAssociatedObjects(isa_gen);
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
if (!UseGC) objc_clear_deallocating(obj);
}
return obj;
}
嗯,runtime的销毁对象函数objc_destructInstance里面会判断这个对象有没有关联对象,如果有,会调用_object_remove_assocations做关联对象的清理工作。