iOS --- 理解Runtime机制及其使用场景
2016-04-17 22:25
483 查看
Runtime是iOS中比较难以理解, 但又非常强大的技术.
The Objective-C language defers as many decisions as it can from compile time and link time to runtime. Whenever possible, it does things dynamically. This means that the language requires not just a compiler, but also a runtime system to execute the compiled code. The runtime system acts as a kind of operating system for the Objective-C language; it’s what makes the language work.
所谓运行时, 就是尽可能地把决定从编译器推迟到运行期, 就是尽可能地做到动态. 只是在运行的时候才会去确定对象的类型和方法的. 因此利用Runtime机制可以在程序运行时动态地修改类和对象中的所有属性和方法.
Objective-C中调用对象的方法时, 会向该对象发送一条消息, runtime根据该消息做出反应.
Runtime是一套比较底层的纯C语言的API, Objective-C是运行在Runtime上的, 因此在Runtime中动态添加和实现一些非常强大的功能也就不足为奇了.
在Objective-C代码中使用Runtime, 需要引入
通过runtime方法class_copyIvarList可获取一个指向类和对象的所有实例变量的Ivar指针.
获取到实例变量的Ivar指针后, 即可通过ivar_getName方法获取该实例变量的名称, 由C语言的字符串表示.
部分打印结果如下:
可以看到, 其中包含了UIView中的很多我们经常使用的实例变量.
对于属性, 如layer, tag等, 会自动生成 _ 前缀的成员变量
通过runtime方法class_copyPropertyList可获取一个指向类和对象的所有属性的objc_property_t指针.
获取到属性的objc_property_t指针后, 即可通过property_getName方法获取该属性的名称, 由C语言的字符串表示.
部分打印结果如下:
通过runtime方法class_copyMethodList可获取一个指向类和对象的所有方法的Method指针.
获取到方法的Method指针后, 即可通过method_getName方法获取该方法的名称, 由C语言的字符串表示.
使用method_getNumberOfArguments可获取该方法的参数个数.
部分打印结果如下:
如在UIView的load方法中:
这样, 想要触发UIView的touchesBegan:withEvent:方法时, 实际调用的却是自定义的custom_touchesBegan:withEvent:方法.
另外, 关于runtime method swizzling的一个使用场景, 请参考博客:
iOS — 使用runtime解决3D Touch导致UIImagePicker崩溃的问题
如将一个字符串关联到一个数组:
这样, 当overview被手动释放时, 却不会被销毁. 因为关联策略指明了数组array要保有相关联的对象.
而array也释放时, overview才会被销毁.
设置关联对象, 指定被关联对象array, 关联关键字overviewKey, 关联对象overview即可:
获取关联对象, 需要传递被关联对象array和关联关键字overviewKey:
断开关联, 只需要设置关联对象为nil即可, 关联策略就无所谓了.
使用objc_removeAssociatedObjects可断开所有关联, 把对象恢复至原始状态.
DemoRuntime
The Objective-C language defers as many decisions as it can from compile time and link time to runtime. Whenever possible, it does things dynamically. This means that the language requires not just a compiler, but also a runtime system to execute the compiled code. The runtime system acts as a kind of operating system for the Objective-C language; it’s what makes the language work.
所谓运行时, 就是尽可能地把决定从编译器推迟到运行期, 就是尽可能地做到动态. 只是在运行的时候才会去确定对象的类型和方法的. 因此利用Runtime机制可以在程序运行时动态地修改类和对象中的所有属性和方法.
Objective-C中调用对象的方法时, 会向该对象发送一条消息, runtime根据该消息做出反应.
Runtime是一套比较底层的纯C语言的API, Objective-C是运行在Runtime上的, 因此在Runtime中动态添加和实现一些非常强大的功能也就不足为奇了.
在Objective-C代码中使用Runtime, 需要引入
#import <objc/runtime.h>
实例变量
OC中类和对象的实例变量, 实际上是一个指向objc_ivar结构体的指针Ivar./// An opaque type that represents an instance variable. typedef struct objc_ivar *Ivar;
通过runtime方法class_copyIvarList可获取一个指向类和对象的所有实例变量的Ivar指针.
u_int count = 0; Ivar *ivars = class_copyIvarList([UIView class], &count); for (int i = 0; i < count; i++) { const char *ivarName = ivar_getName(ivars[i]); NSString *str = [NSString stringWithCString:ivarName encoding:NSUTF8StringEncoding]; NSLog(@"ivarName : %@", str); }
获取到实例变量的Ivar指针后, 即可通过ivar_getName方法获取该实例变量的名称, 由C语言的字符串表示.
部分打印结果如下:
ivarName : _constraintsExceptingSubviewAutoresizingConstraints ivarName : _cachedTraitCollection ivarName : _layer ivarName : _layerRetained ivarName : _gestureInfo ivarName : _gestureRecognizers ivarName : _subviewCache ivarName : _templateLayoutView ivarName : _charge ivarName : _tag ivarName : _viewDelegate ivarName : _backgroundColorSystemColorName ivarName : _countOfMotionEffectsInSubtree ivarName : _countOfTraitChangeRespondersInDirectSubtree ivarName : _cachedScreenScale ivarName : _viewFlags ivarName : _retainCount
可以看到, 其中包含了UIView中的很多我们经常使用的实例变量.
对于属性, 如layer, tag等, 会自动生成 _ 前缀的成员变量
属性
OC中类和对象的属性, 是一个指向objc_property结构体的指针objc_property_t./// An opaque type that represents an Objective-C declared property. typedef struct objc_property *objc_property_t;
通过runtime方法class_copyPropertyList可获取一个指向类和对象的所有属性的objc_property_t指针.
u_int count = 0; objc_property_t *properties = class_copyPropertyList([UIView class], &count); for (int i = 0; i < count; i++) { const char *propertyName = property_getName(properties[i]); NSString *str = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]; NSLog(@"propertyName : %@", str); } free(properties);
获取到属性的objc_property_t指针后, 即可通过property_getName方法获取该属性的名称, 由C语言的字符串表示.
部分打印结果如下:
propertyName : hash propertyName : superclass propertyName : description propertyName : debugDescription propertyName : hash propertyName : superclass propertyName : description propertyName : debugDescription propertyName : userInteractionEnabled propertyName : tag propertyName : layer propertyName : focused propertyName : hash propertyName : superclass propertyName : description propertyName : center propertyName : bounds propertyName : transform
方法
OC中类和对象的方法, 是一个指向objc_method结构体的指针Method./// An opaque type that represents a method in a class definition. typedef struct objc_method *Method;
通过runtime方法class_copyMethodList可获取一个指向类和对象的所有方法的Method指针.
u_int count = 0; // 获取所有方法 Method *methods = class_copyMethodList([UIView class], &count); for (int i = 0; i < count; i++) { Method method = methods[i]; // 方法类型是SEL选择器类型 SEL methodName = method_getName(method); NSString *str = [NSString stringWithCString:sel_getName(methodName) encoding:NSUTF8StringEncoding]; int arguments = method_getNumberOfArguments(method); NSLog(@"methodName : %@, arguments Count: %d", str, arguments); } free(methods);
获取到方法的Method指针后, 即可通过method_getName方法获取该方法的名称, 由C语言的字符串表示.
使用method_getNumberOfArguments可获取该方法的参数个数.
部分打印结果如下:
2016-04-12 13:32:19.925 DemoRuntime[2244:54083] methodName : captureImageIBAIncludingSubviews:, arguments Count: 3 2016-04-12 13:32:19.925 DemoRuntime[2244:54083] methodName : captureImageFromIOSurfaceIBA, arguments Count: 2 2016-04-12 13:32:19.925 DemoRuntime[2244:54083] methodName : hideSubviewsIBA, arguments Count: 2 2016-04-12 13:32:19.925 DemoRuntime[2244:54083] methodName : safeCaptureImageIBA, arguments Count: 2 2016-04-12 13:32:19.926 DemoRuntime[2244:54083] methodName : unhideSubviewsIBAWithContext:, arguments Count: 3 2016-04-12 13:32:19.926 DemoRuntime[2244:54083] methodName : snapshotViewHierarchyIBA, arguments Count: 2 2016-04-12 13:32:19.926 DemoRuntime[2244:54083] methodName : iba_viewControllerName, arguments Count: 2 2016-04-12 13:32:19.926 DemoRuntime[2244:54083] methodName : iba_viewControllerMemoryAddress, arguments Count: 2 2016-04-12 13:32:19.926 DemoRuntime[2244:54083] methodName : iba_alignmentRectForCurrentFrame, arguments Count: 2 2016-04-12 13:32:19.927 DemoRuntime[2244:54083] methodName : iba_participatingConstraints, arguments Count: 2 2016-04-12 13:32:19.927 DemoRuntime[2244:54083] methodName : setIba_contentHuggingPriorities:, arguments Count: 3
Method Swizzling
通过修改一个已存在类的方法, 来实现方法替换是比较常用的runtime技巧.如在UIView的load方法中:
+ (void)load { Method origin = class_getInstanceMethod([UIView class], @selector(touchesBegan:withEvent:)); Method custom = class_getInstanceMethod([UIView class], @selector(custom_touchesBegan:withEvent:)); method_exchangeImplementations(origin, custom); } - (void)custom_touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // TODO }
这样, 想要触发UIView的touchesBegan:withEvent:方法时, 实际调用的却是自定义的custom_touchesBegan:withEvent:方法.
另外, 关于runtime method swizzling的一个使用场景, 请参考博客:
iOS — 使用runtime解决3D Touch导致UIImagePicker崩溃的问题
关联对象
Objective-C中的Category无法向既有的类添加属性, 因此可以使用runtime的关联对象(associated objects)来实现.如将一个字符串关联到一个数组:
static char overviewKey; NSArray *array = [[NSArray alloc] initWithObjects:@"1", @"2", @"3", nil]; // 为了演示的目的,使用initWithFormat:来确保字符串可以被销毁 NSString *overview = [[NSString alloc] initWithFormat:@"@", @"first three numbers"]; objc_setAssociatedObject(array, &overviewKey, overview, OBJC_ASSOCIATION_RETAIN);
这样, 当overview被手动释放时, 却不会被销毁. 因为关联策略指明了数组array要保有相关联的对象.
而array也释放时, overview才会被销毁.
设置关联对象, 指定被关联对象array, 关联关键字overviewKey, 关联对象overview即可:
objc_setAssociatedObject(array, &overviewKey, overview, OBJC_ASSOCIATION_RETAIN);
获取关联对象, 需要传递被关联对象array和关联关键字overviewKey:
NSString *associatedObject = (NSString *)objc_getAssociatedObject(array, &overviewKey);
断开关联, 只需要设置关联对象为nil即可, 关联策略就无所谓了.
objc_setAssociatedObject(array, &overviewKey, nil, OBJC_ASSOCIATION_ASSIGN);
使用objc_removeAssociatedObjects可断开所有关联, 把对象恢复至原始状态.
Demo
Demo地址:DemoRuntime
相关文章推荐
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- Java Runtime Environment 5.0 Update 12 下载
- php set_magic_quotes_runtime() 函数过时解决方法
- Asp.Net 程序错误Runtime Error原因与解决
- 讲解iOS开发中基本的定位功能实现
- iOS中定位当前位置坐标及转换为火星坐标的方法
- js判断客户端是iOS还是Android等移动终端的方法
- iOS应用中UISearchDisplayController搜索效果的用法
- IOS开发环境windows化攻略
- iOS应用中UITableView左滑自定义选项及批量删除的实现
- 浅析iOS应用开发中线程间的通信与线程安全问题
- 检测iOS设备是否越狱的方法
- System 类 和 Runtime 类的常用用法介绍
- .net平台推送ios消息的实现方法