类(objc_class)
OC中的类由Class表示,其实是一个指向objc_class结构体的指针:
typedef struct objc_class *Class;
objc_class的结构:
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY; //isa指针
#if !__OBJC2__
Class _Nullable super_class //父类的指针
const char * _Nonnull name //类名
long version
long info
long instance_size
struct objc_ivar_list * _Nullable ivars //属性列表
struct objc_method_list * _Nullable * _Nullable methodLists //方法列表
struct objc_cache * _Nonnull cache //方法缓存
struct objc_protocol_list * _Nullable protocols //协议列表
#endif
}
如上所示:结构体的第一个成员变量也是isa指针,这就说明了Class本身其实也是一个对象,因此我们称之为【类对象】,类对象在编译期产生用于创建实例对象,是个单例。
实例(objc_object)
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
实例中的isa指针指向类对象
元类(Meta Class)
Q:由上我们知道,类也是个对象,叫做类对象。那么类对象和类方法是从哪创建的呢?
A:就是类中的isa指针指向的结构体,叫元类。元类中保存了创建类对象和类方法的所有信息
- 元类是类对象的类
- 元类的isa指针指向了自己,形成闭环
- 由上我们知道,类本身也是个对象,所以我们可以向这个对象发送消息。(即调用类方法)
- 任何继承自 NSObject 的元类都使用 NSObject 的元类作为自己的所处类。
Method(objc_method)
//方法
struct objc_method {
SEL method_name //方法名
char *method_types //方法类型
IMP method_imp //方法实现
}
1、SEL(objc_selector)
SEL是selector在Objective-C中的表示类型,selector其实是一个string
- 同一个类,selector不能重复
- 不同的类,selector可以重复
2、IMP:指向最终实现程序的内存地址的指针
Runtime中,Method通过selector和IMP两个属性,实现了快速查询方法及实现,相对提高了性能,又保持了灵活性。
类缓存(objc_cache)
iOS消息传递是怎么实现的呢?
- 系统首先找到消息的接收对象,然后通过对象的isa找到它的类。
- 在它的类中【遍历】查找method_list,是否有selector方法。
- 没有则查找父类的method_list。
- 找到对应的method,执行它的IMP。
- 为了提高效率,系统会将调用过的方法以 @selector(xxx) 为key缓存下来。避免每次都要执行遍历操作
- 向一个对象发送消息时,RunTime会先从缓存里找,如果有直接实现方法。如果没有遍历方法列表,找到该方法实现并缓存下来。
Runtime应用
关联对象(Objective-C Associated Objects):给分类增加属性
#import "ViewController.h"
#import "objc/runtime.h"
@interface UIView (DefaultColor)
@property (nonatomic, strong) UIColor *defaultColor;
@end
@implementation UIView (DefaultColor)
@dynamic defaultColor;
static char kDefaultColorKey;
- (void)setDefaultColor:(UIColor *)defaultColor {
objc_setAssociatedObject(self, &kDefaultColorKey, defaultColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)defaultColor {
return objc_getAssociatedObject(self, &kDefaultColorKey);
}
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView *test = [UIView new];
test.defaultColor = [UIColor blackColor];
NSLog(@"%@", test.defaultColor);
}
@end
方法交换(Method Swizzling)
Method originalMethod = class_getInstanceMethod(class,originalSelector);
Method swizzledMethod = class_getInstanceMethod(class,swizzledSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);
从上面说的 Method 我们知道,Method中包含 Selector 方法名和 IMP 指向方法具体实现地址的指针,当执行 method_exchangeImplementations 方法时,将 A 方法名和 B 方法名对应的 指向具体实现的IMP 指针交换。
KVO实现实现
当观察 a 对象的一个属性时,系统会动态创建一个 a 当前类的子类。并将isa指针指向这个子类 B。通过重写 B 类中被观察属性的 setter 方法实现监听。具体实现如下。
- (void)setName:(NSString *)newName {
[self willChangeValueForKey:@"name"]; //KVO 在调用存取方法之前总调用
[super setValue:newName forKey:@"name"]; //调用父类的存取方法
[self didChangeValueForKey:@"name"]; //KVO 在调用存取方法之后总调用
}
实现字典和模型的自动转换(MJExtension)
#import "NSObject+RunTime.h"
#import <objc/runtime.h>
@implementation NSObject (RunTime)
//步骤一:回去类的属性列表,获取属性的 KEY,加到数组里
+ (NSArray *)or_objcProperties{
//runtime可以获取以下列表
//Ivar:成员变量 Property:属性 Method:方法 Protocol:协议
unsigned int count = 0;
objc_property_t *proList = class_copyPropertyList([self class], &count);
//创建数组
NSMutableArray *arrayM = [NSMutableArray array];
// 遍历所有的属性
for (unsigned int i = 0; i < count; i++) {
// 1. 从数组中取得属性
/**
C 语言的结构体指针,通常不需要 `*`
*/
objc_property_t pty = proList[i];
// 2. 从 pty 中获得属性的名称
const char *cName = property_getName(pty);
NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];
// 3. 属性名称添加到数组
[arrayM addObject:name];
}
// 释放数组
free(proList);
return arrayM.copy;
}
//步骤二:遍历传入的字典,如果步骤一中的数组包含字典的 key,则对象执行 setValue for key
+ (instancetype)or_objWithDic:(NSDictionary *)dict {
id object = [[self alloc] init];
NSArray *proList = [self or_objcProperties];
//遍历传入字典
[dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
//判断key是否在proList中
if ([proList containsObject:key]) {
[object setValue:obj forKey:key];
}
}];
return object;
}
@end