主要内容
- Runtime 基础
- Runtime 应用
Runtime 基础
Objective-C 语言将决定尽可能的从编译和链接推迟到运行时。只要有可能 Objective-C 总是使用动态的方式来解决问题。这就意味着 Objective-C 语言不仅需要一个编译器,同时需要一个运行时系统来执行编译好的代码。
交互方式
- 通过 Objective-C 源代码
大部分情况下,运行时系统在后台自动运行,我们只需编写和编译 Objective-C 源代码。当编译 Objective-C 类和方法时,编译器为实现语言动态特性将自动创建一些数据结构和函数,方便运行期发送消息。
- 通过类 NSObject 的方法
NSObject 有些方法能在运行时获得类的信息,并检查一些特性,比如 class
返回对象的类;isKindOfClass:
和 isMemberOfClass:
则检查对象是否在指定的类继承体系中;respondsToSelector:
检查对象能否响应指定的消息;conformsToProtocol:
检查对象是否实现了指定协议类的方法;methodForSelector:
则返回指定方法实现的地址。
- 通过运行时系统的函数
运行时系统是一个有公开接口的动态库,由一些数据结构和函数的集合组成,这些数据结构和函数的声明头文件在 /usr/include/objc 中。这些函数支持用纯 C 的函数来实现和 Objective-C 同样的功能。还有一些函数构成了 NSObject 类方法的基础。这些函数使得访问运行时系统接口和提供开发工具成为可能。尽管大部分情况下它们在 Objective-C 程序不是必须的,但是有时候对于 Objecitve-C 程序来说某些函数是非常有用的。
消息传递
在很多语言,比如 C ,调用一个方法其实就是跳到内存中的某一点并开始执行一段代码。没有任何动态的特性,因为这在编译时就决定好了。而在 Objective-C 中,[object foo]
语法并不会立即执行 foo 这个方法的代码。它是在运行时给 object 发送一条叫 foo 的消息。这个消息,也许会由 object 来处理,也许会被转发给另一个对象,或者不予理睬假装没收到这个消息。多条不同的消息也可以对应同一个方法实现。这些都是在程序运行的时候决定的。
Objective-C 消息语句
[object foo];
编译器编译之后
objc_msgSend(object, @selector(foo));
如果有参数则为
objc_msgSend(object, @selector(foo), arg1, arg2, ...)
接下来我来看一下转换为 objc_msgSend
函数之后的传递过程
id objc_msgSend(id self, SEL _cmd, ...);
- 检查忽略的_cmd,比如当我们运行在有垃圾回收机制的环境中,将会忽略 retain 和 release 消息。
- 检查 self 是否为 nil。不像其他语言,nil 在 Objective-C 中是完全合法的,并且这里有很多原因你也愿意这样,比如,至少我们省去了给一个对象发送消息前检查对象是否为空的操作。如果 self 为空,则会将 _cmd 也设置为空,并且直接返回到消息调用的地方。如果对象非空,就继续下一步。
- 接下来会根据 SEL 到当前类中查找对应的 IMP(方法实现的函数指针),首先会在 cache 中检索它,如果找到了就根据函数指针跳转到这个函数执行,否则进行下一步。
- 检索当前类对象中的方法表(method list),如果找到了,加入 cache 中,并且就跳转到这个函数之行,否则进行下一步。
- 从父类中寻找,直到根类(NSObject)。找到了就将方法加入对应类的 cache 表中,否则进行下一步。
- 动态方法解析,没有实现解析进入下一步。
- 消息转发,如果没有实现转发下一步。
- 闪退。
动态方法解析
当向一个对象发送一个消息时,在对象中没有找到对应的实现,那么就会进入动态解析。
- 如果这个消息由实例方法转换而来,动态解析可以重载
+ (BOOL)resolveInstanceMethod:(SEL)sel
- 如果这个消息由类方法转换而来,动态解析可以重载
+ (BOOL)resolveClassMethod:(SEL)sel
例:动态解析实例方法
@interface MTObject : NSObject
@end
@implementation MTObject
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if(sel == @selector(testFun))
{
class_addMethod([self class], sel, (IMP)dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void dynamicMethodIMP(id self, SEL _cmd)
{
NSLog(@"ok");
}
@end
例:动态解析类方法
@interface MTObject : NSObject
@end
@implementation MTObject
+ (BOOL)resolveClassMethod:(SEL)sel
{
if(sel == @selector(testFun))
{
class_addMethod(object_getClass(self), sel, (IMP)dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveClassMethod:sel];
}
void dynamicMethodIMP(id self, SEL _cmd)
{
NSLog(@"ok");
}
@end
注意:
- IMP 为函数指针定义为
typedef id (*IMP)(id, SEL, ...)
- IMP 可以通过传人一个块获取
IMP imp_implementationWithBlock(id block)
- class_addMethod 中 "v@:" 表示IMP的返回值和参数。依次表示:函数返回为空、id类型的参数、SEL类型的参数,具体参考Type Encoding 官方 和 Type Encoding 其它
消息转发
- 重定向(改变消息接受者)
- 转发
重定向实例方法
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if(aSelector == @selector(xxx))
{
return otherObject;
}
return [super forwardingTargetForSelector:aSelector];
}
重定向类方法
+ (id)forwardingTargetForSelector:(SEL)aSelector
{
if(aSelector == @selector(xxx))
{
return NSClassFromString(@"className");
}
return [super forwardingTargetForSelector:aSelector];
}
转发(当动态方法解析返回 NO,在重定向返回的是 nil 或 self 时开始进行)
@interface MTProxy : NSProxy
@property (nonatomic, strong) id target;
@end
@implementation MTProxy
- (instancetype)initWithTarget:(id)target
{
self.target = target;
return self;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
[anInvocation invokeWithTarget:self.target];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
return [self.target methodSignatureForSelector:sel];
}
@end
注意:
- 消息转发时,在
forwardInvocation:
消息发送前,Runtime系统会向对象发送methodSignatureForSelector:
消息,并取到返回的方法签名用于生成NSInvocation对象。所以我们在重写forwardInvocation:
的同时也要重写methodSignatureForSelector:
方法。
几个难点
1、关于元类(Meta Class),需要先了解一些数据结构的定义
id
typedef struct objc_object *id;
objc_object
struct objc_object {
Class isa;
};
Class
typedef struct objc_class *Class;
objc_class
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
从上面的数据结构可以看出,任何一个可以通过 id 表示的对象可以通过 isa 指针找到对应类的 Class。需要注意的是,在 objc_class
中还有个 isa 指针指向一个 Class,这个 Class 就是元类(Meta Class)。既然元类也是类,自然也有一个 isa 指针了,这时的 isa 指针指向的是 Root Class 的元类,而 Root Class 的元类的 isa 指针指向自己。元类中的 objc_method_list
存储的是类方法。
总结:
- 实例对象的 isa 指针指向对应的类
- 类的 isa 指针指向对应的元类
- 元类的 isa 指针指向根元类(NSObject 的元类)
- 根元类的 isa 指针指向自己
Runtime 应用
- 获取属性列表
@interface MTObject : NSObject
@property (nonatomic, strong) NSString *string1;
@end
@implementation MTObject
{
NSString *string2;
}
@end
//核心代码
- (void)allProperty
{
unsigned int count = 0;
objc_property_t *propertyList = class_copyPropertyList([MTObject class], &count);
for(int i = 0; i < count; i++)
{
NSString *propertyName = [NSString stringWithUTF8String:property_getName(propertyList[i])];
NSLog(@"%@", propertyName);
}
}
//输出结果
19:15:23.177 ObjectiveDemo[1214:54491] string1
- 获取成员变量列表
@interface MTObject : NSObject
@property (nonatomic, readonly) NSString *string1;
@end
@implementation MTObject
{
NSString *string2;
}
@end
//核心代码
- (void)allIvar
{
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList([MTObject class], &count);
for(int i = 0; i < count; i++)
{
NSString *propertyName = [NSString stringWithUTF8String:ivar_getName(ivarList[i])];
NSLog(@"%@", propertyName);
}
}
//输出结果
19:22:39.955 ObjectiveDemo[1287:57828] string2
19:22:39.955 ObjectiveDemo[1287:57828] _string1
注意:@property定义的成员变量都带下划线 _property;如果有 @synthesize 重新定义,则和重新定义的一致。
- 获取实例方法列表
@interface MTObject : NSObject
@end
@implementation MTObject
- (void)instanceMethod{}
+ (void)classMethod{}
@end
//核心方法
- (void)allInstanceMethod
{
unsigned int count = 0;
Method *methodList = class_copyMethodList([MTObject class], &count);
for(int i = 0; i < count; i++)
{
SEL sel = method_getName(methodList[i]);
NSString *methodName = NSStringFromSelector(sel);
NSLog(@"%@", methodName);
}
}
//输出结果
19:49:40.219 ObjectiveDemo[1501:69002] instanceMethod
- 获取类方法列表
@interface MTObject : NSObject
@end
@implementation MTObject
- (void)instanceMethod{}
+ (void)classMethod{}
@end
//核心代码(其实就是 class_copyMethodList 传人的 Class 为对于的元类就可以了)
- (void)allClassMethod
{
unsigned int count = 0;
Method *methodList = class_copyMethodList(objc_getMetaClass(class_getName([MTObject class])), &count);
for(int i = 0; i < count; i++)
{
SEL sel = method_getName(methodList[i]);
NSString *methodName = NSStringFromSelector(sel);
NSLog(@"%@", methodName);
}
}
//输出结果
19:55:45.357 ObjectiveDemo[1528:71022] classMethod
- Associated Objects(可以应用分类添加属性等)
@interface MTObject : NSObject
@end
@implementation MTObject
@end
@interface MTObject (category)
@property (nonatomic, strong) NSString *categoryProperty;
@end
@implementation MTObject (category)
- (void)setCategoryProperty:(NSString *)categoryProperty
{
objc_setAssociatedObject(self,
@selector(categoryProperty),
categoryProperty,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)categoryProperty
{
return objc_getAssociatedObject(self, @selector(categoryProperty));
}
@end
objc_AssociationPolicy几种类型
OBJC_ASSOCIATION_ASSIGN 指定一个关联对象的弱引用。 相当于@property(assign)或
@property(unsafe_unretained)
OBJC_ASSOCIATION_RETAIN_NONATOMIC 指定一个关联对象的强引用,不能被原子化使用。 相当于@property(nonatomic, strong)
OBJC_ASSOCIATION_COPY_NONATOMIC 指定一个关联对象的copy引用,不能被原子化使用。 相当于@property(nonatomic, copy)
OBJC_ASSOCIATION_RETAIN 指定一个关联对象的强引用,能被原子化使用。 相当于@property(atomic, strong)
OBJC_ASSOCIATION_COPY 指定一个关联对象的copy引用,能被原子化使用。 相当于@property(atomic, copy)
- Method Swizzling(可以应用hook等)
@implementation UIViewController (Tracking)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(xxx_viewWillAppear:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// 如果是类方法
// Class class = object_getClass((id)self);
// ...
// Method originalMethod = class_getClassMethod(class, originalSelector);
// Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
#pragma mark - Method Swizzling
- (void)xxx_viewWillAppear:(BOOL)animated {
[self xxx_viewWillAppear:animated];
NSLog(@"viewWillAppear: %@", self);
}
@end
注意:理解 selector, method, implementation 这三个概念之间关系的最好方式是:在运行时,类(Class)维护了一个消息分发列表来解决消息的正确发送。每一个消息列表的入口是一个方法(Method),这个方法映射了一对键值对,其中键值是这个方法的名字 selector(SEL),值是指向这个方法实现的函数指针 implementation(IMP)。 Method swizzling 修改了类的消息分发列表使得已经存在的 selector 映射了另一个实现 implementation,同时重命名了原生方法的实现为一个新的 selector。
网友评论