杂谈转载

一、什么是运行时(Runtime)?

  • 运行时是苹果提供的纯C语言的开发库(运行时是一种非常牛逼、开发中经常用到的底层技术)

二、运行时的作用?

  • 能获得某个类的所有成员变量
  • 能获得某个类的所有属性
  • 能获得某个类的所有方法
  • 交换方法实现
  • 能动态添加一个成员变量
  • 能动态添加一个属性
  • 能动态添加一个方法

三、案例:运行时获取成员变量名称

  • 1、分析
#import <Foundation/Foundation.h>
#import "XMGPerson.h" #import <objc/runtime.h> int main(int argc, const char * argv[]) { @autoreleasepool { // 成员变量的数量 unsigned int outCount = 0; // 获得所有的成员变量 // ivars是一个指向成员变量的指针 // ivars默认指向第0个成员变量(最前面) Ivar *ivars = class_copyIvarList([XMGPerson class], &outCount); // 遍历所有的成员变量 for (int i = 0; i<outCount; i++) { // 取出i位置对应的成员变量 // Ivar ivar = *(ivars + i); Ivar ivar = ivars[i]; // 获得成员变量的名字 NSLog(@"%s", ivar_getName(ivar)); } // 如果函数名中包含了copy\new\retain\create等字眼,那么这个函数返回的数据就需要手动释放 free(ivars); // Ivar ivar = *ivars; // Ivar ivar2 = *(ivars + 1); // NSLog(@"%s %s", ivar_getName(ivar), ivar_getName(ivar2)); // 一个Ivar就代表一个成员变量 // int *p; 指向int类型的变量 // Ivar *ivars; 指向Ivar类型的变量 } return 0; }
  • 2、获取UITextFiled成员变量的名称

Snip20151027_1.png
 // 成员变量的数量unsigned int outCount = 0;// 获得所有的成员变量 Ivar *ivars = class_copyIvarList([UITextField class], &outCount); // 遍历所有的成员变量 for (int i = 0; i<outCount; i++) { // 取出i位置对应的成员变量 Ivar ivar = ivars[i]; // 获得成员变量的名字 NSLog(@"%s", ivar_getName(ivar)); } // 如果函数名中包含了copy\new\retain\create等字眼,那么这个函数返回的数据就需要手动释放 free(ivars);

四、iOS底层

1、The Runtime 简单介绍

  • Objective-C是一门简单的语言,95%是C。只是在语言层面上加了些关键字和语法。真正让Objective-C如此强大的是它的运行时。它很小但却很强大。它的核心是消息分发。

Messages

  • 执行一个方法,有些语言,编译器会执行一些额外的优化和错误检查,因为调用关系很直接也很明显。但对于消息分发来说,就不那么明显了。在发消息前不必知道某个对象是否能够处理消息。你把消息发给它,它可能会处理,也可能转给其他的Object来处理。一个消息不必对应一个方法,一个对象可能实现一个方法来处理多条消息。
  • 在Objective-C中,消息是通过objc_msgSend()这个runtime方法及相近的方法来实现的。这个方法需要一个target,selector,还有一些参数。理论上来说,编译器只是把消息分发变成objc_msgSend来执行。比如下面这两行代码是等价的。
[array insertObject:foo atIndex:5];
objc_msgSend(array, @selector(insertObject:atIndex:), foo, 5);

Objects, Classes, MetaClasses

  • 大多数面向对象的语言里有 classes 和 objects 的概念。Objects通过Classes生成。但是在Objective-C中,classes本身也是objects,也可以处理消息,这也是为什么会有类方法和实例方法。具体来说,Objective-C中的Object是一个结构体(struct),第一个成员是isa,指向自己的class。这是在objc/objc.h中定义的。
typedef struct objc_object {Class isa;
} *id;
  • object的class保存了方法列表,还有指向父类的指针。但classes也是objects,也会有isa变量,那么它又指向哪儿呢?这里就引出了第三个类型: metaclasses。一个 metaclass被指向class,class被指向object。它保存了所有实现的方法列表,以及父类的metaclass。如果想更清楚地了解objects,classes以及metaclasses是如何一起工作地,可以阅读这篇文章。

Methods, Selectors and IMPs

  • 我们知道了运行时会发消息给对象。我们也知道一个对象的class保存了方法列表。那么这些消息是如何映射到方法的,这些方法又是如何被执行的呢?

  • 第一个问题的答案很简单。class的方法列表其实是一个字典,key为selectors,IMPs为value。一个IMP是指向方法在内存中的实现。很重要的一点是,selector和IMP之间的关系是在运行时才决定的,而不是编译时。这样我们就能玩出些花样。

  • IMP通常是指向方法的指针,第一个参数是self,类型为id,第二个参数是_cmd,类型为SEL,余下的是方法的参数。这也是self和_cmd被定义的地方。下面演示了Method和IMP

- (id)doSomethingWithInt:(int)aInt{}id doSomethingWithInt(id self, SEL _cmd, int aInt){}
  • 现在我们知道了objects,classes,selectors,IMPs以及消息分发,那么运行时到底能做什么呢?

运行时到底能做什么呢?

  • 作用:

    • 创建、修改、自省classes和objects
    • 消息分发
  • 之前已经提过消息分发,不过这只是一小部分功能。所有的运行时方法都有特定的前缀。下面是一些有意思的方法:

class

  • class开头的方法是用来修改和自省classes。
  • 方法如:
    • 能拿到一个class的所有内容
      class_addIvar, class_addMethod, class_addProperty和class_addProtocol允许重建classes。class_copyIvarList, class_copyMethodList, class_copyProtocolList和class_copyPropertyList
    • 返回单个内容
      class_getClassMethod, class_getClassVariable, class_getInstanceMethod, class_getInstanceVariable, class_getMethodImplementation和class_getProperty
    • 一些通用的自省方法
      class_conformsToProtocol, class_respondsToSelector, class_getSuperclass
    • 创建一个object
      class_createInstance来创建一个object

ivar

  • 这些方法能让你得到名字,内存地址和Objective-C type encoding。

method

  • 这些方法主要用来自省,比如:
method_getName, method_getImplementation, method_getReturnType等等
  • 也有一些修改的方法,包括:
    method_setImplementation和method_exchangeImplementations

objc

  • 一旦拿到了object,你就可以对它做一些自省和修改。你可以get/set ivar, 使用object_copy和object_dispose来copy和free object的内存。不仅是拿到一个class,而是可以使用object_setClass来改变一个object的class。

property

  • 属性保存了很大一部分信息。除了拿到名字,你还可以使用property_getAttributes来发现property的更多信息,如返回值、是否为atomic、getter/setter名字、是否为dynamic、背后使用的ivar名字、是否为弱引用。

protocol

  • Protocols有点像classes,但是精简版的,运行时的方法是一样的。你可以获取method, property, protocol列表, 检查是否实现了其他的protocol。

sel

  • 最后我们有一些方法可以处理 selectors,比如获取名字,注册一个selector等等。

2、运行时能干什么?(举例)

2.1 Classes And Selectors From Strings

  • 比较基础的一个动态特性是通过String来生成Classes和Selectors。Cocoa提供了NSClassFromString和NSSelectorFromString方法,使用起来很简单:

    Class stringclass = NSClassFromString(@"NSString")
  • 于是我们就得到了一个string class。接下来:

    NSString *myString = [stringclass stringWithString:@"Hello World"];
  • 为什么要这么做呢?直接使用Class不是更方便?通常情况下是,但有些场景下这个方法会很有用。首先,可以得知是否存在某个class,NSClassFromString 会返回nil,如果运行时不存在该class的话。

  • 另一个使用场景是根据不同的输入返回不同的class或method。比如你在解析一些数据,每个数据项都有要解析的字符串以及自身的类型(String,Number,Array)。你可以在一个方法里搞定这些,也可以使用多个方法。其中一个方法是获取type,然后使用if来调用匹配的方法。另一种是根据type来生成一个selector,然后调用之。以下是两种实现方式:

- (void)parseObject:(id)object {for (id data in object) {if ([[data type] isEqualToString:@"String"]) { [self parseString:[data value]]; } else if ([[data type] isEqualToString:@"Number"]) { [self parseNumber:[data value]]; } else if ([[data type] isEqualToString:@"Array"]) { [self parseArray:[data value]]; } } } - (void)parseObjectDynamic:(id)object { for (id data in object) { [self performSelector:NSSelectorFromString([NSString stringWithFormat:@"parse%@:", [data type]]) withObject:[data value]]; } } - (void)parseString:(NSString *)aString {} - (void)parseNumber:(NSString *)aNumber {} - (void)parseArray:(NSString *)aArray {}
  • 可一看到,你可以把7行带if的代码变成1行。将来如果有新的类型,只需增加实现方法即可,而不用再去添加新的 else if。

2.2 Method Swizzling

  • 之前我们讲过,方法由两个部分组成。Selector相当于一个方法的id;IMP是方法的实现。这样分开的一个便利之处是selector和IMP之间的对应关系可以被改变。比如一个 IMP 可以有多个 selectors 指向它。

  • 而 Method Swizzling 可以交换两个方法的实现。或许你会问“什么情况下会需要这个呢?”。我们先来看下Objective-C中,两种扩展class的途径。首先是 subclassing。你可以重写某个方法,调用父类的实现,这也意味着你必须使用这个subclass的实例,但如果继承了某个Cocoa class,而Cocoa又返回了原先的class(比如 NSArray)。这种情况下,你会想添加一个方法到NSArray,也就是使用Category。99%的情况下这是OK的,但如果你重写了某个方法,就没有机会再调用原先的实现了。

  • Method Swizzling 可以搞定这个问题。你可以重写某个方法而不用继承,同时还可以调用原先的实现。通常的做法是在category中添加一个方法(当然也可以是一个全新的class)。可以通过method_exchangeImplementations这个运行时方法来交换实现。来看一个demo,这个demo演示了如何重写addObject:方法来纪录每一个新添加的对象。

#import  <objc/runtime.h>@interface NSMutableArray (LoggingAddObject) - (void)logAddObject:(id)aObject; @end @implementation NSMutableArray (LoggingAddObject) + (void)load { Method addobject = class_getInstanceMethod(self, @selector(addObject:)); Method logAddobject = class_getInstanceMethod(self, @selector(logAddObject:)); method_exchangeImplementations(addObject, logAddObject); } - (void)logAddObject:(id)aobject { [self logAddObject:aObject]; NSLog(@"Added object %@ to array %@", aObject, self); } @end
  • 我们把方法交换放到了load中,这个方法只会被调用一次,而且是运行时载入。如果指向临时用一下,可以放到别的地方。注意到一个很明显的递归调用logAddObject:。这也是Method Swizzling容易把我们搞混的地方,因为我们已经交换了方法的实现,所以其实调用的是addObject:

动态继承、交换

  • 我们可以在运行时创建新的class,这个特性用得不多,但其实它还是很强大的。你能通过它创建新的子类,并添加新的方法。

  • 但这样的一个子类有什么用呢?别忘了Objective-C的一个关键点:object内部有一个叫做isa的变量指向它的class。这个变量可以被改变,而不需要重新创建。然后就可以添加新的ivar和方法了。可以通过以下命令来修改一个object的class.

    object_setClass(myObject, [MySubclass class]);
  • 这可以用在Key Value Observing。当你开始observing an object时,Cocoa会创建这个object的class的subclass,然后将这个object的isa指向新创建的subclass。

动态方法处理

  • 目前为止,我们讨论了方法交换,以及已有方法的处理。那么当你发送了一个object无法处理的消息时会发生什么呢?很明显,"it breaks"。大多数情况下确实如此,但Cocoa和runtime也提供了一些应对方法。

  • 首先是动态方法处理。通常来说,处理一个方法,运行时寻找匹配的selector然后执行之。有时,你只想在运行时才创建某个方法,比如有些信息只有在运行时才能得到。要实现这个效果,你需要重写+resolveInstanceMethod: 和/或 +resolveClassMethod:。如果确实增加了一个方法,记得返回YES。

+ (BOOL)resolveInstanceMethod:(SEL)aSelector {if (aSelector == @selector(myDynamicMethod)) {class_addMethod(self, aSelector, (IMP)myDynamicIMP, "v@:");return YES;}return [super resolveInstanceMethod:aSelector]; }
  • 那Cocoa在什么场景下会使用这些方法呢?Core Data用得很多。NSManagedObjects有许多在运行时添加的属性用来处理get/set属性和关系。那如果Model在运行时被改变了呢?

消息转发

  • 如果 resolve method 返回NO,运行时就进入下一步骤:消息转发。有两种常见用例。1) 将消息转发到另一个可以处理该消息的object。2) 将多个消息转发到同一个方法。

  • 消息转发分两步。首先,运行时调用-forwardingTargetForSelector:,如果只是想把消息发送到另一个object,那么就使用这个方法,因为更高效。如果想要修改消息,那么就要使用-forwardInvocation:,运行时将消息打包成NSInvocation,然后返回给你处理。处理完之后,调用invokeWithTarget:。

  • Cocoa有几处地方用到了消息转发,主要的两个地方是代理(Proxies)和响应链(Responder Chain)。NSProxy是一个轻量级的class,它的作用就是转发消息到另一个object。如果想要惰性加载object的某个属性会很有用。NSUndoManager也有用到,不过是截取消息,之后再执行,而不是转发到其他的地方。

  • 响应链是关于Cocoa如何处理与发送事件与行为到对应的对象。比如说,使用Cmd+C执行了copy命令,会发送-copy:到响应链。首先是First Responder,通常是当前的UI。如果没有处理该消息,则转发到下一个-nextResponder。这么一直下去直到找到能够处理该消息的object,或者没有找到,报错。

使用Block作为Method IMP

  • iOS 4.3带来了很多新的runtime方法。除了对properties和protocols的加强,还带来一组新的以 imp 开头的方法。通常一个 IMP 是一个指向方法实现的指针,头两个参数为 object(self)和selector(_cmd)。iOS 4.0和Mac OS X 10.6 带来了block,imp_implementationWithBlock() 能让我们使用block作为 IMP,下面这个代码片段展示了如何使用block来添加新的方法。
IMP myIMP = imp_implementationWithBlock(^(id _self, NSString *string) {NSLog(@"Hello %@", string); }); class_addMethod([MYclass class], @selector(sayHello:), myIMP, "v@:@");
  • 可以看到,Objective-C 表面看起来挺简单,但还是很灵活的,可以带来很多可能性。动态语言的优势在于在不扩展语言本身的情况下做很多很灵巧的事情。比如Key Value Observing,提供了优雅的API可以与已有的代码无缝结合,而不需要新增语言级别的特性。

转载于:https://www.cnblogs.com/liuxiaokun/p/6505612.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/469268.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Pytorch神经网络实战案例】24 基于迁移学习识别多种鸟类(CUB-200数据集)

1 迁移学习 在实际开发中&#xff0c;常会使用迁移学习将预训练模型中的特征提取能力转移到自己的模型中。 1.1 迁移学习定义 迁移学习指将在一个任务上训练完成的模型进行简单的修改&#xff0c;再用另一个任务的数据继续训练&#xff0c;使之能够完成新的任务。 1.1.1 迁…

【Pytorch神经网络理论篇】 33 基于图片内容处理的机器视觉:目标检测+图片分割+非极大值抑制+Mask R-CNN模型

同学你好&#xff01;本文章于2021年末编写&#xff0c;获得广泛的好评&#xff01; 故在2022年末对本系列进行填充与更新&#xff0c;欢迎大家订阅最新的专栏&#xff0c;获取基于Pytorch1.10版本的理论代码(2023版)实现&#xff0c; Pytorch深度学习理论篇(2023版)目录地址…

arduino i2c 如何写16位寄存器_树莓派3B开发Go语言(二)寄存器版本GPIO

作者&#xff1a;爪爪熊链接&#xff1a;https://www.jianshu.com/p/0495c0554a63來源&#xff1a;简书之前将go语言的运行环境给搭建起来了&#xff0c;但是没有开始真正的试试Go 语言操作树莓派硬件的效果。一、树莓派3B硬件介绍树莓派3B采用了博通的BCM2837方案&#xff0c;…

【Pytorch神经网络实战案例】25 (带数据增强)基于迁移学习识别多种鸟类(CUB-200数据集)

1 数据增强 在目前分类效果最好的EficientNet系列模型中&#xff0c;EfficientNet-B7版本的模型就是使用随机数据增强方法训练而成的。 RandAugment方法也是目前主流的数据增强方法&#xff0c;用RandAugment方法进行训练&#xff0c;会使模型的精度得到提升。 2 RandAugment…

diskgenius 数据迁移_U盘格式化后数据恢复免费方法教程

U盘里的数据一般都很重要&#xff0c;比如论文或者办公文件&#xff0c;而有时候我们会被病毒或者误操作把U盘给格式化了&#xff0c;这时候要怎么恢复U盘里的数据呢&#xff0c;只有一个办法&#xff0c;就是用U盘数据恢复软件&#xff0c;但网上此类软件虽然很多&#xff0c;…

【Pytorch神经网络理论篇】 34 样本均衡+分类模型常见损失函数

同学你好&#xff01;本文章于2021年末编写&#xff0c;获得广泛的好评&#xff01; 故在2022年末对本系列进行填充与更新&#xff0c;欢迎大家订阅最新的专栏&#xff0c;获取基于Pytorch1.10版本的理论代码(2023版)实现&#xff0c; Pytorch深度学习理论篇(2023版)目录地址…

安卓 camera 调用流程_安卓如何做出微信那样的界面仿微信“我”的界面1/5

本系列目标通过安卓编程仿写微信“我”的界面,让大家也能做出类似微信界面.效果图如下:本文目标做出页面顶部的相机部分(其他部分在后续文章中逐步分享).效果图如下:实现方案通过截图工具或者下载一张照相机照片,放到工程的src/main/res/drawable目录下,命名为camera.png添加一…

【Pytorch神经网络实战案例】26 MaskR-CNN内置模型实现目标检测

1 Pytorch中的目标检测内置模型 在torchvision库下的modelsldetecton目录中&#xff0c;找到__int__.py文件。该文件中存放着可以导出的PyTorch内置的目标检测模型。 2 MaskR-CNN内置模型实现目标检测 2.1 代码逻辑简述 将COCO2017数据集上的预训练模型maskrcnm_resnet50_fp…

【Pytorch神经网络实战案例】27 MaskR-CNN内置模型实现语义分割

1 PyTorch中语义分割的内置模型 在torchvision库下的models\segmentation目录中&#xff0c;找到segmentation.Py文件。该文件中存放着PyTorch内置的语义分割模型。 2 MaskR-CNN内置模型实现语义分割 2.1 代码逻辑简述 将COCO 2017数据集上的预训练模型dceplabv3_resnet101…

怎么查看电脑内存和配置_电脑内存不足处理方法,电脑卡死处理方法。

超过10万人正在关注赶快来关注吧&#xff0c;这里有你想找的热点资讯&#xff0c;这里有你想要的各种资料&#xff0c;还有海量的资源&#xff0c;还在等什么。快来关注&#xff0c;大佬带你开车。电脑系统经常奔溃&#xff0c;软件经常运行不了&#xff0c;开不了机&#xff0…

前端开源项目周报0307

由OpenDigg 出品的前端开源项目周报第十一期来啦。我们的前端开源周报集合了OpenDigg一周来新收录的优质的前端开源项目&#xff0c;方便前端开发人员便捷的找到自己需要的项目工具等。 react-trend 简单优雅的光线 react-progressive-web-app 优化ProgressiveWeb应用开发 pull…

【Pytorch神经网络理论篇】 35 GaitSet模型:步态识别思路+水平金字塔池化+三元损失

同学你好&#xff01;本文章于2021年末编写&#xff0c;获得广泛的好评&#xff01; 故在2022年末对本系列进行填充与更新&#xff0c;欢迎大家订阅最新的专栏&#xff0c;获取基于Pytorch1.10版本的理论代码(2023版)实现&#xff0c; Pytorch深度学习理论篇(2023版)目录地址…

win7分区软件_神奇的工作室win7旗舰版重装系统连不上网怎么解决

深度技术win7系统下载有的时刻我们的电脑安装、重装了win10操作系统之后有的小伙伴们就发现了自己的电脑连不上网了。对于这种问题小编以为可能是我们的电脑在安装系统的过程中泛起了一些内部组件的冲突或者是由于网卡驱动没有安装好导致的&#xff0c;可以通过重新安装、重装驱…

【Pytorch神经网络实战案例】28 GitSet模型进行步态与身份识别(CASIA-B数据集)

1 CASIA-B数据集 本例使用的是预处理后的CASIA-B数据集&#xff0c; 数据集下载网址如下。 http&#xff1a;//www.cbsr.ia.ac.cn/china/Gait%20Databases%20cH.asp 该数据集是一个大规模的、多视角的步态库。其中包括124个人&#xff0c;每个人有11个视角(0&#xff0c;18&am…

Android Camera调用流程

一个流程图画的非常好的文章 http://blog.csdn.net/lushengchu_luis/article/details/11033095 1、Packages/apps/到framework 打开Camera ./packages/apps/Camera/src/com/android/camera/Camera.java 进来第一个肯定是onCreate(Bundle icicle) { 这里是开始了一个Camera…

【Pytorch神经网络实战案例】29 【代码汇总】GitSet模型进行步态与身份识别(CASIA-B数据集)

1 GaitSet_DataLoader.py import numpy as np # 引入基础库 import os import torch.utils.data as tordata from PIL import Image from tqdm import tqdm import random# 1.1定义函数&#xff0c;加载文件夹的文件名称# load_data函数&#xff0c; 分为3个步骤&#xff1a;…

linq from 多个sum_快手重拳打击劣质电商 7月以来封禁700多个团伙账号

何为劣质电商&#xff1f;炒作演戏&#xff1f;PK售卖劣质商品&#xff1f;私下交易&#xff1f;夸大其词&#xff1f;……在快手电商的定义里&#xff0c;有上述不良行为的&#xff0c;都可以定义为劣质电商。快手电商站内官方号“快手卖货助手”日前发布第 11 期“自售或PK销…

win10怎么更改账户名称_Win10邮件功能如何查看邮件

win10的用户当中&#xff0c;一方面有说系统臃肿的&#xff0c;另外一方面有说功能多了不少&#xff0c;好用。不管是出于前者还是后者&#xff0c;win10功能确实多了不少&#xff0c;尤其是一些比较常用的功能&#xff0c;比如说邮箱功能&#xff0c;一般用户可能会选择登录网…

AttributeError: ‘set‘ object has no attribute ‘items‘

AttributeError: ‘set’ object has no attribute ‘items’ 出现这个问题&#xff0c;原因可能是定义的header有问题 正确如下&#xff1a; header{“key”&#xff1a;“value”} 如果是直接在请求数据中复制&#xff0c;很有可能会忽略键和值的冒号。

使用eclipse以及Juint进行测试

打开eclipse后&#xff0c;点击左上角的File&#xff0c;新建一个project&#xff0c;命名为testJunit&#xff0c;然后在src目录下新建两个包&#xff0c;分别命名为TestScore和Test(这是文件夹里没有文件所以是白色)。 在TestScore中新建一个class&#xff0c;命名为Score.ja…