iOS--OC底层原理文章汇总
在前面的文章中,探究了类的加载以及分类在懒加载和非懒加载的情况。本文探究一下分类另外一个“表兄弟”——类扩展。
类扩展
以下的形式就是类扩展
// 分类
@interface LGTeacher : NSObject
- (void)instanceMethod;
- (void)classMethod;
@end
// 类扩展
@interface LGTeacher ()
@property (nonatomic, copy) NSString *ext_name;
- (void)ext_instanceMethod;
- (void)ext_classMethod;
@end
@implementation LGTeacher
- (void)ext_instanceMethod{
}
- (void)ext_classMethod{
}
- (void)instanceMethod{
}
- (void)classMethod{
}
@end
最常见的就是ViewController.m
中的以下
在类扩展里面,就是编写:成员变量、属性、私有方法,可以当成一个头文件
xx.h
去理解。
clang
该文件,clang -rewrite-objc main.m -o main.cpp
得到main.cpp
分析之,则可以得到类扩展相关信息
- 属性
可以看到ext_name
在C++
源码层面是为带下划线的_ext_name
,也有其setter、getter
方法的实现。 - 方法
在类扩展的方法列表中,经过编译,方法以及添加到了 methodlist中,作为类的一部分。所以我们可以知道编译时期类扩展的方法直接添加到本类里面。
再深入研究下类扩展在底层源码中的逻辑,打开一份objc
源码工程,编写一个类扩展,实现以下
@interface LGPerson ()
@property (nonatomic, copy) NSString *ext_name;
- (void)ext_instanceMethod;
- (void)ext_classMethod;
@end
可以知道类扩展的方法,作为类的信息在readClass过程中就已经编译到类信息中。
分类与类扩展区别
分类
- 1.专门用来给类添加新的方法;
- 2.不能给类添加属性,添加了属性,也无法取到(包括点
.
语法、getter
); - 3.分类中可以使用@property修饰变量,会生成
setter、getter
方法,但不会有带下划线成员变量以及方法实现; - 4.可通过runtime 给分类添加属性,即属性关联,重写setter、getter方法。
类扩展
- 1.可理解为匿名的分类,是一种特殊分类;
- 2.为类添加属性及方法,但都是私有属性、私有方法。
关联对象:底层原理
由前面知识知道,分类不能添加属性而直接获取到,但是可以在运行时动态添加关联对象,重写添加属性的setter、getter
方法
@interface LGPerson (LG)
@property (nonatomic, copy) NSString *cate_name;
@property (nonatomic, assign) int cate_age;
- (void)cate_instanceMethod1;
- (void)cate_instanceMethod3;
- (void)cate_instanceMethod2;
+ (void)cate_sayClassMethod;
@end
@implementation LGPerson (LG)
- (void)cate_instanceMethod1{
NSLog(@"%s",__func__);
}
- (void)cate_instanceMethod3{
NSLog(@"%s",__func__);
}
- (void)cate_instanceMethod2{
NSLog(@"%s",__func__);
}
+ (void)cate_sayClassMethod{
NSLog(@"%s",__func__);
}
- (void)setCate_name:(NSString *)cate_name{
/**
1: 关联的对象
2: 标识符
3: value
4: 策略
*/
objc_setAssociatedObject(self, "cate_name", cate_name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)cate_name{
/**
1.对象
2.标识符
*/
return objc_getAssociatedObject(self, "cate_name");
}
@end
在分类中定义了一个cate_name
,重写它的setter、getter
方法,并添加关联对象。在这个情况下,它就可以通过获取cate_name
的设值和取值。
老方法,研究源码,打开一个objc
工程,Jump To Definition
, 会有这样一个实现
/**********************************************************************
* Associative Reference Support
**********************************************************************/
id
objc_getAssociatedObject(id object, const void *key)
{
return _object_get_associative_reference(object, key);
}
static void
_base_objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
_object_set_associative_reference(object, key, value, policy);
}
static ChainedHookFunction SetAssocHook{_base_objc_setAssociatedObject};
void
objc_setHook_setAssociatedObject(objc_hook_setAssociatedObject _Nonnull newValue,
objc_hook_setAssociatedObject _Nullable * _Nonnull outOldValue) {
SetAssocHook.set(newValue, outOldValue);
}
void
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
SetAssocHook.get()(object, key, value, policy);
}
通过一层一层的封装,我们找到了_object_set_associative_reference + _object_get_associative_reference
这两个方法,
设值:_object_set_associative_reference
void _object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
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));
DisguisedPtr disguised{(objc_object *)object};
ObjcAssociation association{policy, value};
// retain the new value (if any) outside the lock.
//如果value存在,根据policy的值对value值作相应的操作。
association.acquireValue();
{//局部作用域----关键代码
//这不是一个单例,manager在构造时加锁,析构时开锁。范围为该作用域
AssociationsManager manager;
//DenseMap, DenseMap> 就是个嵌套的的DenseMap, manager.get()操作的是一个static的_mapStorage变量,而associations的数据是从manager.get()初始化来的,而且manager.get()返回的是带&的引用,所以associations也可以看做是静态的。
AssociationsHashMap &associations(manager.get());
if (value) {
auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
if (refs_result.second) {
//判断disguised为key的桶是新插入进来的,根据条件设置isa_t中的has_assoc位为true
object->setHasAssociatedObjects();
}
//找到associations中的disguised对应的ObjectAssociationMap表
auto &refs = refs_result.first->second;
//用key在ObjectAssociationMap表中查找,如果表中不存在该key那么就把key和association对应插入到ObjectAssociationMap中
auto result = refs.try_emplace(key, std::move(association));
//result.second为false, 说明ObjectAssociationMap表中原来已有该key,不会移动,所以这里进行了swap的操作来交换association的值。
if (!result.second) {
association.swap(result.first->second);
}
} else { //value为nil, 取消关联。
//先从associations表中找到disguised对应的ObjectAssociationMap表,又用pair包装后返回。
auto refs_it = associations.find(disguised);
//判断是否从associations表中找到了disguised对应的ObjectAssociationMap表
if (refs_it != associations.end()) {
//从pair中拿到ObjectAssociationMap表
auto &refs = refs_it->second;
//从ObjectAssociationMap表中查找key对应的association,然后把它作为DenseMapIterator的位置指针初始化后返回
auto it = refs.find(key);
if (it != refs.end()) {
//把要擦除的association记录下来,再进行releaseHeldValue
association.swap(it->second);
//从ObjectAssociationMap表中擦除association以及其他相应的操作
refs.erase(it);
if (refs.size() == 0) {
//从associations表中擦除ObjectAssociationMap表
associations.erase(refs_it);
}
}
}
}
}
// release the old value (outside of the lock).
association.releaseHeldValue();
}
typedef DenseMap ObjectAssociationMap;
typedef DenseMap, ObjectAssociationMap> AssociationsHashMap;
// class AssociationsManager manages a lock / hash table singleton pair.
class AssociationsManager {
using Storage = ExplicitInitDenseMap, ObjectAssociationMap>;
static Storage _mapStorage; //静态变量
public:
AssociationsManager() { AssociationsManagerLock.lock(); }
~AssociationsManager() { AssociationsManagerLock.unlock(); }
//注意这里的&,返回的是引用
AssociationsHashMap &get() {
return _mapStorage.get();
}
//map_images_nolock中第一次调用时,通过arr_init调用此方法进行_mapStorage初始化
static void init() {
_mapStorage.init();
}
};
构造时加锁,析构时开锁。为了对象的安全,同时能防止冲突
AssociationsManager manager;
等价于==>
AssociationsManager();
lock();
//TODO: Something
unlock();//作用域之后unlock
取值:_object_get_associative_reference
id _object_get_associative_reference(id object, const void *key)
{
//先初始化一个用来接收值的association
ObjcAssociation association{};
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
//用object作为key从associations表中找到对应的ObjectAssociationMap表
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
if (i != associations.end()) {
ObjectAssociationMap &refs = i->second;
//用key在ObjectAssociationMap表中搜索对应的ObjcAssociation
ObjectAssociationMap::iterator j = refs.find(key);
if (j != refs.end()) {
//找到后赋值给association,然后retain
association = j->second;
association.retainReturnedValue();
}
}
}
return association.autoreleaseReturnedValue();
}
总结以上得到一个流程分析