类扩展 分类
类扩展
就是一个匿名的分类
.
类扩展
在编译的时候就将数据写入到类的信息中,也就ro
中
分类编译底层是
struct category_t
,里面存储着分类的对象方法、类方法、属性、协议信息.
在程序运行的时候,runtime
会将Category
的数据,合并到类信息中(类对象、元类对象中.也就是保存在rw
中.
-
类扩展
通过通过
_read_images
->_getObjc2ClassList
->打印ro
->baseMethodList
,我们发现:
此时
ro
已经有属性生成的set,get
方法.
类扩展还可以单独写成一个头文件,记得导入头文件
.
- 分类底层结构
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; // 属性列表
};
- 从结构体可以知道,有属性列表,所以分类可以声明属性,但是分类只会生成该属性对应的
get
和set
的声明,没有去实现该方法。 - 结构体没有
成员变量列表
,所以不
能声明成员变量。
- Category的加载处理过程
- 通过Runtime加载某个类的所有Category数据
- 把所有Category的方法、属性、协议数据,合并到一个大数组中,后面参与编译的Category数据,会在数组的前面
- 将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面
4.既然分类不能声明属性,可以通过关联对象
- 分类中有
cate_name
的属性
#import "LGPerson.h"
NS_ASSUME_NONNULL_BEGIN
@interface LGPerson (LG)
@property (nonatomic, copy) NSString *cate_name;
@end
NS_ASSUME_NONNULL_END
为起关联对象
#import "LGPerson+LG.h"
#import
@implementation LGPerson (LG)
-(void)setCate_name:(NSString *)cate_name{
/**
参数一:id object : 给哪个对象添加属性,这里要给自己添加属性,用self。
参数二:void * == id key : 属性名,根据key获取关联对象的属性的值,在objc_getAssociatedObject中通过次key获得属性的值并返回。
参数三:id value : 关联的值,也就是set方法传入的值给属性去保存。
参数四:objc_AssociationPolicy policy : 策略,属性以什么形式保存。
*/
objc_setAssociatedObject(self, @"name",cate_name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
/**
存储
manager -> hashMap 有所有的关联对象 - 总表 - 千千万的 -> 关联表 -> index -> 属性
哈希
哈希函数 -> index - key (属性) + 地址 (对象)
*/
}
-(NSString *)cate_name{
/**
参数一:id object : 获取哪个对象里面的关联的属性。
参数二:void * == id key : 什么属性,与objc_setAssociatedObject中的key相对应,即通过key值取出value。
*/
return objc_getAssociatedObject(self, @"name");
}
@end
这些都是平时常用的关联对象代码,那么底层源码为我们做了什么?
objc_setAssociatedObject
就是
AssociationsManager
管理一张总的哈希表,然后里面是一张张小表,通过对象地址找到对象的哈希表,在通过key
,去保存.
取值也是,先找到自己的表,在通过key
,取出关联的值
//存值
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// This code used to work when nil was passed for object and key. Some code
// probably relies on that to not crash. Check and handle it explicitly.
// rdar://problem/44094390
if (!object && !value) return;
assert(object);
if (object->getIsa()->forbidsAssociatedObjects())
_objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
// retain the new value (if any) outside the lock.
// 在锁之外保留新值(如果有)。
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
// 关联对象的管理类
AssociationsManager manager;
// 获取关联的 HashMap -> 存储当前关联对象
AssociationsHashMap &associations(manager.associations());
// 对当前的对象的地址做按位去反操作 - 就是 HashMap 的key (哈希函数)
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
// 获取 AssociationsHashMap 的迭代器 - (对象的) 进行遍历
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// secondary table exists
ObjectAssociationMap *refs = i->second;
// 根据key去获取关联属性的迭代器
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
// 替换设置新值
j->second = ObjcAssociation(policy, new_value);
} else {
// 到最后了 - 直接设置新值
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
// create the new association (first time).
// 如果AssociationsHashMap从没有对象的关联信息表,
// 那么就创建一个map并通过传入的key把value存进去
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
}
} else {
// setting the association to nil breaks the association.
// 如果传入的value是nil,并且之前使用相同的key存储过关联对象,
// 那么就把这个关联的value移除(这也是为什么传入nil对象能够把对象的关联value移除)
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).
// 最后把之前使用传入的这个key存储的关联的value释放(OBJC_ASSOCIATION_SETTER_RETAIN策略存储的)
if (old_association.hasValue()) ReleaseValue()(old_association);
}
- _object_get_associative_reference
id _object_get_associative_reference(id object, void *key) {
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
// 关联对象的管理类
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
// 生成伪装地址。处理参数 object 地址
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();
// OBJC_ASSOCIATION_GETTER_RETAIN - 就会持有一下
if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
objc_retain(value);
}
}
}
}
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
objc_autorelease(value);
}
return value;
}
- 关联对象的释放在dealloc方法执行时跟着释放了
- (void)dealloc
_objc_rootDealloc(id obj)
objc_object::rootDealloc()
object_dispose(id obj)
void *objc_destructInstance(id obj)
if (assoc) _object_remove_assocations(obj);
void _object_remove_assocations(id object) {
vector< ObjcAssociation,ObjcAllocator > 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()) {
// copy all of the associations that need to be removed.
ObjectAssociationMap *refs = i->second;
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
elements.push_back(j->second);
}
// remove the secondary table.
delete refs;
associations.erase(i);
}
}
// the calls to releaseValue() happen outside of the lock.
for_each(elements.begin(), elements.end(), ReleaseValue());
}
-
总结
load_images
load
方法在什么时候调用?
查看函数调用栈
在之前调用load_images
void
load_images(const char *path __unused, const struct mach_header *mh)
{
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
mutex_locker_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
call_load_methods
猜测就是调用load
的时候,但是在之前有个局部函数prepare_load_methods
,
查看
void prepare_load_methods(const headerType *mhdr)
{
size_t count, I;
runtimeLock.assertLocked();
classref_t const *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[I];
Class cls = remapClass(cat->cls);
if (!cls) continue; // category for ignored weak-linked class
if (cls->isSwiftStable()) {
_objc_fatal("Swift class extensions and categories on Swift "
"classes are not allowed to have +load methods");
}
realizeClassWithoutSwift(cls, nil);
ASSERT(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
schedule_class_load
和add_category_to_loadable_list
先取出类的列表,将load
方法加入到list
,然后在加载分类的load
方法加入到list
中.
- call_load_methods
void *pool = objc_autoreleasePoolPush();
do {
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
为栈操作,类的先压入栈中,调用为函数指针方式
struct loadable_category {
Category cat; // may be nil
IMP method;
};
load
方法,先主后分类
一般方法,分类方法attachlist
往前面插
方法调用时找到就返回,分类方法不覆盖
多个分类相同方法,看文件那个加载,就执行那个.
initalize
-
initialize
在类第一次接收到消息时调用,也就是objc_msgSend()
。 - 先调用父类的
+initialize
,再调用类的initialize
。
- 方法调用都会来到
lookUpImpOrForwarz
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
初始化
initializeNonMetaClass(nonmeta);
调用
callInitialize(cls);
+initialize
的调用过程:
- 如果本类和父类都实现了
initialize
方法,初始化本类时,会先初始化父类(即调用父类的initialize
方法),然后再调用本类的initialize
方法; - 如果本类没有实现
initialize
方法,父类实现了initialize
方法,则多个子类初始化时会多次调用父类的initialize
方法,但是本质上只有第一次initialize
方法是初始化父类,后面几个initialize
都是方法的调用,即子类没有实现,通过superclass
到父类里查找。