结构模型
- 介绍下runtime的内存模型(isa、对象、类、metaclass、结构体的存储信息等)
Class:
typedef struct objc_class *Class;
struct objc_class {
Class isa; //指向meta_class
Class super_class; //指向父类
const char* name;
long version;
long info;
long instance_size;
struct objc_ivar_list *ivars; //类的成员变量,使用数组存储,使用中基本不变化(只有动态生成的类才能添加成员变量Ivar),存取效率高。
struct objc_method_list **methodLists; //类的成员方法,使用链表存储。使用中可能会增加方法,使用链表存储。
struct objc_cache *cache; //类的方法缓存。
struct objc_protocol_list *protocols; //类遵守的协议。
};
对象
struct objc_object {
Class isa; //指向对象所属的类。
};
- 为什么要设计metaclass
元类保存了类方法的列表
class_copyIvarList & class_copyPropertyList区别
class_copyPropertyList返回的仅仅是对象类的属性(@property申明的属性),而class_copyIvarList返回类的所有属性和变量(包括在@interface大括号中声明的变量)class_rw_t 和 class_ro_t 的区别
ObjC 类中的属性、方法还有遵循的协议等信息都保存在 class_rw_t 中:其中还有一个指向常量的指针 ro,其中存储了当前类在编译期就已经确定的属性、方法以及遵循的协议。category如何被加载的,两个category的load方法的加载顺序,两个
category的同名方法的加载顺序
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父类
const char *name OBJC2_UNAVAILABLE; // 类名
long version OBJC2_UNAVAILABLE; // 类的版本信息,默认0
long info OBJC2_UNAVAILABLE; // 类信息,供运行期使用的一些位标识
long instance_size OBJC2_UNAVAILABLE; // 该类的实例变量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 该类的成员变量链表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定义的链表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法缓存
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 协议链表
#endif
} OBJC2_UNAVAILABLE;
struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods; //存储实例方法
struct method_list_t *classMethods; //存储类方法
struct protocol_list_t *protocols; //协议
struct property_list_t *instanceProperties;//属性
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
分类的实现原理是将category中的方法,属性,协议数据放在category_t结构体中,然后将结构体内的方法列表拷贝到类对象的方法列表中。
category的+load执行顺序是根据编译顺序决定的
- category & extension区别,能给NSObject添加Extension吗,结果如何
Category的小括号中有名字,而Extension没有;
Category只能扩充方法,不能扩充成员变量和属性;
如果Category声明了声明了一个属性,那么Category只会生成这个属性的set,get方法的声明,也就不是会实现.
消息转发机制,消息转发机制和其他语言的消息机制优劣对比
1.category
2.运行时
3.消息机制
4.可以和C,C++,swift混和编程
1.不支持命名空间
2.不支持运算符重载
3.不支持多重继承
4.使用动态运行时类型,所有的方法都是函数点用,很多编译时的优化方法都用不到,如内联函数在方法调用的时候,方法查询-> 动态解析-> 消息转发 之前做了什么
1、正常判空处理
2、TAGGED_POINTERS判断(后面文章再一起探究)
3、通过isa指针拿到他的class(class中存储它的方法以及方法缓存)
4、CacheLookup 查看方法缓存IMP、SEL、Method的区别和使用场景
SEL
SEL方法选择器,表示一个selector的指针
无论什么类里,只要方法名相同,SEL就相同。项目里的所有SEL都保存在一个NSSet集合里(NSSet集合里的元素不能重复),所以查找对应方法,只要找到对应的SEL就可以了。
SEL实际是根据方法名hash化了的字符串
IMP
定义:函数指针,指向方法实现的首地址。
Method
Method定义如下:它主要是用语描述类里面的方法
typedef struct objc_method *Method;
objc_method结构体定义如下
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;//方法名
char *method_types OBJC2_UNAVAILABLE;//参数返回值字符串描述
IMP method_imp OBJC2_UNAVAILABLE;//方法的实现
}
从上述代码可以看出,Method是一个结构体,包含了SEL和IMP成员变量。
实际上,相当于在SEL和IMP之间做了一个映射,有了Method,SEL就可以找到对应的IMP,从而调用方法。
- load、initialize方法的区别什么?在继承关系中他们有什么区别
load和initialize会被自动调用,不能手动调用它们。
子类实现了load和initialize的话,会隐式调用父类的load和initialize方法
load和initialize方法内部使用了锁,因此它们是线程安全的。
2.2 不同点
子类中没有实现load方法的话,不会调用父类的load方法;而子类如果没有实现initialize方法的话,也会自动调用父类的initialize方法。
load方法是在类被装在进来的时候就会调用,initialize在第一次给某个类发送消息时调用(比如实例化一个对象),并且只会调用一次,是懒加载模式,如果这个类一直没有使用,就不回调用到initialize方法。
load
父类先于子类调用
类先于分类调用
initialize 只会在对应类的方法第一次被调用时,如果一个子类没有实现 +initialize 方法,那么父类的实现是会被执行多次的,如果一个类的分类实现了 +initialize 方法,那么就会对这个类中的实现造成覆盖。