# runtime
[TOC]
### Method
Method 代表类中某个方法的类型
```
typedef struct objc_method *Method;
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;
}
```
`objc_method` 存储了方法名,方法类型和方法实现:
- 方法名类型为 `SEL`
- 方法类型 `method_types` 是个 char 指针,存储方法的参数类型和返回值类型
- `method_imp` 指向了方法的实现,本质是一个函数指针
### Ivar
`Ivar` 是表示成员变量的类型。
```
typedef struct objc_ivar *Ivar;
struct objc_ivar {
char *ivar_name OBJC2_UNAVAILABLE;
char *ivar_type OBJC2_UNAVAILABLE;
int ivar_offset OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
}
```
其中 `ivar_offset` 是基地址偏移字节
### IMP
IMP在objc.h中的定义是:
```
typedef id (*IMP)(id, SEL, ...);
```
它就是一个**函数指针**,这是由编译器生成的。当你发起一个 ObjC 消息之后,最终它会执行的那段代码,就是由这个函数指针指定的。而 `IMP` 这个函数指针就指向了这个方法的实现。
如果得到了执行某个实例某个方法的入口,我们就可以绕开消息传递阶段,直接执行方法,这在后面 `Cache` 中会提到。
你会发现 `IMP` 指向的方法与 `objc_msgSend` 函数类型相同,参数都包含 `id` 和 `SEL` 类型。每个方法名都对应一个 `SEL` 类型的方法选择器,而每个实例对象中的 `SEL` 对应的方法实现肯定是唯一的,通过一组 `id`和 `SEL` 参数就能确定唯一的方法实现地址。
而一个确定的方法也只有唯一的一组 `id` 和 `SEL` 参数。
### Cache
Cache 定义如下:
```
typedef struct objc_cache *Cache
struct objc_cache {
unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE;
unsigned int occupied OBJC2_UNAVAILABLE;
Method buckets[1] OBJC2_UNAVAILABLE;
};
```
Cache 为方法调用的性能进行优化,每当实例对象接收到一个消息时,它不会直接在 isa 指针指向的类的方法列表中遍历查找能够响应的方法,因为每次都要查找效率太低了,而是优先在 Cache 中查找。
Runtime 系统会把被调用的方法存到 Cache 中,如果一个方法被调用,那么它有可能今后还会被调用,下次查找的时候就会效率更高。就像计算机组成原理中 CPU 绕过主存先访问 Cache 一样。
### Property
```
typedef struct objc_property *Property;
typedef struct objc_property *objc_property_t;//这个更常用
```
可以通过`class_copyPropertyList` 和 `protocol_copyPropertyList` 方法获取类和协议中的属性:
```
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
```
> 注意:
> 返回的是属性列表,列表中每个元素都是一个 `objc_property_t` 指针
```
#import <Foundation/Foundation.h>
@interface Person : NSObject
/** 姓名 */
@property (strong, nonatomic) NSString *name;
/** age */
@property (assign, nonatomic) int age;
/** weight */
@property (assign, nonatomic) double weight;
@end
```
以上是一个 Person 类,有3个属性。让我们用上述方法获取类的运行时属性。
```
unsigned int outCount = 0;
objc_property_t *properties = class_copyPropertyList([Person class], &outCount);
NSLog(@"%d", outCount);
for (NSInteger i = 0; i < outCount; i++) {
NSString *name = @(property_getName(properties[i]));
NSString *attributes = @(property_getAttributes(properties[i]));
NSLog(@"%@--------%@", name, attributes);
}
```
打印结果如下:
```
2014-11-10 11:27:28.473 test[2321:451525] 3
2014-11-10 11:27:28.473 test[2321:451525] name--------T@"NSString",&,N,V_name
2014-11-10 11:27:28.473 test[2321:451525] age--------Ti,N,V_age
2014-11-10 11:27:28.474 test[2321:451525] weight--------Td,N,V_weight
```
`property_getName` 用来查找属性的名称,返回 c 字符串。`property_getAttributes` 函数挖掘属性的真实名称和 `@encode` 类型,返回 c 字符串。
```
objc_property_t class_getProperty(Class cls, const char *name)
objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty)
```
`class_getProperty` 和 `protocol_getProperty` 通过给出属性名在类和协议中获得属性的引用。
# method swizzling
在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的。
每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点类似函数指针,指向具体的Method实现。
![img](http://upload-images.jianshu.io/upload_images/1771779-da34056b24fef205.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/509)
Paste_Image.png
我们可以利用 method_exchangeImplementations 来交换2个方法中的IMP,我们可以利用 class_replaceMethod 来修改类,我们可以利用 method_setImplementation 来直接设置某个方法的IMP,归根结底,都是偷换了selector的IMP,如下图所示:
![img](http://upload-images.jianshu.io/upload_images/1771779-d3fb26be7bfd47c8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/483)
Paste_Image.png
这就是所谓的method swizzling
***
isa 混写
KVO 基于 isa swizzling