谈Runtime机制和使用的整体化梳理

本文深入探讨Objective-C的Runtime机制,涵盖基本概念、方法调用原理及常见应用,如动态添加成员变量、方法交换等,辅以生动实例帮助理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

谈Runtime机制和使用的整体化梳理


相比“凌波微步”的Swift,Object-C被誉为“如来神掌”。传说Runtime就是支持这“如来神掌”说法的最好体现。听起来总是这么的神秘高级,于是总能在各个论坛看到碎片资料,时间一长总记不住哪里是哪里,每次都要打开好几个网页。这种记不住象现显然是知识体系还不完整重要体现。还是自己从Runtime的思想到动手代码呈现上做出总结尚为上策。

02.png

学习流程图

一、基本概念

  1. RunTime简称运行时,就是系统在运行的时候的一些机制,其中最主要的是消息机制。

  2. 对于C语言,函数的调用在编译的时候会决定调用哪个函数,编译完成之后直接顺序执行,无任何二义性。

  3. OC的函数调用成为消息发送。属于动态调用过程。在编译的时候并不能决定真正调用哪个函数(事实证明,在编 译阶段,OC可以调用任何函数,即使这个函数并未实现,只要申明过就不会报错。而C语言在编译阶段就会报错)。

  4. 只有在真正运行的时候才会根据函数的名称找 到对应的函数来调用。

官网文档还提供关于传统和现代版本Runtime的说明

  1. In the legacy runtime, if you change the layout of instance variables in a class, you must recompile classes that inherit from it.

  2. In the modern runtime, if you change the layout of instance variables in a class, you do not have to recompile classes that inherit from it.In addition, the modern runtime supports instance variable synthesis for declared properties (see Declared Properties in The Objective-C Programming Language).

二、知晓OC的方法调用在Runtime中具体的实现

1.OC代码调用一个方法

1
[self.loginBt login];

2.在编译时RunTime会将上述代码转化成[发送消息]

1
objc_msgSend(self.loginB,@selector(login));

三、常见的作用

既然是“如来神掌”,简直可以无法无天啦,当街拦下一个人问道“这是马还是鹿啊?”,那人看是Runtime大人惧怕道“Runtime大人,您说是马就是马,是鹿就是鹿~”。Runtime大快“wow哈哈哈~,见你乖巧,我也不为难于你。你缺头驴是吧?,本大人现在造一头送于你,迁回家便是!喔~哈哈哈”。

呵呵,扯远了,回到Runtime作用上。无所不能的事情就不一一介绍了,梳理下较为可能用的几个地方:

1. 动态的添加对象的成员变量和方法

2. 动态交换两个方法的实现

3. 实现分类也可以添加属性

4. 实现NSCoding的自动归档和解档

5. 实现字典转模型的自动转换

四、编写代码实现

1. 动态变量控制

1)Sense:

Teacher: What's your name?  

XiaoMing: My name is XiaoMing.  

Teacher: Pardon?  

XiaoMing: My name is __

在程序当中,假设XiaoMing的name原来的值为XiaoMing,后来被Runtime偷换了一个名字叫Minggo。那么,Runtime是如何做到的呢?

2)Step:

①动态获取XiaoMing类中的所有属性[当然包括私有]

1
Ivar *ivar = class_copyIvarList([self.xiaoMing class], &count);

②遍历属性找到对应name字段

1
const char *varName = ivar_getName( var );

③修改对应的字段值成Minggo

1
object_setIvar(self.xiaoMing,  var , @ "Minggo" );

3)Show Code:

QQ截图20160127164009.png

2. 动态交换方法

1)Sense:

Teacher: What's your name?  

XiaoMing: My name is XiaoMing.  

Teacher: Pardon?  

XiaoMing: My name is __

在程序当中,假设XiaoMing的第一次回答为firstSay,后来被Runtime交换了一个名字叫secondSay的方法,最终再调用firstSay的时候,其实是调用了secondSay的实现。那么,Runtime是如何做到的呢?

2)Step:

①动态找到firstSay和secondSay方法

1
2
Method m1 = class_getInstanceMethod([self.xiaoMing class], @selector(firstSay));
Method m2 = class_getInstanceMethod([self.xiaoMing class], @selector(secondSay));

②交换两个方法

1
method_exchangeImplementations(m1, m2);

3)Show Code:

1
2
3
4
5
6
7
8
9
10
11
-(void)answer{
 
     Method m1 = class_getInstanceMethod([self.xiaoMing class], @selector(firstSay));
     Method m2 = class_getInstanceMethod([self.xiaoMing class], @selector(secondSay));
     
     method_exchangeImplementations(m1, m2);
     NSString *secondName = [self.xiaoMing firstSay];
     
     self.nameTf.text = secondName;
     NSLog(@ "XiaoMing:My name is %@" ,secondName);
}

3. 动态添加方法

1)Sense:

Teacher: Where is LiLei from?

XiaoMing: I don't know.

Teacher: Guess?.

LiHua: He is from __

在程序当中,假设XiaoMing的中没有guess这个方法,后来被Runtime添加一个名字叫guess的方法,最终再调用guess方法做出相应。那么,Runtime是如何做到的呢?

2)Step:

①动态给XiaoMing类中添加guess方法:

1
class_addMethod([self.xiaoMing class], @selector(guess), (IMP)guessAnswer,  "v@:" );

这里参数地方说明一下:

(IMP)guessAnswer 意思是guessAnswer的地址指针;

"v@:" 意思是,v代表无返回值void,如果是i则代表int;@代表 id sel; : 代表 SEL _cmd;

“v@:@@” 意思是,两个参数的没有返回值。

②调用guess方法响应事件:

1
[self.xiaoMing performSelector:@selector(guess)];

③编写guessAnswer的实现:

1
2
3
void guessAnswer(id self,SEL _cmd){
     NSLog(@ "He is from GuangTong" );   
}

这个有两个地方留意一下:

1.void的前面没有+、-号,因为只是C的代码。

2.必须有两个指定参数(id self,SEL _cmd)

3)Show Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-(void)answer{
     class_addMethod([self.xiaoMing class], @selector(guess), (IMP)guessAnswer,  "v@:" );
     if  ([self.xiaoMing respondsToSelector:@selector(guess)]) {
         [self.xiaoMing performSelector:@selector(guess)];
         
     else {
         NSLog(@ "Sorry,I don't know" );
     }
     self.cityTf.text = @ "GuangTong" ;
}
 
void guessAnswer(id self,SEL _cmd){
 
     NSLog(@ "He is from GuangTong" );
     
}

4. 动态为Category扩展加属性

这一点上有两点要表达一下:第一,XCode运行你在Category的.h文件申明@Property,编译通过,但运行时如果没有Runtime处理,进行赋值取值,就马上报错。第二,这一点是iOS面试当中经常面到的问题:如何给扩展添加属性?

1)Sense:

Teacher: What's your Chinese name?

XiaoMing: I have no one.

LiHua: You should have one.

LiHua: Your Chinese name is __

在程序当中,假设XiaoMing的中没有chineseName这个属性,后来被Runtime添加一个名字叫chineseName的属性。那么,Runtime是如何做到的呢?

2)Step:

①申明chineseName属性

1
2
3
4
5
6
7
#import "XiaoMing.h"
 
@interface XiaoMing (MutipleName)
 
@property(nonatomic,copy) NSString *chineseName;
 
@end

②动态添加属性和实现方法

214.png

③使用chineseName属性

1
2
3
4
-(void)answer{
     NSLog(@ "My Chinese name is %@" ,self.xiaoMing.chineseName);
     self.chineseNameTf.text = self.xiaoMing.chineseName;
}

3)Show Code:

上边就是最要的Code了。以下更精彩。

五、效果图更直观

runtime.gif

六、源码下载地址更详细

https://github.com/minggo620/iOSRuntimeLearn.git

<think>好的,用户想用C++编写一个图像处理的模型推理类,支持ONNX模型,同时要集成OpenVINOTensorRT加速。首先,我需要理清整个架构的设计思路。 首先,得确定这个类的核心功能。用户需要处理图像数据,进行预处理、推理后处理。支持两种不同的推理引擎,可能需要抽象出一个基类,然后分别实现两个子类。这样设计可以保证接口统一,方便扩展。 然后考虑模型加载部分。ONNX模型是通用的,但OpenVINOTensorRT都需要将ONNX转换成自己的格式。比如,OpenVINO需要转换成IR格式,而TensorRT可能需要生成plan文件。所以在初始化的时候,可能需要检查是否有已经转换好的模型,如果没有的话,可能需要进行转换。不过这一步可能会增加复杂度,用户可能需要提前转换好,或者在类内部处理转换逻辑。 接下来是预处理。不同的框架可能有不同的数据布局要求,比如OpenVINO通常是NCHW,而TensorRT可能也是类似的。需要确保图像被正确地调整大小、归一化,并转换为正确的数据格式,比如RGB或BGR顺序。可能需要一个统一的预处理方法,或者根据不同的引擎进行调整。 推理部分,需要封装各自的API。OpenVINO有InferenceEngine库,TensorRT有自己的C++ API。这两个引擎的初始化、上下文创建、内存分配等步骤差异较大,所以每个子类需要独立实现这些步骤。比如,OpenVINO需要加载网络,创建推理请求,而TensorRT需要构建引擎,创建上下文。 后处理部分可能比较统一,比如解析输出张量,应用非极大值抑制等。但也要考虑不同引擎的输出顺序或格式是否一致,可能需要调整后处理的代码。 异步推理的支持也是一个考虑点。用户可能需要同步异步两种模式,但初期可能先实现同步,确保功能正常后再扩展。 内存管理方面,需要注意输入输出缓冲区的分配。比如,OpenVINO使用Blob,而TensorRT使用Device内存Host内存,可能需要统一管理这些资源,避免内存泄漏。 错误处理也不能忽视,比如模型加载失败、推理错误等,需要适当的异常机制或错误码返回。 另外,需要考虑跨平台兼容性,比如OpenVINO主要在Intel硬件上运行,而TensorRT在NVIDIA GPU上。所以在初始化时可能需要检测可用硬件,自动选择或让用户指定使用哪个引擎。 最后,示例代码的结构需要清晰,基类定义接口,子类实现具体细节。用户使用时,可以通过工厂模式或配置参数选择不同的推理引擎,这样比较灵活。 可能遇到的挑战包括不同引擎的API变化、内存对齐问题、性能优化,以及如何处理不同的预处理需求。需要查阅最新的官方文档,确保接口的正确性,并进行充分的测试,尤其是在不同硬件环境下的稳定性。 总结下来,设计这样一个类需要模块化的结构,良好的抽象,同时处理不同推理引擎的细节,确保高效易用性。</think>好的,我将为您梳理使用C++开发跨框架(OpenVINO/TensorRT)的ONNX模型推理类的设计逻辑,以下是分步骤说明: --- ### **一、整体架构设计** ```cpp class InferenceEngine { public: virtual bool Initialize(const std::string& modelPath) = 0; virtual void Preprocess(const cv::Mat& input) = 0; virtual void Infer() = 0; virtual void Postprocess(std::vector<float>& outputs) = 0; virtual ~InferenceEngine() {} }; class OpenVINOEngine : public InferenceEngine {...}; class TensorRTEngine : public InferenceEngine {...}; ``` --- ### **二、核心功能模块** #### 1. **模型加载与初始化** - **OpenVINO**: ```cpp bool OpenVINOEngine::Initialize(const std::string& modelPath) { Core ie; // 转换ONNX为IR格式 auto network = ie.ReadNetwork(modelPath); ExecutableNetwork executable_network = ie.LoadNetwork(network, "CPU"); infer_request_ = executable_network.CreateInferRequest(); } ``` - **TensorRT**: ```cpp bool TensorRTEngine::Initialize(const std::string& modelPath) { IBuilder* builder = createInferBuilder(logger); nvinfer1::INetworkDefinition* network = builder->createNetworkV2(flags); auto parser = nvonnxparser::createParser(*network, logger); parser->parseFromFile(modelPath.c_str(), static_cast<int>(severity)); } ``` #### 2. **预处理模块** - 统一处理逻辑: ```cpp void Preprocess(const cv::Mat& input) { cv::Mat resized, normalized; cv::resize(input, resized, cv::Size(224, 224)); // 调整尺寸 resized.convertTo(normalized, CV_32F, 1.0/255.0); // 归一化 // 转换为NCHW格式... } ``` #### 3. **推理执行** - **OpenVINO**: ```cpp void OpenVINOEngine::Infer() { infer_request_.Infer(); } ``` - **TensorRT**: ```cpp void TensorRTEngine::Infer() { context_->enqueueV2(buffers_, stream_, nullptr); } ``` #### 4. **后处理模块** ```cpp void Postprocess(std::vector<float>& outputs) { // 获取输出Buffer float* prob = static_cast<float*>(buffers_[outputIndex]); // 应用softmax/argmax等操作... } ``` --- ### **三、关键问题处理** 1. **内存管理** - OpenVINO使用`Blob`对象自动管理内存 - TensorRT需手动管理Host/Device内存: ```cpp cudaMalloc(&deviceBuffer_, size); cudaMemcpy(deviceBuffer_, hostBuffer_, size, cudaMemcpyHostToDevice); ``` 2. **多线程支持** - OpenVINO通过`AsyncInferRequest`实现异步推理 - TensorRT使用CUDA Stream实现流水线 3. **性能优化** - OpenVINO:设置CPU扩展(MKLDNN)、GPU插件 - TensorRT:FP16/INT8量化、层融合优化 --- ### **四、工厂模式示例** ```cpp std::unique_ptr<InferenceEngine> CreateEngine(EngineType type) { switch(type) { case EngineType::OPENVINO: return std::make_unique<OpenVINOEngine>(); case EngineType::TENSORRT: return std::make_unique<TensorRTEngine>(); default: throw std::runtime_error("Unsupported engine type"); } } ``` --- ### **五、使用示例** ```cpp int main() { auto engine = CreateEngine(EngineType::OPENVINO); engine->Initialize("model.onnx"); cv::Mat image = cv::imread("input.jpg"); engine->Preprocess(image); engine->Infer(); std::vector<float> results; engine->Postprocess(results); } ``` --- ### **六、注意事项** 1. 确保ONNX模型输入/输出节点名称与代码匹配 2. OpenVINO需要`libopenvino.so`,TensorRT需链接`nvinfer.so` 3. 处理不同框架的数值精度差异(FP32/FP16) 4. 建议使用RAII模式管理CUDA/TensorRT资源 如果需要更详细的某个模块实现(如TensorRT的上下文创建),我可以提供进一步的代码示例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值