iOS原理(三)----CateGory
创建一个Animal
,及其两个分类,并调用其eat
,run
,sleep
方法.
@interface Animal : NSObject
- (void)eat;
@end
@implementation Animal
- (void)eat {
NSLog(@"---eat---");
}
@end
@interface Animal (Run)
- (void)run;
@end
@implementation Animal (Run)
- (void)run {
NSLog(@"---run---");
}
@end
@interface Animal (Sleep)
- (void)sleep;
@end
@implementation Animal (Sleep)
- (void)sleep {
NSLog(@"---sleep---");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Animal *ani = [[Animal alloc] init];
[ani eat];
[ani run];
[ani sleep];
}
return 0;
}
用命令生成C++变异文件:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
;
每个分类都会转成一个_category_t
类型的结构体,
struct _category_t {
// 类名
const char *name;
// 类信息
struct _class_t *cls;
// 实例方法列表
const struct _method_list_t *instance_methods;
// 类方法列表
const struct _method_list_t *class_methods;
// 协议列表
const struct _protocol_list_t *protocols;
// 属性列表
const struct _prop_list_t *properties;
};
static struct _category_t _OBJC_$_CATEGORY_NSObject_$_Run __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"NSObject",
0, // &OBJC_CLASS_$_NSObject,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_NSObject_$_Run,
0,
0,
0,
};
static struct _category_t _OBJC_$_CATEGORY_NSObject_$_Sleep __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"NSObject",
0, // &OBJC_CLASS_$_NSObject,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_NSObject_$_Sleep,
0,
0,
0,
};
最后把该类的所有分类放在:
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [2] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_NSObject_$_Run,
&_OBJC_$_CATEGORY_NSObject_$_Sleep,
};
到底何时将分类的信息添加到原本的类当中的呢?查看苹果源码片段如下:
static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
if (!cats) return;
if (PrintReplacedMethods) printReplacements(cls, cats);
bool isMeta = cls->isMetaClass();
// fixme rearrange to remove these intermediate allocations
// 方法数组
method_list_t **mlists = (method_list_t **)
malloc(cats->count * sizeof(*mlists));
// 属性数组
property_list_t **proplists = (property_list_t **)
malloc(cats->count * sizeof(*proplists));
// 协议数组
protocol_list_t **protolists = (protocol_list_t **)
malloc(cats->count * sizeof(*protolists));
// Count backwards through cats to get newest categories first
int mcount = 0;
int propcount = 0;
int protocount = 0;
int i = cats->count;
bool fromBundle = NO;
// 倒序遍历
while (i--) {
auto& entry = cats->list[I];
// 新增的方法列表
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
mlists[mcount++] = mlist;
fromBundle |= entry.hi->isBundle();
}
// 新增属性列表
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
proplists[propcount++] = proplist;
}
// 新增协议列表
protocol_list_t *protolist = entry.cat->protocols;
if (protolist) {
protolists[protocount++] = protolist;
}
}
auto rw = cls->data();
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
rw->methods.attachLists(mlists, mcount);
free(mlists);
if (flush_caches && mcount > 0) flushCaches(cls);
rw->properties.attachLists(proplists, propcount);
free(proplists);
rw->protocols.attachLists(protolists, protocount);
free(protolists);
}
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;
// 将原来的array()->lists后移,大小为addedCount
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
// 将新增的addedLists,添加到array()->lists中,从开头开始添加,大小为addedCount
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else {
// 1 list -> many lists
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
从上面的代码看出,在类初始化的利用runtime将分类属性,方法,协议添加到原来类当中.
在之前的Rnu
,Sleep
分类中的分别实现Animal
中的eat
方法,我们调用eat
方法,那到底会调那个eat
方法.实际调用如下:
调用了Run分类的eat
方法.为什么会调用Run分类的eat
方法?
此时编译的顺序为:
Snip20181110_4.png改变Run
分类和Sleep
分类的编译顺序,
此时调用为Sleep
分类的eat
方法:
这是因为又前面的源码得知,当app按顺序编译后,运行的时候mlists
是倒序添加的分类方法列表,调用attachLists()
函数将mlists
所有新增方法,放在array()->lists
的开始.所以最后编译的分类方法,运行时添加在array()->lists
的最前面.
所以Category的原理是:
- 编译时每个分类生成
_category_t
类型的结构体. - 通过Runtime加载某个类的所有Category数据.
- 把所有Category的方法、属性、协议数据,合并到一个大数组中(后面参与编译的Category数据,会在数组的前面).
- 将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面.
有_category_t
并没有存放成员变量,所有我们在分类中并不能直接添加成员变量,但是我们可以间接用关联对象
添加成员变量.比如我们给Run
分类添加一个height
属性(只生成setter,getter声明,不提供实现),方法如下:
void objc_setAssociatedObject(id object, const void * key, id value, objc_AssociationPolicy policy)
其中policy
有四个值,
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
// 相当于assign
OBJC_ASSOCIATION_ASSIGN = 0,
// 相当于strong, nonatomic
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
// 相当于copy, nonatomic
OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
// 相当于strong, atomic
OBJC_ASSOCIATION_RETAIN = 01401,
// 相当于copy, atomic
OBJC_ASSOCIATION_COPY = 01403
};
setter实现如下:
- (void)setHeight:(double)height {
objc_setAssociatedObject(self, @"height", @(height), OBJC_ASSOCIATION_ASSIGN);
}
getter实现如下:
- (double)height {
return [objc_getAssociatedObject(self, @"height") doubleValue];
}
由前面的得知关联对象我们并没有实际生成成员变量,那到底底层实际是如何实现的呢?查看runtime
源码,void objc_setAssociatedObject()
代码如下:
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);
}
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// retain the new value (if any) outside the lock.
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{ // 关联对象管理类
AssociationsManager manager;
// AssociationsHashMap中参数中的DISGUISE(object)为key,值为ObjectAssociationMap
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
// value不为nil
if (new_value) {
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// ObjectAssociationMap已经存在时
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
// value已经存在ObjectAssociationMap时
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);
} else {
// value没有存在ObjectAssociationMap时
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
// ObjectAssociationMap不存在时
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
}
} else {
// 值为nil的时候,相当于移除关联关系
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
refs->erase(j);
}
}
}
}
// release the old value (outside of the lock).
if (old_association.hasValue()) ReleaseValue()(old_association);
}
从上面源码得知:整个关联对象由AssociationsManager
类管理,里面有一个map为AssociationsHashMap
,key为由object
参数生成的DISGUISE(object)
,值为一个map为ObjectAssociationMap
,key为参数的key
,值为参数的value
.
当移除某个key的关联关系时,直接将参数value
设置为nil
.
获取关联对象的值的源码为:
id objc_getAssociatedObject(id object, const void *key) {
return _object_get_associative_reference(object, (void *)key);
}
id _object_get_associative_reference(id object, void *key) {
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
ObjcAssociation &entry = j->second;
value = entry.value();
policy = entry.policy();
if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
objc_retain(value);
}
}
}
}
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
objc_autorelease(value);
}
return value;
}
知道了objc_setAssociatedObject()
原理之后,也很容易理解objc_getAssociatedObject()
的代码.
如果要移除object
的所有关联关系,函数为void objc_removeAssociatedObjects(id object)
,其实就是移除AssociationsManager
的AssociationsHashMap
对应由DISGUISE(object)
作为key的值,就是ObjectAssociationMap
.
void objc_removeAssociatedObjects(id object)
{
if (object && object->hasAssociatedObjects()) {
_object_remove_assocations(object);
}
}
void _object_remove_assocations(id object) {
vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
if (associations.size() == 0) return;
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// 复制所有需要移除的关联对象
ObjectAssociationMap *refs = i->second;
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
elements.push_back(j->second);
}
delete refs;
associations.erase(i);
}
}
for_each(elements.begin(), elements.end(), ReleaseValue());
}
网友评论