SDWebImage SDK 底层
UML 类图
- 泛化(generalization):表示is-a的关系,是对象之间偶合度最大的一种关系,子类继承父类的所有细节。直接使用语言中的继承表达。在类图中使用带三角箭头的实线表示,箭头从子类指向父类。
- 实现(Realization):在类图中就是接口和实现的关系。在类图中使用带三角箭头的虚线表示,箭头从实现类指向接口。
- 聚合(Aggregation):表示has-a的关系,是一种不稳定的包含关系。较强于一般关联,有整体与局部的关系,并且没有了整体,局部也可单独存在。如公司和员工的关系,公司包含员工,但如果公司倒闭,员工依然可以换公司。在类图使用空心的菱形表示,菱形从局部指向整体。
- 组合(Composition):表示contins-a的关系,是一种强烈的包含关系。组合类负责被组合类的生命周期。是一种更强的聚合关系。部分不能脱离整体存在。如公司和部门的关系,没有了公司,部门也不能存在了;调查问卷中问题和选项的关系;订单和订单选项的关系。在类图使用。
category 和 extension
- 区别:
- extension 只有.h 文件,可以声明私有属性,方法,变量,实现在当前类.m 之中。
- 通过@property 声明的属性,编译器会自动添加 setter 和 getter 方法和 生成 _变量名 的变量
- @synthesize
- 让编译器自动生成属性的存取方法,并将存取方法作用于系统根据属性名创建的(_+属性名)变量。
- 当我们自定义存取方法时可以覆盖系统自动生成的存取方法。(注意当我们重写set和get方法时系统不会自动创建1中提到的变量,这时需要我们自己声明实例变量)
- @dynamic
-
告诉编译器不自动生成存取方法,由开发者自行实现存取方法。
-
- category 是一对.h 和.m 文件,可以为本类添加方法
- 不能直接添加成员变量,
- 因为 category 编译之后是一个结构体,分类中可以存放实例方法,类方法,协议,属性,但是没有存放成员变量的地方。所以不能添加,没有存储成员变量的地方
- 虽然在分类中可以写@property添加属性,
- 但是不会自动生成私有属性,也不会生成set,get方法的实现,只会生成set,get的声明,需要我们自己去实现。
- setter 和 getter 方法通过后面函数来进行关联到对象上。
- objc_setAssociatedObject objc_getAssociatedObject
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; }; /var/folders/9c/kdp2y0g17nlgstxd7zyzv8sh0000gn/T/LGPerson+Test-645ee6.mi:86830:17: warning: property 'nccName' requires method 'nccName' to be defined - use @dynamic or provide a method implementation in this category [-Wobjc-property-implementation] @implementation LGPerson (Test) ^ /var/folders/9c/kdp2y0g17nlgstxd7zyzv8sh0000gn/T/LGPerson+Test-645ee6.mi:86303:41: note: property declared here @property (nonatomic, strong) NSString *nccName; ^ /var/folders/9c/kdp2y0g17nlgstxd7zyzv8sh0000gn/T/LGPerson+Test-645ee6.mi:86830:17: warning: property 'nccName' requires method 'setNccName:' to be defined - use @dynamic or provide a method implementation in this category [-Wobjc-property-implementation] @implementation LGPerson (Test) ^ /var/folders/9c/kdp2y0g17nlgstxd7zyzv8sh0000gn/T/LGPerson+Test-645ee6.mi:86303:41: note: property declared here @property (nonatomic, strong) NSString *nccName; ^ 3 warnings generated- category 运行时期确定,不能添加变量,extension 编译时期确定,内存布局,隐藏类的私有属性/添加属性
association_object 实现原理
-
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
object 给哪个对象添加属性, key 是给关联对象要添加属性的 key, value 是给关联对象要添加属性的 value, policy 属性的内存策略(assign copy)
-
AssociationsManager 全局的管理关联对象 AssociationsHashMap, AssociationsHashMap,以 key : value 的形式存储关联对象
- AssociationsHashMap {disguised, ObjectAssociationMap}, disguised是object转化之后的地址
- ObjectAssociationMap {key, ObjcAssociation} 存储的是关联属性的 key 以及值
- ObjcAssociation {policy, value} 存储值以及内存策略
- ObjectAssociationMap {key, ObjcAssociation} 存储的是关联属性的 key 以及值
- AssociationsHashMap {disguised, ObjectAssociationMap}, disguised是object转化之后的地址
AssociationsManager 整个 APP 全局管理关联对像的 manager,AssociationsHashMap 是以 key:value 形式
AssociationsHashMap *_map;
AssociationsHashMap 里面存储了许多关联对象, disguised_ptr_t 是外部传过来的关联对象转化之后的地址
disguised_ptr_t ObjectAssociationMap
disguised_ptr_t ObjectAssociationMap
disguised_ptr_t ObjectAssociationMap
......
ObjectAssociationMap 管理存储了许多外部传入的要关联对象添加的属性,value
void* ObjcAssociation void* 是外部传入的关联对象想要添加属性 key
void* ObjcAssociation
void* ObjcAssociation
......
ObjcAssociation 管理了外部传入的关联对象想要添加属性对应的 value 以及内存存储方式
uniptr_t _policy; _policy 代表 assign 或者 copy
id _value; 外部传入的值
void _object_set_associative_reference(id object, const 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;
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));
// 对关联对象处理,生成一个disguised 地址作为 key 来保存 AssociationsHashMap
DisguisedPtr<objc_object> disguised{(objc_object *)object};
ObjcAssociation association{policy, value};
// retain the new value (if any) outside the lock.
association.acquireValue();
bool isFirstAssociation = false;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
if (value) {
// refs_result 是一个迭代器,他有两个值,一个 first 是 key,一个 second 是 value
auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
if (refs_result.second) {
/* it's the first association we make */
isFirstAssociation = true;
}
/* establish or replace the association */
auto &refs = refs_result.first->second;
auto result = refs.try_emplace(key, std::move(association));
if (!result.second) {
association.swap(result.first->second);
}
} else {
auto refs_it = associations.find(disguised);
if (refs_it != associations.end()) {
auto &refs = refs_it->second;
auto it = refs.find(key);
if (it != refs.end()) {
association.swap(it->second);
refs.erase(it);
if (refs.size() == 0) {
associations.erase(refs_it);
}
}
}
}
}
// Call setHasAssociatedObjects outside the lock, since this
// will call the object's _noteAssociatedObjects method if it
// has one, and this may trigger +initialize which might do
// arbitrary stuff, including setting more associated objects.
if (isFirstAssociation)
object->setHasAssociatedObjects();
// release the old value (outside of the lock).
association.releaseHeldValue();
}
category 加载
- 多个分类/类也有同名方法,方法怎么查找?
- runtime 遍历方法列表,通过二分查找法,找到之后不会返回 imp 指针,往前移动,找同名方法
- 调用规则
- 一个类的load方法在所有父类load方法调用之后调用
- 分类的load方法在当前类的load方法调用之后调用
- 分类的load方法调用顺序和编译顺序有关
- load方法调用
- load_images
- prepare_load_methods()
- 调整当前的调用顺序
- 类 递归遍历 loadable_classes<cls, method>
- 分类 loadable_categorys 跟编译顺序有关
- call_load_methods()
- 先调用loadable_classes
- 后调用loadable_categorys
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
runtime_init();
exception_init();
#if __OBJC2__
cache_t::init();
#endif
_imp_implementationWithBlock_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
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);
loading = NO;
}
懒加载类不能添加 load 方法,因为在_objc_init 的时候,加载镜像会调用所有的 load 方法。