iOS --- 理解Runtime机制及其使用场景
来源:互联网 发布:第三方抽奖软件 编辑:程序博客网 时间:2024/05/21 20:17
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, 需要引入
#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 : _constraintsExceptingSubviewAutoresizingConstraintsivarName : _cachedTraitCollectionivarName : _layerivarName : _layerRetainedivarName : _gestureInfoivarName : _gestureRecognizersivarName : _subviewCacheivarName : _templateLayoutViewivarName : _chargeivarName : _tagivarName : _viewDelegateivarName : _backgroundColorSystemColorNameivarName : _countOfMotionEffectsInSubtreeivarName : _countOfTraitChangeRespondersInDirectSubtreeivarName : _cachedScreenScaleivarName : _viewFlagsivarName : _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 : hashpropertyName : superclasspropertyName : descriptionpropertyName : debugDescriptionpropertyName : hashpropertyName : superclasspropertyName : descriptionpropertyName : debugDescriptionpropertyName : userInteractionEnabledpropertyName : tagpropertyName : layerpropertyName : focusedpropertyName : hashpropertyName : superclasspropertyName : descriptionpropertyName : centerpropertyName : boundspropertyName : 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: 32016-04-12 13:32:19.925 DemoRuntime[2244:54083] methodName : captureImageFromIOSurfaceIBA, arguments Count: 22016-04-12 13:32:19.925 DemoRuntime[2244:54083] methodName : hideSubviewsIBA, arguments Count: 22016-04-12 13:32:19.925 DemoRuntime[2244:54083] methodName : safeCaptureImageIBA, arguments Count: 22016-04-12 13:32:19.926 DemoRuntime[2244:54083] methodName : unhideSubviewsIBAWithContext:, arguments Count: 32016-04-12 13:32:19.926 DemoRuntime[2244:54083] methodName : snapshotViewHierarchyIBA, arguments Count: 22016-04-12 13:32:19.926 DemoRuntime[2244:54083] methodName : iba_viewControllerName, arguments Count: 22016-04-12 13:32:19.926 DemoRuntime[2244:54083] methodName : iba_viewControllerMemoryAddress, arguments Count: 22016-04-12 13:32:19.926 DemoRuntime[2244:54083] methodName : iba_alignmentRectForCurrentFrame, arguments Count: 22016-04-12 13:32:19.927 DemoRuntime[2244:54083] methodName : iba_participatingConstraints, arguments Count: 22016-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
- iOS --- 理解Runtime机制及其使用场景
- iOS --- 理解UIWindow及其常见使用场景
- iOS开发 runtime使用场景
- iOS runTime机制简单理解
- ios学习--Runtime的使用场景
- Runtime使用场景
- Runtime 使用场景
- iOS之Runtime的理解和使用
- iOS runtime——函数/使用方法/使用场景/示例
- iOS开发:runtime原理简单分析和基本使用场景
- iOS开发:runtime原理简单分析和基本使用场景
- 我理解的iOS中的RunTime机制(1)
- iOS runtime 机制浅析
- iOS runtime 机制
- 【iOS开发】runtime机制
- iOS runTime 机制详解
- iOS runtime机制
- iOS runtime机制
- Java设置GUI的窗口图标
- findLadders--BFS--a到z 每个字母尝试着换。
- 百度云私密分享多线程破解脚本
- Leetcode题解15 58. Length of Last Word
- 在texlive下使用CJK的3条小心得
- iOS --- 理解Runtime机制及其使用场景
- mysql连接IP地址失败的原因
- 原型模式
- SVM核技巧的经典解释
- 每天一篇python:简单爬虫下载图片篇
- 常见linux命令
- 第7周项目2-友元类
- Error running app: Instant Run requires 'Tools | Android | Enable ADB integration' to be enabled.
- [LintCode] 前序遍历和中序遍历树构造二叉树 Construct Binary Tree from Preorder and Inorder Traversal