Contents
  1. 一、 EffectiveOC目录
  2. 二、 进一步熟悉内存机制
  3. 三、 命名规范
  4. 四、 进一步理解消息转发机制
    1. 1. objc_msgSend传递消息
    2. 2. 消息转发
    3. 3. Method Swizzing
  • 五、 类和元类
    1. 1. 理解类对象
    2. 2.查询类型信息
    3. 3. 理解元类(meta class)
    4. 4.”元类的类”
    5. 5.类和元类的继承
  • 总结
  • 后续关注内容
  • 我觉得EffectiveOC(暂且简称为EffectiveOC)这本书主要针对iOS开发中的一些常见问题和需要注意的地方供一些建议,但是每个点讲的比较深入,不仅告诉你这样做,并且从底层上讲解why,从讲解的why上我对OC有了更多的理解,这里做一点分享,也算做懒人的一种笔记吧.

    EffectiveOC封面


    一、 EffectiveOC目录

    • 优化Objective-C对象之间的互动与关系.
    • 掌握接口与API的设计原则,写出令开发者用起来得心应手的类.
    • 善用协议与分类,编写便于维护且不易出现bug的代码.
    • 在自动引用计数(ARC)环境下避免内存泄漏.
    • 用”块”与”大中枢派发”编写呈模块化且功能强大的代码.
    • 理解Objective-C中的协议与其他编程语言中的多重继承有何区别,并掌握协议的用法.
    • 通过数组、字典、集合等组合对象来提高代码性能.
    • 揭示CocoaCocoa Touch框架的强大之处.


    二、 进一步熟悉内存机制


    1
    NSString *str = [[NSString alloc] initWithString:@"This is a string"];

    常说的实例对象其实是指向对象内存地址的指针。

    实例对象.png


    三、 命名规范

    OC的方法名可能很长,但是是为了避免歧义,在命名方面,先要保证表达清楚,没有歧义,然后再考虑长度优化。


    四、 进一步理解消息转发机制

    OC是一门极其动态语言,在编译器定义好的方法在运行期系统会查找、调用某方法的实现代码,才能真正确定所调用的方法,如果类无法立即响应某个Selector,就会启动消息转发流程。

    1. objc_msgSend传递消息

    objc_msgSend

    1
    id returnValue = [someObject messageName: parameter];

    消息传递调用的核心函数叫做objc_msgSend,编译器会把刚才的方法转换成:

    1
    id returnValue = objc_msgSend(someObject, @selecor(messageName:), parameter);

    objc_msgSend()方法中,主要通过以下步骤来查找和调用函数:

    • 根据对象obj找到对象类中存储的函数列表methodLists

    • 再根据SEL@selector(doSth)在methodLists中查找对应的函数指针method_imp

    • 根据函数指针method_imp调用响应的函数。

    old_method_list结构体:

    1
    2
    3
    4
    5
    6
    struct old_method_list {
    void *obsolete; //废弃的属性
    int method_count; //方法的个数
    /* variable length structure */
    struct old_method method_list[1]; //方法的首地址
    };

    old_method结构体:

    1
    2
    3
    4
    5
    struct old_method {
    SEL method_name; //函数的SEL
    char *method_types; //函数的类型
    IMP method_imp; //函数指针
    };
    • obj->isa(Class类型) obj对象通过isa属性拿到对应的Class
    • Class->methodLists(old_method_list类型) Class通过methodLists属性拿到存放所有方法的列表
    • old_method_list->old_method 在old_method_list中通过SEL查找到对应的old_method
    • old_method->method_imp(IMP类型) old_method通过method_imp属性拿到函数指针

    • method_imp->调用函数 通过函数指针调用函数
      objc_msgSend函数会根据接受者和Selector的类型来调用适当的方法,如果找到与Selector名称相符的方法名,就跳转到该方法的实现代码,如果没有就沿着继承体系继续向上查找,如果还是找不到,就执行消息转发。
      (流程图)


    2. 消息转发
    • 2.1 “动态方法解析”(dynamic method resolution) 查看所属的类是否能动态添加方法,已处理当前的未知选择子(unknown selector).
    • 2.2 “完整的消息转发机制”(full forwatding mechanism)请接受者看看有没有其他对象能处理这个消息,如果可以就把消息转发给那个对象,如果没有”备援接受者”(replacement receiver)则启动完整的消息转发机制,运行期系统会把消息有关的全部细节封装到NSInvocation对象中,给receiver最后一次机会,设法解决这条未处理的消息.

    消息转发

    Selector是方法选择器,里面存放的是方法的名字。对应方法的映射列表。
    objc_msgSend函数会一句及守着与Selector的类型来调用适当的方法,他会在方法接受者所属类中搜寻方法列表,如果找到了与Selector名称相符的方法。


    3. Method Swizzing

    使用method_exchangeImplemetations(originalMethod, swappedMethod);实现运行时的Selector交换

    methodSwizzing


    五、 类和元类


    1. 理解类对象

    比起类,可能对象的概念更熟悉一点,这是对象的定义:
    对象的结构体
    你会发现有一个定义成Class类型的isa,这是实例对象用以表明其所属类型的,指向Class对象的指针。通过Class搭建了类的继承体系(class hirerarchy)。

    其实类也是对象,打开定义的头文件,发现是用一个结构体来存储类的信息。

    类的结构体

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    typedef struct objc_class *Class;
    struct objc_class {
    Class isa; // 指向metaclass
    Class superclass; // 指向父类Class
    const char *name; // 类名
    uint32_t version; // 类的版本信息
    uint32_t info; // 一些标识信息,标明是普通的Class还是metaclass
    uint32_t instance_size; // 该类的实例变量大小(包括从父类继承下来的实例变量);
    struct old_ivar_list *ivars; //类中成员变量的信息
    struct old_method_list **methodLists; 类中方法列表
    Cache cache; 查找方法的缓存,用于提升效率
    struct old_protocol_list *protocols; // 存储该类遵守的协议
    }

    类的结构体存放着该类的信息:类的方法列表,实例变量,协议,父类等信息。
    每个类的isa指针指向该类的所属类型元类(metaClass),用来表述类对象的数据。每个类仅有一个类对象,而每个类对象仅有一个与之相关的”元类”。
    比如一个继承NSObjct名叫SomeClass的类,其继承体系如下:

    类的继承体系

    Objective-C中任何的类定义都是对象。即在程序启动的时候任何类定义都对应于一块内存。在编译的时候,编译器会给每一个类生成一个且只生成一个”描述其定义的对象”,也就是水果公司说的类对象(class object),它是一个单例(singleton).
    因此,程序里的所有实例对象(instace objec)都是在运行时由Objective-C的运行时库生成的,而这个类对象(class object)就是运行时库用来创建实例对象(instance object)的依据。


    2.查询类型信息

    有时候会需要查询一个”objct”对象的所属的类,有人会这样写:

    1
    2
    3
    4
    id objct = /* ... */
    if ([objct class] == [SomeClass class]) {
    //objct is an instance of SomeClass.
    }

    其实OC中提供了专门用于查询类型信息的方法,由于runtime在运行时的动态性,对于对象所属类的查询,建议使用isKindOfClassisMemberOfClass,因为某些对象可能实现了消息转发功能,从而判断可能不准确.


    3. 理解元类(meta class)

    为了调用类里的方法,类的isa指针必须指向包含这些类方法的类结构体。
    这就引出了元类的定义:元类是类对象的类。
    简单说就是:

    当你给对象发送消息时,消息是在寻找这个对象的类的方法列表。
    当你给类发消息时,消息是在寻找这个类的元类的方法列表。
    元类是必不可少的,因为它存储了类的类方法。每个类都必须有独一无二的元类,因为每个类都有独一无二的类方法。


    4.”元类的类”

    元类,就像之前的类一样,它也是一个对象。你也可以调用它的方法。自然的,这就意味着他必须也有一个类。

    所有的元类都使用根元类(继承体系中处于顶端的类的元类)作为他们的类。这就意味着所有NSObject的子类(大多数类)的元类都会以NSObject的元类作为他们的类

    根据这个规则,所有的元类使用根元类作为他们的类,根元类的元类则就是它自己。也就是说基类的元类的isa指针指向他自己。


    5.类和元类的继承

    类用 super_class指针指向了超类,同样的,元类用super_class指向类的super_class的元类。

    说的更拗口一点就是,根元类把它自己的基类设置成了super_class。

    在这样的继承体系下,所有实例、类以及元类(meta class)都继承自一个基类。

    这意味着对于继承于NSObject的所有实例、类和元类,他们可以使用NSObject的所有实例方法,类和元类可以使用NSObject的所有类方法

    这些文字看起来莫名其妙难以理解,可以用一份图谱来展示这些关系:

    类和元类


    总结

    1. 任何直接或间接继承了NSObject的类,它的实例对象 (instacne object)中都有一个isa指针,指向它的类对象(class object)。这个类对象(class object)中存储了关于这个实例对象(instace object)所属的类的定义的一切:包括变量,方法,遵守的协议等等。
    2. NSObjectisa指针指向所述的类,而类对象(class object)的isa指针指向元类对象(metaClass object),类对象包含了类的实例变量、实例方法的定义,是用来描述该类的对象的信息;元类对象中包含了类的类方法的定义,是用来描述类的信息(类名,版本,类方法).
    3. 元类(meta class)是Class对象的类。每个类(Class)都有自己独一无二的元类(每个类都有自己第一无二的方法列表)。这意味着所有的类对象都不同。所有的元类使用基类的元类作为自己的基类,对于顶层基类的元类也是一样,只是它指向自己而已。


    后续关注内容

    1. git教程

    2. Programming with Objective-C

    参考阅读

    Objective-C特性:Runtime

    Effective Objective C 2.0

    Objective-C Runtime

    Objective-C 中的元类(meta class)是什么?

    Contents
    1. 一、 EffectiveOC目录
    2. 二、 进一步熟悉内存机制
    3. 三、 命名规范
    4. 四、 进一步理解消息转发机制
      1. 1. objc_msgSend传递消息
      2. 2. 消息转发
      3. 3. Method Swizzing
  • 五、 类和元类
    1. 1. 理解类对象
    2. 2.查询类型信息
    3. 3. 理解元类(meta class)
    4. 4.”元类的类”
    5. 5.类和元类的继承
  • 总结
  • 后续关注内容