0%

Runtime 将尽可能多的决策从编译时和链接时,推迟到运行时,以便于程序运行时动态的创建对象、检查对象,以及修改对象的某些行为。

  • 类型结构

1)objc_object

1
2
3
4
struct objc_object {
/// 所属类的地址
Class _Nonnull isa;
};

2)objc_method

1
2
3
4
5
6
7
8
struct objc_method {
/// 方法实现
IMP _Nonnull method_imp;
/// 方法名字
SEL _Nonnull method_name;
/// 方法类型
char * _Nullable method_types;
};

3)objc_class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
struct objc_class {
/// 所属元类的地址
Class _Nonnull 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
};

4)objc_category

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct objc_category {
/// 分类名称
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;
};
  • 方法交换

1
2
3
4
5
6
7
8
9
10
11
//  NSObject+Test.h
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (Test)
/// 替换系统description
- (NSString *)swizzlingDescription;
@end

NS_ASSUME_NONNULL_END
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//  NSObject+Test.m
#import "NSObject+Test.h"
#import <objc/runtime.h>

@implementation NSObject (Test)

+ (void)load {

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^ {
// 当前类
Class class = [self class];
// 原始方法名
SEL originalSelName = @selector(description);
// 待替换方法名
SEL swizzledSelName = @selector(swizzlingDescription);
// 原始方法
Method originalMethod = class_getInstanceMethod(class, originalSelName);
// 待替换方法
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelName);
/* 如果当前类没有原方法的实现,则当前类是从父类继承过来的方法实现,
* 需要在当前类中添加一个原始方法,并用待替换方法(swizzlingDescription)去实现它
*/
BOOL isSuccess = class_addMethod(class,
originalSelName,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (isSuccess) {
// 若原始方法的IMP添加成功后,修改替换此IMP为原始方法的IMP
class_replaceMethod(class,
swizzledSelName,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
// 若添加失败,则当前类已包含原方法的IMP,随后调用交换两个方法的实现
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}

- (NSString *)swizzlingDescription { return @"a swizzling description string"; }

@end
  • 关联策略

1
2
3
4
5
6
7
8
9
10
//  NSObject+Test.h
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (Test)
@property (nonatomic, copy)NSString *testName;
@end

NS_ASSUME_NONNULL_END
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//  NSObject+Test.m
#import "NSObject+Test.h"
#import <objc/runtime.h>

@implementation NSObject (Test)

#pragma mark method
- (void)setTestName:(NSString *)testName {
objc_setAssociatedObject(self, @selector(testName), testName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

#pragma mark method
- (NSString *)testName {
return objc_getAssociatedObject(self, _cmd);
}

@end
  • 获取某个类的行为列表

1)objc_ivar_list

1
2
3
4
5
6
7
8
9
10
11
#pragma mark 实例变量列表
- (void)ivarList {
unsigned int count;
Ivar *ivarList = class_copyIvarList([NSObject class], &count);
for (unsigned int i = 0; i < count; i++) {
Ivar myIvar = ivarList[i];
const char *ivarName = ivar_getName(myIvar);
NSLog(@"%d)ivar == %@", i, [NSString stringWithUTF8String:ivarName]);
}
free(ivarList);
}

2)objc_property_list

1
2
3
4
5
6
7
8
9
10
#pragma mark 属性列表
- (void)propertyList {
unsigned int count;
objc_property_t *propertyList = class_copyPropertyList([NSObject class], &count);
for (unsigned int i = 0; i < count; i++) {
const char *propertyName = property_getName(propertyList[i]);
NSLog(@"%d)propertyName == %@", i, [NSString stringWithUTF8String:propertyName]);
}
free(propertyList);
}

3)objc_method_list

1
2
3
4
5
6
7
8
9
10
#pragma mark 方法列表
- (void)methodList {
unsigned int count;
Method *methodList = class_copyMethodList([NSObject class], &count);
for (unsigned int i = 0; i < count; i++) {
Method method = methodList[i];
NSLog(@"%d)method == %@", i, NSStringFromSelector(method_getName(method)));
}
free(methodList);
}

4)objc_protocol_list

1
2
3
4
5
6
7
8
9
10
11
#pragma mark 协议列表
- (void)protocolList {
unsigned int count;
__unsafe_unretained Protocol *protocolList = class_copyProtocolList([NSObject class], &count);
for (unsigned int i = 0; i < count; i++) {
Protocol *myProtocal = protocolList[i];
const char *protocolName = protocol_getName(myProtocal);
NSLog(@"%d)protocol == %@", i, [NSString stringWithUTF8String:protocolName]);
}
free(protocolList);
}

CategoryObjective-C 2.0之后添加的语言特性,Category主要作用是为已经存在的类添加新行为(属性、方法、协议)。

  • Category 与 Extension

Extension(扩展)被称为匿名Category(分类),Extension 是在编译阶段决定,是类的一部分,它伴随类的产生而产生,亦随之一起消亡。Extension 一般用来隐藏类的私有信息,你必须有一个类的源码才能为该类添加 Extension,因此你无法为系统类,添加Extension。
另外,Extension 是可以添加实例变量。

Category(分类) 的特性是,可以在运行时阶段动态地为已有类添加新行为,是在运行时期决定,而实例变量的内存布局是在编译阶段确定好了,因此在运行时添加实例变量的话,会破坏原有类的内存布局,从而造成灾难性后果。

  • Category 本质内容

Category 属于 objc_category 结构体类型,通过查看 objc_category内部结构,也从侧面说明,Category 为何不能添加实例变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct objc_category *Category;
struct objc_category {
/// 类名称
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;
};
  • Category 加载过程

Category 添加的新行为,只是添加到原有类上,并没有将原有类的行为进行覆盖替换,会被添加到原有类的行为列表最前边,因此 Category 的行为会先被搜索到,随后直接执行,而原有类的行为则不会被执行。

  • Category 与 load 方法

load 执行顺序是先主类,后 Category,
多个 Category,load 执行顺序是由编译顺序决定。

  • Category 与 关联对象

Category 可以添加属性,但是不会生成对应的实例变量,也不能生成 getter、setter 方法。因此可以通过关联对象方式,为 Category 添加实例变量。

1
2
3
4
5
6
7
8
// 使用给定key和关联策略为对象设置关联值
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);

// 返回与给定key的对象关联的值
id objc_getAssociatedObject(id object, const void *key);

// 移除对象所关联的属性
void objc_removeAssociatedObjects(id object);
  • Example
1
2
3
4
5
6
7
8
9
10
//  NSObject+Test.h
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (Test)
@property (nonatomic, copy)NSString *testName;
@end

NS_ASSUME_NONNULL_END
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//  NSObject+Test.m
#import "NSObject+Test.h"
#import <objc/runtime.h>

@implementation NSObject (Test)

// setter method
- (void)setTestName:(NSString *)testName {
objc_setAssociatedObject(self, @selector(testName), testName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

// getter method
- (NSString *)testName {
return objc_getAssociatedObject(self, _cmd);
}
@end
  • objc_AssociationPolicy

    • OBJC_ASSOCIATION_COPY (atomic,copy)
    • OBJC_ASSOCIATION_RETAIN (atomic,strong)
    • OBJC_ASSOCIATION_ASSIGN (atomic,assign)
    • OBJC_ASSOCIATION_COPY_NONATOMIC (nonatomic,copy)
    • OBJC_ASSOCIATION_RETAIN_NONATOMIC (nonatomic,strong)

performSelector

  • 无参数

1
- (id)performSelector:(SEL)aSelector;
1
2
3
4
5
6
7
// eg:
- (void)aSelectorTest { NSLog(@"aSelectorTest"); }

// ex:
if ([self respondsToSelector:@selector(aSelectorTest)]) {
[self performSelector:@selector(aSelectorTest)];
}
  • 一个参数

1
- (id)performSelector:(SEL)aSelector withObject:(id)object;
1
2
3
4
5
6
7
8
9
// eg:
- (void)aSelectorTest:(NSString *)testString {
NSLog(@"aSelectorTest == %@", testString);
}

// ex:
if ([self respondsToSelector:@selector(aSelectorTest:)]) {
[self performSelector:@selector(aSelectorTest:) withObject:@"a test string"];
}
  • 两个参数

1
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
1
2
3
4
5
6
7
8
9
// eg:
- (void)aSelectorTest:(NSString *)testString at:(int)index {
NSLog(@"aSelectorTest == %@, index == %i", testString, index);
}

// ex:
if ([self respondsToSelector:@selector(aSelectorTest:at:)]) {
[self performSelector:@selector(aSelectorTest:at:) withObject: @"other test string" withObject: @6];
}