这两天看了关于iOS运行时的资料,自己做个记录和总结。
1.类和对象
OC中的对象相本质上是一个结构体,结构体中包含了一个指向类的指针
typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
类的定义如下:
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
没有属性列表,因为属性本质上就是set/get方法 加上成员变量 从上面可以看出实例对象其实是没有方法列表的
2.方法和方法的调用
1).h中只是方法的声明,如果只是在.h中声明了一个方法而没有在.m中实现,则当前对象的方法列表(这里习惯上称为对象的方法列表,从1中可以看出其实是当前对象所属类的方法列表)中是没有该方法的。如果没有在.h中声明某个方法,而在的方法列表.m中实现了某个方法则方法列表中有该方法。
2)实例对象进行方法调用时实际上是消息的发送。通过方法选择器@selector将方法名转化为唯一的一个编号--SEL(这个编号与类和对象无关 只要方法名相同 这个编号就会相同;所以,相同的类不能有相同的方法名),我们可以通过这个编号找到对应的方法实现--IMP(IMP本质上是一个指针,这个指针指向方法实现的首地址)。在查找方法的实现时,首先会去对象所属类的方法列表中查找,如果没有找到则会通过isa指针去所属类的父类的方法列表中查找,直到顶级父类。
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
方法结构体中包含方法名(SEL)和IMP指针
struct objc_method {
SEL _Nonnull method_name OBJC2_UNAVAILABLE;
char * _Nullable method_types OBJC2_UNAVAILABLE;
IMP _Nonnull method_imp OBJC2_UNAVAILABLE;
}
个人猜测SEL的地址与IMP指针的地址相同 求大神给出答案
po [NSString stringWithFormat:@"%p",@selector(func)]
0x109b6a88a
po (IMP *)(self,@selector(func));
0x0000000109b6a88a
3)如果查到顶级父类后依然没有找到对应的方法,则会进行以下步骤:
①动态方法解析:系统首先会调用+ (BOOL)resolveInstanceMethod:(SEL)sel方法 我们可以在该方法中通过class_addMethod函数动态添加方法(动态添加的方法要实现)
②转发给其他接收者:如果没有通过①来处理消息系统还会给我们机会让我们选择其他接收者来处理这条消息通过
- (id)forwardingTargetForSelector:(SEL)aSelector 方法添加新的接收者
③消息完整转发:如果②也都没能处理该消息,系统会给当前接收者最后一次机会(如果在这步仍然没有处理消息则引发异常)把消息转发给其他对象。完整转发就包括了SEL,目标和参数我们可以通过- (void)forwardInvocation:(NSInvocation *)anInvocation 方法来选择要处理当前消息的对象。但是我们需要重写 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 方法
4)结合1)、2)中的下划线部分可以得出如下结论:子类对象可以调用父类.m中的方法(如果直接调用编译时会出现语法错误,所以可以用[a performSelector:@selector(functionB)]; 来调用。 其中a为A的实例,A继承于B,functionB为B的实例方法,所以我们可以通过这种方式来调用一些类的私有方法)
如果大家碰到什么疑问或者发现错误请告知,大家共同探讨共同进步