【iOS】KVO

文章目录

  • 前言
  • 一、KVO使用
    • 1.基本使用
    • 2.context使用
    • 3.移除KVO通知的必要性
    • 4.KVO观察可变数组
  • 二、代码调试探索
    • 1.KVO对属性观察
    • 2.中间类
    • 3.中间类的方法
    • 3.dealloc中移除观察者后,isa指向是谁,以及中间类是否会销毁?
    • 总结
  • 三、KVO本质
    • GNUStep窥探KVO源码
      • 重写setter方法
      • 重写class方法
      • 重写delloc方法
      • 重写KVC方法
      • 成员变量使用KVC触发KVO
  • 总结


前言

KVO的全称 Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变

KVO是一种机制,它允许将其他对象的指定属性的更改通知给对象

在iOS官方文档中有这么一句话:
理解KVO之前,必须先理解KVC(即KVO是基于KVC基础之上)

In order to understand key-value observing, you must first understand key-value coding.
KVC是键值编码,在对象创建完成后,可以动态的给对象属性赋值,而KVO是键值观察,提供了一种监听机制,当指定的对象的属性被修改后,则对象会收到通知,所以可以看出KVO是基于KVC的基础上对属性动态变化的监听

我们知道NSNotificatioCenter也是一种监听方式,那么KVONSNotificatioCenter有什么区别呢?

  • 相同点:
    1、两者的实现原理都是观察者模式,都是用于监听

2、都能实现一对多的操作

  • 不同点:
    1、KVO监听对象属性的变化,同时只能通过NSString来查找属性名,较容易出错

2、NSNotification的发送监听(post)的操作我们可以控制,kvo由系统控制。

3、KVO可以记录新旧值变化

一、KVO使用

1.基本使用

KVO的基本使用分为三步

  • 注册观察addObserver:forKeyPath:options:context
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];
  • 实现KVO回调observeValueForKeyPath:ofObject:change:context
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{if ([keyPath isEqualToString:@"name"]) {NSLog(@"%@",change);}
}
  • 移除观察者removeObserver:forKeyPath:context
[self.person removeObserver:self forKeyPath:@"nick" context:NULL];

2.context使用

我们注意到这些方法中都有参数context,我们来讲解一下

context 参数的主要作用是为 KVO 回调提供一个标识符或标记,这有助于区分同一属性上的不同观察者或在多个地方注册的同一个观察者

在官方文档中,针对参数context有如下说明:
在这里插入图片描述
通俗的讲,context上下文主要是用于区分不同对象的同名属性,从而在KVO回调方法中避免使用字符串进行区分,而是直接使用context进行区分,可以大大提升性能,以及代码的可读性

因此我们可以知道,context常用于标识,从而区分
不同对象的同名属性

context使用总结

  • 不使用context,使用keyPath区分通知来源
//context的类型是 nullable void *,应该是NULL,而不是nil
[self.person addObserver:self forKeyPath:@"nick" options:NSKeyValueObservingOptionNew context:NULL];
  • 使用context区分通知来源
//定义context
static void *PersonNickContext = &PersonNickContext;
static void *PersonNameContext = &PersonNameContext;//注册观察者
[self.person addObserver:self forKeyPath:@"nick" options:NSKeyValueObservingOptionNew context:PersonNickContext];
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:PersonNameContext];//KVO回调
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{if (context == PersonNickContext) {NSLog(@"%@",change);}else if (context == PersonNameContext){NSLog(@"%@",change);}
}

3.移除KVO通知的必要性

首先我们需要理解一下观察者与被观察者,例如下面这段代码:

[self.person addObserver:selfforKeyPath:@"name"options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOldcontext:nil];

观察者将观察 Person 类的 name 属性的变化。在这个例子中,我们将使用 ViewController 作为观察者

在官方文档中,针对KVO的移除有以下几点说明
在这里插入图片描述

删除观察者时,请记住以下几点:

  • 要求被移除为观察者(如果尚未注册为观察者)会导致NSRangeException。您可以对removeObserver:forKeyPath:context:进行一次调用,以对应对addObserver:forKeyPath:options:context:的调用,或者,如果在您的应用中不可行,则将removeObserver:forKeyPath:context:调用在try / catch块内处理潜在的异常。

  • 释放后,观察者不会自动将其自身移除。被观察对象继续发送通知,而忽略了观察者的状态。但是,与发送到已释放对象的任何其他消息一样,更改通知会触发内存访问异常。因此,您可以确保观察者在从内存中消失之前将自己删除。

  • 该协议无法询问对象是观察者还是被观察者。构造代码以避免发布相关的错误。一种典型的模式是在观察者初始化期间(例如,在init或viewDidLoad中)注册为观察者,并在释放过程中(通常在dealloc中)注销,以确保成对和有序地添加和删除消息,并确保观察者在注册之前被取消注册,从内存中释放出来。

KVO注册观察者 和移除观察者是需要成对出现的,如果只注册,不移除,会出现类似野指针的崩溃,如下图所示
在这里插入图片描述

崩溃的原因是,由于第一次注册KVO观察者后没有移除,再次进入界面,会导致第二次注册KVO观察者,导致KVO观察的重复注册,而且第一次的通知对象还在内存中,没有进行释放,此时接收到属性值变化的通知,会出现找不到原有的通知对象,只能找到现有的通知对象,即第二次KVO注册的观察者,所以导致了类似野指针的崩溃,即一直保持着一个野通知,且一直在监听

其实简单来讲就是可能当我们推出视图控制器时,视图控制器已经被销毁,同时我们的观察者是视图控制器,但是我们的视图控制器仍然是观察者,并没有被移除,因此当我们后续继续通过被观察者通知观察者时,就会出现观察者时已经被销毁的视图控制器,从而出现访问野指针的情况导致崩溃

4.KVO观察可变数组

KVO是基于KVC基础之上的,所以可变数组如果直接添加数据,是不会调用setter方法的,所有对可变数组的KVO观察下面这种方式不生效的,即直接通过[self.person.dateArray addObject:@“1”];向数组添加元素,是不会触发kvo通知回调的

在KVC官方文档中,针对可变数组的集合类型,有如下说明,即访问集合对象需要需要通过mutableArrayValueForKey方法,这样才能将元素添加到可变数组

    [_t addObserver:self forKeyPath:@"array" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];[_t.array addObject:@1];

这样不会出发通知,即使数组元素改变

我们应该使用mutableArrayValueForKey方法

    [_t addObserver:self forKeyPath:@"array" options:NSKeyValueObservingOptionNew | [[self.t mutableArrayValueForKey:@"array"] addObject:@"1"];

二、代码调试探索

1.KVO对属性观察

现在有一个属性与成员变量,分别注册KVO并且直接修改他们的值
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
发现只有age属性发生了变化
在这里插入图片描述

结论:
KVO只观察属性,不直接观察成员变量,这是因为setter方法的原因,但是使用KVC修改成员变量可以触发KVO

KVO 通常只能观察通过属性的 setter 方法修改的属性。这是因为当您为某个属性添加观察者时,Objective-C
运行时会动态创建该属性的一个特殊子类,并在这个子类中重写 setter 方法来插入属性变化通知的代码。由于直接修改成员变量不会触发
setter 方法
,因此不会产生 KVO 通知。

2.中间类

我们刚才提到了在运行时会创建一个中间类,接下来我们讲解一下这个中间类

根据官方文档所述,在注册KVO观察者后,观察对象的isa指针指向会发生改变

在注册观察者前后,对象的isa指针发生了变化
在这里插入图片描述

综上所述,在注册观察者后,实例对象的isa指针指向由kunkun类变为了NSKVONotifying_kunkun中间类,即实例对象的isa指针指向发生了变化
在这里插入图片描述

3.中间类的方法

既然生成了一个中间类,那么我们来查看一下这个中间类中有什么方法

#pragma mark - 遍历方法-ivar-property
- (void)printClassAllMethod:(Class)cls{unsigned int count = 0;Method *methodList = class_copyMethodList(cls, &count);for (int i = 0; i<count; i++) {Method method = methodList[i];SEL sel = method_getName(method);IMP imp = class_getMethodImplementation(cls, sel);NSLog(@"%@-%p",NSStringFromSelector(sel),imp);}free(methodList);
}//********调用********
[self printClassAllMethod:objc_getClass("NSKVONotifying_kunkun")];

输出:
在这里插入图片描述

那么我们的父类也有一个setAge方法,那么这里的这个方法是继承还是重写呢?
我们接下来打印父类的方法列表看一下
在这里插入图片描述
从这里说明继承的方法不会在子类中显示,所以NSKVONotifying_kunkun重写了set方法

综上所述,有如下结论:

  • NSKVONotifying_kunkun中间类重写了父类kunkunsetAge方法
  • NSKVONotifying_kunkun中间类重写了基类NSObjectclass 、 dealloc 、 _isKVOA方法
    其中dealloc是释放方法
    _isKVOA判断当前是否是kvo

我们这里再来设计一个函数来验证中间类与类的关系

创建一个函数来遍历所有已注册的类,并检查它们是否是指定类的子类。

void PrintSubclassesOfClass(Class parentClass) {int numClasses = objc_getClassList(NULL, 0);Class *classes = NULL;classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses);numClasses = objc_getClassList(classes, numClasses);for (int i = 0; i < numClasses; i++) {Class cls = classes[i];Class superClass = class_getSuperclass(cls);while (superClass) {if (superClass == parentClass) {NSLog(@"%@ is a subclass of %@", NSStringFromClass(cls), NSStringFromClass(parentClass));break;}superClass = class_getSuperclass(superClass);}}free(classes);
}// 调用PrintSubclassesOfClass([_t class]);

在这里插入图片描述

由此发现中间类是类的子类,用到了isa swizzling技术

3.dealloc中移除观察者后,isa指向是谁,以及中间类是否会销毁?

    [_t addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];[_t removeObserver:self forKeyPath:@"age"];

这两段代码执行后分别打印其isa指向
在这里插入图片描述
由此可见移除观察者后isa又变回了原来的指向

同时我们再次调用子类查找函数

    [_t addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];[_t removeObserver:self forKeyPath:@"age"];PrintSubclassesOfClass([_t class]);

输出:
在这里插入图片描述
说明中间类仍然存在没有被销毁

这里可能是考虑到重用的技术,后面再次注册观察者就不用重复生成中间类

总结

综上所述,关于中间类,有如下说明:

  • 实例对象isa的指向在注册KVO观察者之后,由原有类更改为指向中间类
  • 中间类重写了观察属性的setter方法、class、dealloc、_isKVOA方法
  • dealloc方法中,移除KVO观察者之后,实例对象isa指向由中间类更改为原有类
  • 中间类从创建后,就一直存在内存中,不会被销毁

由此我们可以得到如下关系图
在这里插入图片描述

三、KVO本质

在前面铺垫了那么多,我们现在来讲讲KVO的实现流程

KVO的本质是改变setter方法的调用

首先我们知道了中间类重写了setter方法,我们来打印一下重写后的方法的IMP,也就是方法实际上会调用哪一个函数
在这里插入图片描述
当修改instance对象的属性时,会调用Foundation_NSSetXXXValueAndNotify函数

Foundation框架中还有很多例如_NSSetBoolValueAndNotify、_NSSetCharValueAndNotify、_NSSetFloatValueAndNotify、_NSSetLongValueAndNotify等等函数。
在这里插入图片描述

GNUStep窥探KVO源码

由于KVO的实现没有开源,因此我们无法查看KVO的源码

GNUStep是一个成熟的框架,适用于高级GUI桌面应用程序和服务器应用程序,它将Cocoa Objective-C软件库,以自由软件方式重新实现,能够运行在Linux和windows操作系统上。

GNUStepFoundation与apple的API相同,虽然具体实现可能不一样,但仍旧有借鉴意义。

重写setter方法

GNUStep有一个模板类叫做GSKVOSetter,针对不同的数据类型,都有一个不同的setter方法实现,列举其中一个方法:

- (void) setterChar: (unsigned char)val
{NSString  *key; // 定义一个用来存储属性名称的字符串Class     c = [self class]; // 获取当前对象的类// 定义一个函数指针,用来存储原始的 setter 方法的实现void      (*imp)(id,SEL,unsigned char);// 通过类和当前方法的选择器(_cmd),获取这个方法的原始实现,并转换为适当的函数指针类型imp = (void (*)(id,SEL,unsigned char))[c instanceMethodForSelector: _cmd];// 通过 _cmd 选择器获取与之关联的属性名,通常通过移除 set 前缀和小写化首字母实现key = newKey(_cmd); // 这个 newKey 函数的实现没有给出,假设它能从 setter 名生成属性名// 检查这个类是否为 key 提供自动 KVO 通知// 这个检查是由 automaticallyNotifiesObserversForKey: 方法进行,该方法默认返回 YESif ([c automaticallyNotifiesObserversForKey: key] == YES) // 通常总是返回 YES,除非在子类中被重写{[self willChangeValueForKey: key]; // 在改变值之前手动通知 KVO 系统属性即将变更(*imp)(self, _cmd, val); // 调用原始的 setter 方法实现来更新属性值[self didChangeValueForKey: key]; // 在改变值之后手动通知 KVO 系统属性已经变更}else{// 如果类表示不自动通知,则直接调用原始实现,不发送 KVO 通知(*imp)(self, _cmd, val);}RELEASE(key); // 释放之前为 key 分配的内存(这个假设 key 是动态分配的,但代码中没有显示这部分)
}

由此我们可以知道重写后的setter方法的主要步骤

      [self willChangeValueForKey: key]; // 在改变值之前手动通知 KVO 系统属性即将变更(*imp)(self, _cmd, val); // 调用原始的 setter 方法实现来更新属性值[self didChangeValueForKey: key]; // 在改变值之后手动通知 KVO 系统属性已经变更
  • 先调用willChangeValueForKey方法,
  • 再调用父类原来的setter方法
  • 最后调用didChangeValueForKey,其内部会触发监听器(Oberser)的监听方法(observeValueForKeyPath:ofObject:change:context:);

我们用代码来验证一下调用顺序

- (void)setAge:(int)age {_age = age; // 直接赋值操作,确保使用下划线来访问实例变量,避免递归调用setterNSLog(@"调用成功:已将 age 设置为 %d", _age); // 打印信息
}
- (void)willChangeValueForKey:(NSString *)key {NSLog(@"willChangeValueForKey--begin");[super willChangeValueForKey:key];NSLog(@"willChangeValueForKey--end");
}- (void)didChangeValueForKey:(NSString *)key {NSLog(@"didChangeValueForKey--begin");[super didChangeValueForKey:key];NSLog(@"didChangeValueForKey--end");
}

在这里插入图片描述
符合我们上面所说的流程,同时在didChangeValueForKey方法中我们调用了observeValueForKeyPath:ofObject:change:context:,由此我们可以推测一下observeValueForKeyPath:ofObject:change:context:的实现代码
在这里插入图片描述

重写class方法

由于我们不想中间类暴露给用户,因此我们的程序同时重写了中间类的class方法

- (Class) class
{return class_getSuperclass(object_getClass(self));
}

由此我们class方法返回的就是原来的实例对象所属的类,而非中间类

重写delloc方法

- (void) dealloc
{// Turn off KVO for self ... then call the real dealloc implementation.[self setObservationInfo: nil];object_setClass(self, [self class]);[self dealloc];GSNOSUPERDEALLOC;
}

- (void) dealloc对象释放后,移除KVO数据,将对象重新指向原始类

重写KVC方法

- (void) setValue: (id)anObject forKey: (NSString*)aKey这是KVC中的方法,但是在GNUStep中也重写了这个方法

- (void) setValue: (id)anObject forKey: (NSString*)aKey
{Class     c = [self class];void      (*imp)(id,SEL,id,id);imp = (void (*)(id,SEL,id,id))[c instanceMethodForSelector: _cmd];if ([[self class] automaticallyNotifiesObserversForKey: aKey]){[self willChangeValueForKey: aKey];imp(self,_cmd,anObject,aKey);[self didChangeValueForKey: aKey];}else{imp(self,_cmd,anObject,aKey);}
}

这与我们上面讲到的重写后的setter方法类似,实现在原始类KVC调用前后添加[self willChangeValueForKey: aKey][self didChangeValueForKey: aKey],而这两个方法是触发KVO通知的关键。
所以说KVO是基于KVC的,而KVC正是KVO触发的入口

成员变量使用KVC触发KVO

由此如果我们直接修改成员变量不会触发KVO,但是如果通过KVC修改成员变量就会触发KVO

在这里插入图片描述

[_t setValue:@5 forKey:@"height"];NSLog(@"@%d", _t->height);

在这里插入图片描述

总结

  • KVC是KVO的入口,网上许多人说成员变量无法被KVO观察,其实是可以的,只是需要调用KVC,但是面试时一般都会说KVO只能用来观察属性
  • KVO的实现主要就是通过isa swizzling技术交换isa指针,在运行时生成中间类,在中间类中重写setter方法从而通知触发KVO监听函数。
  • 重写后的setter方法调用顺序主要为willChangeValueForKey->setter方法->didChangeValueForKey
  • 同时移除观察者后中间类会一直存在等待重用
  • 参考博客
    iOS底层原理总结 - 探寻KVO本质
    KVO源码浅析
    iOS-底层原理 23:KVO 底层原理

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

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

相关文章

基于51单片机的遥控开关仿真

基于51单片机的遥控开关设计 &#xff08;仿真&#xff0b;程序&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 本课题研究的是一款遥控开关&#xff0c;采用51单片机进行发射电路与接收电路的设计&#xff0c;发射电路由单片机最小系统及四个按键构成&am…

经典笔试题:快速排序 计数排序

Problem: 912. 排序数组 思路 &#x1f468;‍&#x1f3eb; 三叶题解 &#x1f496; AC&#xff1a;计数排序 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( n ) O(n) O(n) class Solution {public int[] sortArray(int[] nums) {int max -50001, min 50001;for (…

【半个月我拿下了软考证】软件设计师高频考点--系统化教学-关系模式

&#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;软件设计师考点暴击 ⭐&#x1f170;️进入狂砍分⭐ ⭐软件设计师高频考点文档&#xff0c; ⭐软件设计师高频考点专栏 ⭐软件设计师高频考点⭐ &#x1f3b6;&#xff08;A) 考点1,关系模式 考点&#xff1a; 三个模式相…

【JVM基础篇】类加载器分类介绍

文章目录 类加载器什么是类加载器类加载器的作用是什么应用场景类加载器的分类启动类加载器用户扩展基础jar包 扩展类加载器和应用程序类加载器扩展类加载器通过扩展类加载器去加载用户jar包&#xff1a; 应用程序加载器 Arthas中类加载器相关功能 文章说明 类加载器 什么是类…

[C++核心编程-01]----C++内存四区详细解析

目录 前言 正文 01-内存区域简介 02-全局区 03-栈区 04-堆区 05-new操作符 总结 前言 当程序运行时&#xff0c;操作系统会为程序分配一块内存空间&#xff0c;这块内存空间被划分为不同的区域&#xff0c;每个区域有其独特的作用…

Unity图形图表XChart插件使用

最近做了一款数字孪生项目,其中涉及到了图形图表的应用,网上找了一下,找到了XChart插件,使用起来蛮方便的,不过还有待继续研究,很多细节性的知识点需要进行学习探索。以下是项目中的应用。 官方应用: ![](https://img-blog.csdnimg.cn/direct/ab9de8e84e7b4be4a50ea…

vs2019 cpp20 规范的线程头文件 <thread> 注释并探讨两个问题

&#xff08;1&#xff09;学习线程&#xff0c;与学习其它容器一样&#xff0c;要多读 STL 库的源码。很多知识就显然而然的明白了。也不用死记硬背一些结论。上面上传了一份注释了一下的 源码。主要是补充泛型推导与函数调用链。基于注释后的源码探讨几个知识点。 STL 库的多…

哈希(构造哈希函数)

哈希 哈希也可以叫散列 画一个哈希表 哈希冲突越多&#xff0c;哈希表效率越低。 闭散列开放定址法: 1.线性探测&#xff0c;依次往后去找下一个空位置。 2.二次探测&#xff0c;按2次方往后找空位置。 #pragma once #include<vector> #include<iostream> #i…

Linux重定向及缓冲区理解

重定向&#xff1a; 在上一期虚拟文件系统中讲到了每个进程在打开后&#xff0c;都会默认打开3个文件&#xff0c;如下&#xff1a; stdin 标准输入&#xff08;键盘&#xff09; 文件描述符&#xff1a;0 stdout 标准输出&#xff08;显示器&#xff09;文件描述符&a…

每日OJ题_贪心算法四⑤_力扣354. 俄罗斯套娃信封问题

目录 力扣354. 俄罗斯套娃信封问题 解析代码1_动态规划&#xff08;超时&#xff09; 解析代码2_重写排序贪心二分 力扣354. 俄罗斯套娃信封问题 354. 俄罗斯套娃信封问题 难度 困难 给你一个二维整数数组 envelopes &#xff0c;其中 envelopes[i] [wi, hi] &#xff0…

25计算机考研院校数据分析 | 中南大学

中南大学&#xff08;Central South University&#xff09;&#xff0c;位于湖南省长沙市&#xff0c;是中华人民共和国教育部直属的全国重点大学 &#xff0c;中央直管副部级建制&#xff0c;位列国家“双一流”、“985工程”、“211工程”&#xff0c;入选国家“2011计划”牵…

陪玩系统APP小程序H5音视频社交系统陪玩系统源码,陪玩app源码,陪玩源码搭建陪玩社交系统开发(现成,可定制)线下陪玩系统项目开发搭建

线下陪玩系统项目的设计 在需求分析完成后&#xff0c;接下来进行系统设计。系统设计主要包括以下几个部分&#xff1a; 1. 数据库设计&#xff1a;根据需求分析的结果&#xff0c;设计数据库结构&#xff0c;包括用户信息表、服务信息表、订单信息表等。 2. 界面设计&#…

阮怀俊参与五龙乡黄沙村村企联办“强村公司”

为走好海岛县高质量发展共同富裕特色之路&#xff0c;探索村级集体经济发展新路径、扶持新模式、运行新机制&#xff0c;嵊泗县五龙乡黄沙村股份经济合作社与杭州山舍乡建乡村产业发展有限责任公司联办成“强村公司”。 创始人阮怀俊表示&#xff0c;双方就融合乡域发展和文旅产…

MFC桌面应用中窗口的客户区与非客户区的

在MFC&#xff08;Microsoft Foundation Class&#xff09;中&#xff0c;窗口被分为客户区和非客户区。理解这两个概念对于设计和开发Windows应用程序至关重要。 客户区&#xff08;Client Area&#xff09;&#xff1a; 客户区是窗口中用于显示应用程序内容的区域。它是窗口…

PXE高效批量装机

一、PXE的概述 PXE是由Inter 公司开发的网络引导技术&#xff0c;工作在Client / Server 模式。允许客户机通过网络从远程服务器下载引导镜像&#xff0c;并加载安装文件或者整个操作系统。 1.1PXE优点 规模化&#xff1a;同时装配多台服务器 自动化&#xff1a;安装系统&am…

力扣10.正则表达式匹配

前言&#xff1a; 由于今天面试前端&#xff0c;面试官问对正则表达式的匹配理解吗&#xff1f; 当时脑袋发热&#xff0c;我说就是对字符串的替换。。。。 太抽象了&#xff0c;于是我面试结束后马上打开力扣&#xff0c;解了正则表达式的匹配算法题(四种语言)&#xff1b; 下…

03c++重载运算符

1、深入理解new和delete原理 #include<iostream> using namespace std;/* new 和 delete 1、malloc和new的区别 new 内存开辟构造函数 2、free和 delete的区别 delete 内存回收析构函数 开辟失败malloc返nullptr ,new抛出bad_alloc异常new->operator new delete -&…

回归预测 | Matlab实现GA-LSSVM遗传算法优化最小二乘支持向量机多输入单输出回归预测

回归预测 | Matlab实现GA-LSSVM遗传算法优化最小二乘支持向量机多输入单输出回归预测 目录 回归预测 | Matlab实现GA-LSSVM遗传算法优化最小二乘支持向量机多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 Matlab实现GA-LSSVM遗传算法优化最小…

Linux进程控制——Linux进程终止

前言&#xff1a;前面了解完前面的Linux进程基础概念后&#xff0c;我们算是解决了Linux进程中的一大麻烦&#xff0c;现在我们准备更深入的了解Linux进程——Linux进程控制&#xff01; 我们主要介绍的Linux进程控制内容包括&#xff1a;进程终止&#xff0c;进程等待与替换&a…

车载测试:为什么你投十份简历,只有一两家公司约你?

最根本的原因&#xff0c;就是一方在汲汲渴求&#xff0c;而恰恰另一方呈现出的关键点让其怦然心动。求者心中有所想&#xff0c;而应者恰恰展现了求者所想的那一面。这就是个中奥妙。 程序员在找工作时&#xff0c;在一开始有三件事情会对能否获得面试机会至关重要&#xff1…