Objective-C 方法交换实践(一) - 基础知识
2017-11-20 23:54
183 查看
一、Objective-C 中的基本类型
首先看下 Objective-C 的对象模型,每个 Objective-C 对象都是一个指向 Class 的指针。Class 的结构如下: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;
这个结构已经有很多的说明了,下面简单的再描述下
1. 变量列表
变量 Ivar 也是一个结构体,每个 Class 中用变长结构体的方式存储了 Class 的变量列表。 IVar 的定义如下,包含 名称、类型、偏移、占用空间。
typedef struct objc_ivar *Ivar; struct objc_ivar { char * _Nullable ivar_name OBJC2_UNAVAILABLE; char * _Nullable ivar_type OBJC2_UNAVAILABLE; int ivar_offset OBJC2_UNAVAILABLE; #ifdef __LP64__ int space OBJC2_UNAVAILABLE; #endif } OBJC2_UNAVAILABLE;
这个变长结构体定义如下:
struct objc_ivar_list { int ivar_count OBJC2_UNAVAILABLE; #ifdef __LP64__ int space OBJC2_UNAVAILABLE; #endif /* variable length structure */ struct objc_ivar ivar_list[1] OBJC2_UNAVAILABLE; } OBJC2_UNAVAILABLE;
2. 方法列表
每个方法 Method 的定义如下,包含 SEL 指向对外的命名,char * 型 的方法类型, IMP 方法指针,指向具体的函数实现。typedef struct objc_method *Method; struct objc_method { SEL _Nonnull method_name OBJC2_UNAVAILABLE; char * _Nullable method_types OBJC2_UNAVAILABLE; IMP _Nonnull method_imp OBJC2_UNAVAILABLE; } OBJC2_UNAVAILABLE;
同样一个变长结构体来存储方法列表。Class 中的这个列表是个2级指针,所以可以向 Class 中动态的添加方法。
struct objc_method_list { struct objc_method_list * _Nullable obsolete OBJC2_UNAVAILABLE; int method_count OBJC2_UNAVAILABLE; #ifdef __LP64__ int space OBJC2_UNAVAILABLE; #endif /* variable length structure */ struct objc_method method_list[1] OBJC2_UNAVAILABLE; } OBJC2_UNAVAILABLE;
3. 缓存
同样一个变长结构体存储之前找到的 Method。1)、mask:可以认为是当前能达到的最大index(从0开始的),所以缓存的size(total)是mask+1;
2)、occupied:被占用的槽位,因为缓存是以散列表的形式存在的,所以会有空槽,而occupied表示当前被占用的数目。
他是通过 要查找的 Method 的 SEL 地址和 mask 做一系列运算来确定 Method 的存储与查找位置。更详细的说明可以看参考4。其中提到的几点也在说下:
子类的 cache 会存储在父类中找到的方法;cache 的大小会动态增加,但是增加之前一定会先清空自己(变长结构体的特性)。
typedef struct objc_cache *Cache OBJC2_UNAVAILABLE; #define CACHE_BUCKET_NAME(B) ((B)->method_name) #define CACHE_BUCKET_IMP(B) ((B)->method_imp) #define CACHE_BUCKET_VALID(B) (B) #ifndef __LP64__ #define CACHE_HASH(sel, mask) (((uintptr_t)(sel)>>2) & (mask)) #else #define CACHE_HASH(sel, mask) (((unsigned int)((uintptr_t)(sel)>>3)) & (mask)) #endif struct objc_cache { unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE; unsigned int occupied OBJC2_UNAVAILABLE; Method _Nullable buckets[1] OBJC2_UNAVAILABLE; };
4. 协议
typedef struct objc_category *Category; struct objc_category { char * _Nonnull category_name OBJC2_UNAVAILABLE; char * _Nonnull class_name OBJC2_UNAVAILABLE; struct objc_method_list * _Nullable instance_methods OBJC2_UNAVAILABLE; struct objc_method_list * _Nullable class_methods OBJC2_UNAVAILABLE; struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE; } OBJC2_UNAVAILABLE; struct objc_protocol_list { struct objc_protocol_list * _Nullable next; long count; __unsafe_unretained Protocol * _Nullable list[1]; };
5. isa 和 superClass
看一张经典的图:isa 表明当前对象所属于的 Class 类型(Class 也是一个对象,Class 的类型叫 MetaClass)。
superClass 表明当前对象从哪个父类派生出来的,根类型(比如 NSObject、NSProxy)的 superClass 是 nil。
向对象发送消息时,会去方法列表里面查询,找不到会去父类的方法列表,再找不到会进入动态添加、消息转发、消息包装的过程。向 Class 发送消息时,会去 MetaClass 走同样的过程。
二、self 和 super
self 是类的隐藏的参数,指向当前调用方法的类super 是一个"编译器指示符", 是一个标记,告诉编译器起始于当前类的父类方法列表中搜索方法的实现。
看一个例子
@A - (void)show{ } - (void)log { NSLog(@"i am a"); } - (void)print { NSLog(@"i am %@",[self class]); } @end @B: A - (void)show { [self/super log]; [self/super print]; } - (void)log { NSLog(@"i am b"); } - (void)print { NSLog(@"i am %@",[self class]); } @end @ C: B - (void)log { NSLog(@"i am c"); } @end
在 B 的show 方法中分别改成 self 和 super,如下调用会输出什么?
C *c = [[C alloc] init]; [c show];
结果是 self 的时候 输出
i am c i am C
super 的时候输出
i am a i am C
用 self 调用方法,会编译成
objc_msgSend方法,其定义如下:
void objc_msgSend(void /* id self, SEL op, ... */ )
第一个参数是消息接收者,也就是对象本身,第二个参数是调用的具体类方法的 selector。这里有个隐藏参数
_cmd,代表当前类方法的selector。
用super 调用方法,会编译成
objc_msgSendSuper方法,其定义如下:
void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
其中
objc_super的定义如下:
/// Specifies the superclass of an instance. struct objc_super { /// Specifies an instance of a class. __unsafe_unretained _Nonnull id receiver; /// Specifies the particular superclass of the instance to message. #if !defined(__cplusplus) && !__OBJC2__ /* For compatibility with old objc-runtime.h header */ __unsafe_unretained _Nonnull Class class; #else __unsafe_unretained _Nonnull Class super_class; #endif /* super_class is the first class to search */ };
三、消息转发
当向一个类的实例发送方法时,会去Class结构的方法缓存列表
objc_cache和 方法列表
objc_method_list中查找有没有这个方法,如果没有的话,则会进入消息转发阶段。
消息转发主要分为两大阶段:
动态方法解析:看对象所属类是否能动态添加方法
转发阶段:既然第一步已经不会新增方法来响应,那系统就会请接受者看看有没有其他对象响应这个消息;如果没有,就把消息封装到 NSInvocation中,再做一次尝试。
参考:
1.http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html
2.http://time-track.cn/variable-length-structure.html
3.https://tech.meituan.com/DiveIntoMethodCache.html
4.http://blog.csdn.net/datacloud/article/details/7275170
5.http://blog.csdn.net/wzzvictory/article/details/8487111
相关文章推荐
- Java基础知识之位运算--两变量值交换方法
- Objective-C基础知识之(四):实例变量的可变度、初始化方法的声明、self和super的具体应用
- 黑马程序员---Objective-C基础学习---类、对象、方法相关知识笔记
- 黑马程序员--Objective-C语言基础知识--构造方法和内存管理
- Objective-C 基础知识之(九):创建对象的方法
- JAVA程序设计(06)-----二变量交换的方法 基础知识
- Objective-C基础知识之(二):方法的类型、setter方法、getter方法、初始化方法、其他方法
- 《软件工程 ——理论、方法与实践》知识概括第六章 面向对象基础
- Objective-C 方法交换实践(二) - 方法指针交换
- Objective-C 基础知识之(十): OC中的排序方法
- Java基础知识强化37:StringBuffer类之StringBuffer的构造方法
- [C# 基础知识系列]专题十一:匿名方法解析 推荐
- Java基础学习6(位运算符,交换两个数的三种方法)
- [Objective-c 基础 - 2.7] 构造方法、重写init方法
- iOS基础知识:Objective-C 之 NSString,NSArray,NSDictionary,NSSet
- JAVA基础知识以及一个引用方法来求矩形周长的代码
- [C# 基础知识系列] 专题十五:全面解析扩展方法
- 网站知识基础+网站搭建方法
- [C# 基础知识系列]专题三:如何用委托包装多个方法——委托链
- Objective-C基础知识之(三):继承、#import、@class: