iOS之学习总结

作者: 枫叶无处漂泊 | 来源:发表于2021-05-09 23:47 被阅读0次

一、load 方法

1、load方法加载

  • +Load方法会在runtime加载类、分类时调用
  • 每个类、分类的+load方法,在程序运行过程中只调用一次。

2、调用顺序

  1. 先调用类的+load方法

    • 按照编译先后顺序调用(先编译,先调用)
    • 调用子类的+load方法之前会先调用父类的+load方法
  2. 再调用分类的+load方法

    • 按照编译先后顺序调用(先编译先调用)

3、load加载过程底层原发源码执行顺序

  1. _objc_init: 加载对象的入口方法,_objc_init里面调用load_images方法

  2. load_images :加载所有类load的方法,里面调用prepare_load_methods、call_load_methods.

  3. prepare_load_methods:加载load方法的准备工作

    1. classref_t *classList = _getObjc2NonlazyClassList(mhdr, &count)方法获取所有类,遍历classList
      • 遍历classList,调用schedule_class_load(class cls):
        • 先 递归调用schedule_class_load(cls->superclass)调用父类
        • add_class_to_loadable_list:讲cls加载到数据的后面
    2. category **categoryList = _getObjc2NonlazyCategoryList(mhdr, &count);获取所有分类。
      • 遍历categoryList,调用add_category_to_loadable_list把分类加到列表最后面。

(categoryList是一个二维数组里,第一维代表是某一个类,第二维代表的是某个类下面有多个分类。
[person_category_list,stu_categorylist].
person_category_list = [category_t, category_t]
stu_category_list = [category_t, category_t]

  1. call_load_methods:调用load方法列表
    • call_class_loads():先加载完类的load方法

      • 类的加载过程,遍历add_class_to_loadable_list中类,调用load_method函数调用load方法。
    • call_category_loads():在加载分类的load方法

      • 分类的加载过程,遍历add_class_to_loadable_list中类,调用load_method函数调用load方法。

类和分类最后都会调用(*load_method)(cls, SEL_load) 这个函数,+load方法是根据方法地址直接调用,并不是经过objc_msgSend函数调用。

二、initialize方法

一、initialize调用时机

  • +initialize 方法会在类第一次收到消息时调用

    • 先调用父类的+initlialize,在调用子类的+initialize。

二、底层源码实现

  1. 调用objc_msgSend
    • class_getClassMethod(Class cls,SEL sel)(找类方法,传的就是元类)

    • lookUpImpOrNil 找方法

    • lookUpImpOrForward 继续查找

    • if (initialize && !cls->isInitialize) {
      _class_initialize方法调用initialize
      } 先判断是否需要初始化,需要的话就调用

    • callInitialize调用initialize

    • 最后调用到 objc_msgSend(cls, SEL_initialize)

三.load 与initialize区别

1、调用方式

  • load是根据函数地址调用
  • initialize是通过objc_msgSend调用

2、调用时刻

  • load是runtime加载类、分类的时候调用(只会调用一次)
  • initialize是类第一次接收到消息的时候调用,每一个类只会initialize一次(父类的initialize方法可能会被调用多次,当多个子类没有自己实现initialize,会调用父类)

3、l调用顺序

load调用顺序:
  • 先调用类的load

    • 先编译的类,优先调用load
    • 调用子类的load之前,会先调用父类的load
  • 在调用分类的load

    • 先编译的分类,优先调用load
initialize调用顺序
  • 先初始化父类
  • 再初始化子类(子类没实现,最终调用的是父类的initialize方法)

三、关联对象

给分类添加属性,分类只声明没有方法的实现,实现的方式是关联对象。
关联对象所用的相关类主要是下面4个类:

  • AssociationManager
  • AssociationHashMap
  • ObjectAssociationMap
  • ObjcAssociation

使用方式

分类.h文件
@property(nonatomic,copy)NSString *name;

分类.m文件

- (void)setName {
    objc_setAssociatedObject( self,
                                @slector(name), OBJC_ASSOCIATION_COPY_NONATOMIC)
}

- (void)name {
    objc_getAssociatedObject( self,
                                @slector(name), OBJC_ASSOCIATION_COPY_NONATOMIC)
}

第一个参数给关联对象

第二个是key

第三参数是关联策略

  • objc_AssociationPolicy :对应的修饰符

    • OBJC_ASSOCIATION_ASSIGN :assign
    • OBJC_ASSOCIATION_RETAIN_NONATOMIC: strong, nonatomic
    • OBJC_ASSOCIATION_COPY_NONATOMIC: copy, nonatomic
    • OBJC_ASSOCIATION_RETAIN:strong, atomic
    • OBJC_ASSOCIATION_COPY :copy, atomic

关联对象的核心原理

  • AssociationManager中声明一个全局的haspmap。
class AssociationManager {
    static AssociationHashMap *_map;
}
  • AssociationHashMap中存储着键值对,key是这个关联的object,值是Object AssociationMap这个也是对象的hasmap
 AssociationHashMap : public unordered_map<disguised_prt_t, Object  AssociationMap>

如下面表格:

key value
disguised_prt_t Object AssociationMap
disguised_prt_t Object AssociationMap
  • AssociationMap这个也是一个存储着键值对的hashmap
class ObjectAssociationMap : public std ::map <void *, ObjcAssociation>

如下面的表格:

key value
void * ObjcAssociation
void * ObjcAssociation
  • ObjcAssociation 包含关联策略和值,如下:
class ObjcAssociation {
    uintptr_t _policy;
    id _value;
}

关联对象的原理执行流程如下图:

[图片上传失败...(image-212701-1620575201542)]

3、移除关联对象

  • 设置关联对象为nil,就相当于移除关联对象
  • remove_Association(self) 移除该对象所有的挂链对象

4.关联对象总结

  • 关联对象并不是存储在关联对象本身内存中
  • 关联对象存储在全局同意的一个AssociationsManager中

四、KVO

1、kvo 使用

  • 注册观察者
/*
@observer:就是观察者,是谁想要观测对象的值的改变。
@keyPath:就是想要观察的对象属性。
@options:options一般选择NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld,
这样当属性值发生改变时我们可以同时获得旧值和新值,如果我们只填NSKeyValueObservingOptionNew则属性发生改变时只会获得新值。
@context:想要携带的其他信息,比如一个字符串或者字典什么的。
*/
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath 
options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
  • 监听回调
/*
@keyPath:观察的属性
@object:观察的是哪个对象的属性
@change:这是一个字典类型的值,通过键值对显示新的属性值和旧的属性值
@context:上面添加观察者时携带的信息
*/
- (void)observeValueForKeyPath:(nullable NSString *)keyPath 
ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change 
context:(nullable void *)context;
  • kvo 原理

如下图:

KVO执行过程.png

五、KVC实现原理

  • setValue: forKey

执行流程如下图:


KVC赋值.png
  • valueForKey

执行流程如下图:

KVC取值.png

六、关联对象结构图

关联对象.png

七、Notification通知心原理

Notification通知心原理

八、响应链和事件传递

响应链和事件传递

九、编程中的六大设计原则

编程中的六大设计原则

相关文章

网友评论

    本文标题:iOS之学习总结

    本文链接:https://www.haomeiwen.com/subject/btkbdltx.html