iOS主要知识点梳理回顾-3-运行时

运行时(runtime)

        运行时是OC的重要特性,也是OC动态性的根本支撑。动态,如果利用好了,扩展性就很强。当然了,OC的动态性只能算是一个一般水平。与swift、java这种强类型校验的语言相比,OC动态性很强,和js这种纯动态的语言(随时给类增加函数、属性)相比,OC的动态性就弱很多。动态,可以帮助我们在运行时修改类的属性、函数、甚至创建一个新类

相关知识点

消息机制

        OC中的方法调用、属性读写等都是通过消息机制来处理的,当我们调用一个方法时,其实是向那个实例发送了一个消息(包含类方法,类本身是Class的实例)。所以后面的一些功能特点其实也使用了消息机制的特性,这里我们主要说说消息转发。

OC的函数调用实际上发送了一条消息,那调用过程就是消息处理过程,首先从类方法或者实例方法列表中获取你要调用的方法,如果没找到就进入转发流程。转发流程中预留了可扩展的点,大致过程是这样的

  1. 动态解析,此过程如果正确添加了方法就执行方法并结束转发,否则继续下面的步骤
  2. 备用接收者,转发给其他类/实例,如果不转发,则继续下面的步骤
  3. 完整转发,获取方法签名并转发,如果正确设置了签名并转发则结束,否则继续
  4. 调用未找到方法函数并抛出异常


动态解析

类方法动态解析(resolveClassMethod)

+ (BOOL)resolveClassMethod:(SEL)sel {
    NSString *selName = NSStringFromSelector(sel);
    NSLog(@"%s : %@", __func__, selName);
    if (sel == @selector(greet)) {
        // 动态添加 +greet 方法
        class_addMethod(object_getClass(self), sel, (IMP)greet1, "v@:");
    }
    return NO;
}

void greet1(id self, SEL _cmd) {
    NSLog(@"Hello from MyClass!");
}

这里我用class_addMethod来动态添加了一个方法,方法名是greet,方法的实现指向了greet1.还应该注意到class_addMethod的第一个参数,他的类型是Class,如果写self是不生效的,需要用object_getClass(self)才可以。原因是我们要给原类增加方法,具体:

  • object_getClass(self) 返回类的 元类(meta-class),是 self 的类的类对象。
  • self和self.class 返回的是 类对象,即当前类本身。

实例方法动态解析(resolveInstanceMethod)

+ (BOOL)resolveInstanceMethod:(SEL)aSelector {

    if (aSelector == @selector(greet)) {
        class_addMethod(self, aSelector, (IMP)greet1, "v@:");
    }
    
    return [super resolveClassMethod:aSelector];
}

这里和类方法的创建只是第一个参数有差别,其他一样

关于返回值我没发现区别,关键还在于是不是正确的添加了方法,如果添加了则不再继续后续转发步骤,否则不论返回YES还是NO都会继续后面的步骤。

备用接收者

如果 resolveInstanceMethod(或class)没有正确的添加方法,Runtime 会调用 forwardingTargetForSelector: 方法。在这个阶段,你可以返回一个对象,如果是类方法返回类。Runtime 将会尝试将消息转发给这个对象或类,如果这个对象或类能够响应消息,消息就被解析了。后面将不再单独区分类还是实例,方法名都一样,只是+方法和-方法的区别

- (id)forwardingTargetForSelector:(SEL)aSelector {
    
    if (aSelector == @selector(greet)) {
        return [[OtherClass alloc] init];
    }
    
    return [super forwardingTargetForSelector:aSelector]; // 确保继续转发
}

完整转发

如果没有设置备用接收者,将进入最后的完整转发阶段,Runtime 会调用 methodSignatureForSelector: 方法获取方法的签名,然后再调用 forwardInvocation: 方法来处理消息。在 forwardInvocation: 中,你可以自定义消息的处理逻辑,包括选择使用哪个对象来处理消息。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    // 将消息转发给 OtherClass
    [anInvocation invokeWithTarget:[[OtherClass alloc] init]]; 
}

关于signature的书写规则参考:

类型编码符号
voidv
id@
SEL:
inti
floatf
doubled
char **
BOOLB
struct{name=...}
pointer (void *)^v

未找到方法

如果最终消息无法解析,以上步骤都失败,Runtime 将会调用 doesNotRecognizeSelector: 方法,该方法默认会抛出异常,导致程序崩溃。

应用,消息机制是OC动态特性的一个体现,我们可以动态的处理函数调用,增加切入操作等。
 

方法交换

        方法交换多用于做一些监控或者跟踪,这个时候,本质上是把一些代码注入到了系统的关键函数中,如生命周期函数。举个例子,我想监控用户的页面浏览过程,那就可以给UIViewController的viewWillAppear做个注入(用新的viewWillAppear方法替换并包含原viewWillAppear的逻辑,所以往往是替换之后有调用了一下原方法),这样用户每打开一个页面都可以被监控到同时还保留了原viewWillAppear做的事情。

事例简单 不在赘述

类和实例的操作

我们可以通过运行时函数,

  • 创建类(objc_allocateClassPair)
  • 关联对象(objc_setAssociatedObject、objc_getAssociatedObject给类扩展属性)
  • 创建函数(class_getMethodImplementation)
  • 获取类的属性列表(class_copyPropertyList)
  • 函数列表(class_copyMethodList)

这些有啥用,属性扩展经常用,创建函数可以用于异常兜底,属性列表可以做成解析器(把json解析成具体的实例)

关类别和类的扩展,都可创建单独的文件

- 类别,就是有别名的扩展,创建类别时会生成h+m文件。既然有.m文件,那么就可以实现缺失的逻辑,就是说他可以对系统类(比如UIImage)进行扩展。如果增加函数,自己写实现;关联对象,实现增加属性的效果,自己实现其getter和setter。类别会增加类的能力,方便我们开发,但是如果扩展的是系统的类,推荐命名时做好隔离。扩展的过程,一旦出现重名,可能会带来很多不稳定结果,如果和系统函数重名,将会覆盖系统函数,如果和其他类别扩展的函数重名了,则会根据加载顺序,被后加载的文件覆盖,结果将会不稳定。

举例如下,其中括号里面的Help是类别的名称,示例中,我没有加前缀。

/// .h文件
@interface UIImage (Help)

/// name
@property (nonatomic, copy) NSString *imageName;

/// Get a image of target color
- (UIImage *)imageWithColor:(UIColor *)color;

@end

/// .m文件
@implementation UIViewController(Help)

- (UIImage *)imageWithColor:(UIColor *)color {
    // 具体实现...
    return image;
}

- (NSString *)imageName {
    return objc_getAssociatedObject(self, @selector(imageName));
}

- (void)setImageName:(NSString *)imageName {
    objc_setAssociatedObject(self, @selector(imageName), imageName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

@end

- 类的扩展,没有别名(匿名),创建类的扩展时你会发现,他只有一个h文件,所以不给你写实现的机会。没有实现,那就只能给你有源码(自定义类)进行扩展。那可能有人说:有源码,我扩展个鸟,直接去改就行了。类的扩展可以帮助我们在不同的场景下报漏不同的属性、函数,方便管理;也可以在日常开发中书写私有属性,不对外曝露。

代码示例如下

@interface MyClass ()

/// name
@property (nonatomic, copy) NSString *cName;

- (void)doSomething;

@end

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

51单片机(国信长天)矩阵键盘的基本操作

在CT107D单片机综合训练平台上,首先将J5处的跳帽接到1~2引脚,使按键S4~S19按键组成4X4的矩阵键盘。在扫描按键的过程中,发现有按键触发信号后(不做去抖动),待按键松开后,在数码管的第一位显示相应的数字:从左至右&…

如何在浏览器中搭建开源Web操作系统Puter的本地与远程环境

文章目录 前言1.关于Puter2.本地部署Puter3.Puter简单使用4. 安装内网穿透5.配置puter公网地址6. 配置固定公网地址 前言 嘿,小伙伴们!是不是每次开机都要像打地鼠一样不停地点击各种网盘和应用程序的登录按钮,感觉超级麻烦?更让…

【JavaScript】this 指向由入门到精通

this 的概念 this 在JavaScript 及其其他面向对象的编程语言中,存在的目的是为了提供一种在对象方法中引用当前对象的方式。 它为方法提供了对当前实例的引用,使得方法能够访问或者修改实例的成员变量。 注意点: this 的绑定和定位的位置…

javaEE-10.CSS入门

目录 一.什么是CSS ​编辑二.语法规则: 三.使用方式 1.行内样式: 2.内部样式: 3.外部样式: 空格规范 : 四.CSS选择器类型 1.标签选择器 2.类选择器 3.ID选择器 4.通配符选择器 5.复合选择器 五.常用的CSS样式 1.color:设置字体颜色 2.font-size:设置字体大小 3…

数据中台是什么?:架构演进、业务整合、方向演进

文章目录 1. 引言2. 数据中台的概念与沿革2.1 概念定义2.2 历史沿革 3. 数据中台的架构组成与关键技术要素解析3.1 架构组成3.2 关键技术要素 4. 数据中台与其他平台的对比详细解析 5. 综合案例:金融行业数据中台落地实践5.1 背景5.2 解决方案5.3 成果与价值 6. 方向…

Linux磁盘空间使用率100%(解决删除文件后还是显示100%)

本文适用于,删除过了对应的数据文件,查看还是显示使用率100%的情况 首先使用df -h命令查看各个扇区所占用的情况 一、先对系统盘下所有文件大小进行统计,是否真的是数据存储以达到了磁盘空间 在对应的扇区路径下使用du -sh * | sort -hr 命…

DeepSeek--教师备课效能100%

关键功能深度解析 深度思考(R1) 开启这个功能,就如同为 DeepSeek 赋予了深度思考的 “大脑”。当你向它咨询备课问题时,它会像经验丰富的教师一样,在 “脑海” 中梳理思路,不仅给出答案,还会展…

基于Java的自助多张图片合成拼接实战

目录 前言 一、图片合成需求描述 二、图片合成设计与实现 1、编程语言 2、基础数据准备 3、图片合成流程 4、图片合成实现 三、总结 前言 在当今数字化时代,图像处理技术在各个领域都发挥着至关重要的作用。从社交媒体到电子商务,从在线教育到虚拟…

大模型基本原理(四)——如何武装ChatGPT

传统的LLM存在几个短板:编造事实、计算不准确、数据过时等,为了应对这几个问题,可以借助一些外部工具或数据把AI武装起来。 实现这一思路的框架包括RAG、PAL、ReAct。 1、RAG(检索增强生成) LLM生成的内容会受到训练…

电控--PWM

理论知识 脉宽调制(Pulse Width Modulation,PWM) 对脉冲信号的宽度改变并输出出来高频的PWM波可以让设备进行频繁开关、通断 PWM波形的参数 周期(T):完整脉冲循环时间(单位:秒)频率(f)&…

CNN卷积神经网络多变量多步预测,光伏功率预测(Matlab完整源码和数据)

代码地址:CNN卷积神经网络多变量多步预测,光伏功率预测(Matlab完整源码和数据) 标题:CNN卷积神经网络多变量多步预测,光伏功率预测 一、引言 1.1 研究背景及意义 随着全球能源危机的加剧和环保意识的提升&#xff…

在clion中对linux的工程进行远程调试

本地主机:windows 远程主机:ubuntu 0. 建立一个用于同步远程工程代码的文件夹 在windows上新建了一个iot_frame_0210文件夹,用于远程调试,远程的代码会被下载到这个本地目录。 调试的时候,如果修改文件,则不会直接…

使用sunshine和moonlight串流时的音频输出问题

设备:电脑和平板串流,把平板当副屏使用 1.如果启用安装steam音频驱动程序,则平板有声,电脑无声,在moonlight端可以设置平板和电脑同时发声,但是有点卡 2.只想电脑发声,平板无声 禁用安装steam…

postgresql 游标(cursor)的使用

概述 PostgreSQL游标可以封装查询并对其中每一行记录进行单独处理。当我们想对大量结果集进行分批处理时可以使用游标,因为一次性处理可能造成内存溢出。 另外我们可以定义函数返回游标类型变量,这是函数返回大数据集的有效方式,函数调用者…

深入探索人工智能的未来:DeepSeek R1与蓝耘智算平台的完美结合

在当今数字化时代,人工智能(AI)和机器学习(ML)正以前所未有的速度改变着我们的生活和工作方式。从智能语音助手到自动驾驶汽车,从精准医疗到金融风险预测,AI的应用无处不在。深度学习作为AI的核…

树和二叉树_9

树和二叉树_9 一、leetcode-107二、题解1.引库2.代码 一、leetcode-107 二叉树的层序遍历Ⅱ 给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)。 样例输…

【安当产品应用案例100集】037-强化OpenVPN安全防线的卓越之选——安当ASP身份认证系统

在当前数字化时代,网络安全已成为企业发展的重要组成部分。对于使用OpenVPN的企业而言,确保远程访问的安全性尤为重要。安当ASP身份认证系统凭借其强大的功能和便捷的集成方式,为OpenVPN的二次登录认证提供了理想的解决方案,特别是…

Blazor-<select>

今天我们来说说<select>标签的用法&#xff0c;我们还是从一个示例代码开始 page "/demoPage" rendermode InteractiveAuto inject ILogger<InjectPage> logger; <h3>demoPage</h3> <select multiple>foreach (var item in list){<…

基于微信小程序的博物馆预约系统的设计与实现

hello hello~ &#xff0c;这里是 code袁~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生…

鸿蒙NEXT开发-发布三方库

开发一个三方库 如需发布一个 har 包&#xff0c;必须包含 oh-package.json5、README.md&#xff0c;CHANGELOG.md 和 LICENSE 四个文件&#xff0c;若文件缺失&#xff0c;会导致上架至中心仓失败。 HAR&#xff08;Harmony Archive&#xff09;是静态共享包&#xff0c;可以…