当前位置: 首页 > news >正文

【iOS】KVOKVC原理

1 KVO 键值监听

1.1 KVO简介

KVO的全称是Key-Value Observing,俗称"键值监听",可以用于监听摸个对象属性值得改变。

KVO一般通过以下三个步骤使用:

// 1. 添加监听
[self.student1 addObserver:self forKeyPath:@"age" options:options context:nil];// 2. 重写- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context方法- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {NSLog(@"%@的%@被改变:%@", object, keyPath, change);
}// 3. 适当时机移除监听
[self.student1 removeObserver:self forKeyPath:@"age"];

1.2 KVO简单使用

  1. 建立SXStudent类和SXTeacher
//SXStudent.h #import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface SXStudent : NSObject
@property (nonatomic, assign) NSInteger age;
@endNS_ASSUME_NONNULL_END// SXTeacher.h
#import <Foundation/Foundation.h>
#import "SXStudent.h"NS_ASSUME_NONNULL_BEGIN@interface SXTeacher : NSObject
@property (nonatomic, strong) SXStudent *student1;
@property (nonatomic, strong) SXStudent *student2;
- (void)demo;
@endNS_ASSUME_NONNULL_END
  1. 实现SXStudent类。
// SXStudent.m#import "SXStudent.h"
@implementation SXStudent
@end
  1. 实现SXTeacher类,重写init方法,为SXTeacherstudent1属性添加监听。实现demo方法,分别更改student1student2age值。
// SXTeacher.m#import "SXTeacher.h"
#import <objc/runtime.h>@implementation SXTeacher- (id)init {if (self = [super init]) {self.student1 = [[SXStudent alloc] init];self.student2 = [[SXStudent alloc] init];self.student1.age = 1;self.student2.age = 2;// 添加监听NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;[self.student1 addObserver:self forKeyPath:@"age" options:options context:nil];}return self;
}- (void)demo {self.student1.age = 20;self.student2.age = 30;
}- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {NSLog(@"%@的%@被改变:%@", object, keyPath, change);
}- (void)dealloc {// 移除监听[self.student1 removeObserver:self forKeyPath:@"age"];
}@end
  1. mian函数内创建SXTeacher的实例对象并调用demo方法测试。
#import <Foundation/Foundation.h>
#import "SXTeacher.h"int main(int argc, const char * argv[]) {@autoreleasepool {SXTeacher *teacher = [[SXTeacher alloc] init];[teacher demo];}return 0;
}
  1. 查看运行结果。
    在这里插入图片描述

1.3 实现原理探究

1.3.1 student1发生的变化

为什么student1setter方法可以触发监听,添加监听的方法到底对student1做了什么?

  1. 我们在添加监听后打一个断点。
    在这里插入图片描述
  2. 试着利用lldb调试查看student1student2isa指针。
    在这里插入图片描述

我们发现student1isa指针的指向被更改成了NSKVONotifying_SXStudent(NSKVONotifying_为前缀,原类名为后缀)类。

1.3.2 NSKVONotifying_XXX类

  1. 关于NSKVONotifying_XXX
  • NSKVONotifying_XXX类是 Runtime动态创建的一个类,在程序运行的过程中产生的一个新的类。
  • NSKVONotifying_XXX类是原类的一个子类。
  • NSKVONotifying_XXX类存在自己的 setAge:classdeallocisKVOA…方法。

试着验证NSKVONotifying_XXX类的方法和父类,我们可以使用如下代码打印NSKVONotifying_SXStudent类和SXStudent类的方法列表和父类类型。

- (void)demo2 {[self printMethods:object_getClass(self.student1)];[self printMethods:object_getClass(self.student2)];
}- (void) printMethods:(Class)cls {unsigned int count;Method *methods = class_copyMethodList(cls, &count);NSMutableString *methodNames = [NSMutableString string];[methodNames appendFormat:@"%@ - ", cls];NSLog(@"%@ superClass ----> %@", NSStringFromClass(cls), NSStringFromClass(class_getSuperclass(cls)));for (int i = 0; i < count; i++) {Method method = methods[i];NSString *methodName = NSStringFromSelector(method_getName(method));[methodNames appendFormat:@"%@ ", methodName];}NSLog(@"%@", methodNames);free(methods);
}

打印结果:
在这里插入图片描述

可以看到NSKVONotifying_SXStudent类有自己的setAge:classdealloc_isKVOA方法。

  • 重写class方法是为了隐藏NSKVONotifying_XXX类的存在。重写后的class方法返回其父类(原来的类)类型,使用户以为类没有变化。
  • _isKVOA则是用来标识当前类是否是通过runtime动态生成的类对象,如果是,就返回YES,不是,则返回NO。
  • 当对像被销毁后,dealloc做一些收尾工作。

1.3.3 方法调用探究

由上面可分析出我们的student1isa指针指向的类对象是NSKVONotifying_SXStudent,并且NSKVONotifying_SXStudent中还带有setAge: 方法,所以student1setAge:方法走的应该是NSKVONotifying_SXStudent类中的setAge:方法。

我们试着使用下面的代码打印student1被监听前后的setAge:方法的地址,并使用lldb调试一探究竟。

- (id)init {if (self = [super init]) {self.student1 = [[SXStudent alloc] init];self.student2 = [[SXStudent alloc] init];self.student1.age = 1;self.student2.age = 2;NSLog(@"添加监听之前 - p1 = %p, p2 = %p", [self.student1 methodForSelector:@selector(setAge:)], [self.student2 methodForSelector:@selector(setAge:)]);// 添加监听NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;[self.student1 addObserver:self forKeyPath:@"age" options:options context:nil];NSLog(@"添加监听之后 - p1 = %p, p2 = %p", [self.student1 methodForSelector:@selector(setAge:)], [self.student2 methodForSelector:@selector(setAge:)]);}return self;
}

打断点使用lldb打印方法地址对应的方法名:
在这里插入图片描述

我们发现student1setAge:方法实际上是调用了Foundation框架的_NSSetLongLongValueAndNotify函数。这又是怎么回事,我们先来了解一下这个函数。

1.3.3 _NSSetXXXValueAndNotify

经过查阅资料我们可以了解到。
NSKVONotifyin_XXX中的setage:方法中其实调用了 Fundation框架中C语言函数 _NSsetXXXValueAndNotify_NSsetXXXValueAndNotify内部做的操作相当于,首先调用willChangeValueForKey 将要改变方法,之后调用原来的setage方法对成员变量赋值,最后调用didChangeValueForKey已经改变方法。didChangeValueForKey中会调用监听器的监听方法,最终来到监听者的observeValueForKeyPath方法中。

Foundation框架中会根据属性的类型,调用不同的方法。例如我们之前定义的NSInteger类型的age属性,那么我们看到Foundation框架中调用的_NSsetLongLongValueAndNotify函数。那么我们把age的属性类型变为double重新打印一遍。

在这里插入图片描述

我们发现调用的函数变为了_NSSetDoubleValueAndNotify,那么这说明Foundation框架中有许多此类型的函数,通过属性的不同类型调用不同的函数。

我们可以重写 SXStudent类的willChangeValueForKey方法和didChangeValueForKey方法来验证上述说法。

#import "SXStudent.h"@implementation SXStudent
- (void)setAge:(NSInteger)age {NSLog(@"setAge");_age = 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");
}
@end

打印结果:
在这里插入图片描述

可知:

  • _NSSetXXXValueAndNotify调用willChangeValueForKey:
  • _NSSetXXXValueAndNotify调用setter实现;
  • _NSSetXXXValueAndNotify调用didChangeValueForKey:
  • didChangeValueForKey内部会调用observer的observeValueForKeyPath:ofObject:change:context:方法。

1.3.4 伪代码

据上所述,可以写出NSKVONotifying_SXStudent类的伪代码:

///> NSKVONotifying_SXStudent.m 文件#import "NSKVONotifying_SXStudent.h"@implementation NSKVONotifying_SXStudent- (void)setAge:(int)age{_NSSetLongLongValueAndNotify();  ///> 文章末尾 知识点补充小结有此方法来源
}void _NSSetLongLongValueAndNotify(){[self willChangeValueForKey:@"age"];[super setAge:age];[self didChangeValueForKey:@"age"];
}- (void)didChangeValueForKey:(NSString *)key{///> 通知监听器 key发生了改变[observe observeValueForKeyPath:key ofObject:self change:nil context:nil];
}@end

2 KVC 键值编码

2.1 KVC简介

KVC的全称key - value - coding,俗称"键值编码",可以通过key来访问某个属性。

常见的API有:

- (void)setValue:(id)value forKey:(NSString *)key;
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;- (id)valueForKey:(NSString *)key 
- (id)valueForKeyPath:(NSString *)keyPath;

2.2 KVC简单使用

2.2.1 自定义SXDog类、SXStudent类和SXTeacher类。

// SXDog.h
#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface SXDog : NSObject
@property (nonatomic, assign) CGFloat weight;
@endNS_ASSUME_NONNULL_END// SXStudent.h
#import <Foundation/Foundation.h>
#import "SXDog.h"NS_ASSUME_NONNULL_BEGIN@interface SXStudent : NSObject
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) SXDog *dog;
@endNS_ASSUME_NONNULL_END// SXTeacher.h
#import <Foundation/Foundation.h>
#import "SXStudent.h"NS_ASSUME_NONNULL_BEGIN@interface SXTeacher : NSObject
@property (nonatomic, strong) SXStudent *student1;
@endNS_ASSUME_NONNULL_END

2.2.2 实现这三个类,为了方便使用,重写SXStudent和SXTeacher的初始化方法,在初始化方法里对属性进行初始化。

SXDog.m

#import "SXDog.h"@implementation SXDog
@end

SXStudent.m

#import "SXStudent.h"
#import <objc/runtime.h>@implementation SXStudent
- (id)init {if (self = [super init]) {self.dog = [[SXDog alloc] init];}return self;
}
@end

SXTeacher.m

#import "SXTeacher.h"
#import <objc/runtime.h>@implementation SXTeacher
- (id)init {if (self = [super init]) {self.student1 = [[SXStudent alloc] init];}return self;
}
@end

2.2.3 为SXTeacher添加两个方法,在这两个方法里试调用KVC。

  1. SetValue:ForKey:ValueForKey:方法
- (void)demoSetValueForKeyAndValueForKey {[self.student1 setValue:@20 forKey:@"age"];NSLog(@"点语法:%ld", self.student1.age);NSNumber *value = [self.student1 valueForKey:@"age"];NSLog(@"KVC:%@", value);
}
  1. SetValue:ForKeyPath:ValueForKeyPath:
- (void)demoSetValueForKeyPathAndValueForKeyPath {[self.student1 setValue:@16 forKeyPath:@"dog.weight"];NSLog(@"点语法:%lf", self.student1.dog.weight);NSNumber *value = [self.student1 valueForKeyPath:@"dog.weight"];NSLog(@"KVC:%@", value);
}
  1. 调用上面两个函数,运行。
    在这里插入图片描述

2.2.4 KeyPath 和 Key 的区别:

  • keyPath 相当于根据路径去寻找属性,一层一层往下找。
  • key 是直接访问属性的名字,如果按路径找会报错。

2.3 KVC流程

2.3.1 setValue:forkey:赋值流程

在这里插入图片描述

  • 首先会按照setKey:、_setKey:的顺序到对象的方法列表中寻找这两个方法,如果找到了方法,则传参并且调用方法。
  • 如果没有找到方法,则通过accessInstanceVariablesDirectly方法的返回值来决定是否能够查找成员变量。如果accessInstanceVariablesDirectly返回YES,则会按照以下顺序到成员变量列表中查找对应的成员变量:
    • _key
    • _isKey
    • key
    • isKey
  • 如果accessInstanceVariablesDirectly返回NO,则直接抛出NSUnknownKeyException异常。
    如果在成员变量列表中找到对应的属性值,则直接进行赋值,如果找不到,则会抛出NSUnknownKeyException异常。

accessInstanceVariablesDirectly函数

+ (BOOL)accessInstanceVariablesDirectly{return YES;   ///> 可以直接访问成员变量//    return NO;  ///>  不可以直接访问成员变量,  ///> 直接访问会报NSUnkonwKeyException错误  
}

2.3.2 valueForKey:取值流程

在这里插入图片描述

  • 首先会按照以下顺序查找方法列表:
    • getKey
    • key
    • isKey
    • _key
  • 如果找到就直接传递参数,调用方法,如果未找到则查看accessInstanceVariablesDirectly方法的返回值,如果返回NO,则直接抛出NSUnknownKeyException异常。
  • 如果accessInstanceVariablesDirectly方法返回YES,则按如下顺序查找成员变量列表:
    • _key
    • _isKey
    • key
    • isKey
  • 如果能找到对应的成员变量,则直接获取成员变量的值,如果未找到,则抛出NSUnknownKeyException异常。

2.3.3 试验证setValue:forkey:赋值流程

对上述例子进行小修改:
SXStudent.h

@interface SXStudent : NSObject {@publicint _age;int _isAge;int age;int isAge;
}
@end

SXTeacher.m

- (id)init {if (self = [super init]) {self.student1 = [[SXStudent alloc] init];NSKeyValueObservingOptions option = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;[self.student1 addObserver:self forKeyPath:@"age" options:option context:nil];[self.student1 setValue:@20 forKey:@"age"];NSLog(@"-----");}return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {NSLog(@"%@的%@被改变:%@", object, keyPath, change);
}

NSLog(@"-----");处打下断点,运行,查看student1中的成员变量。看看谁被赋值了。
在这里插入图片描述

可以看到_age首先被赋值,我们注释掉SXStudent中的_age成员变量,看看下一个是谁被赋值。如此反复,就可以得到setValue:forkey:赋值流程。结果与上述无误,我就不继续了。

通过本例,我们还可以知道KVC也可以触发KVO监听。

3 一些问题

3.1 iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)

给一个实例对象添加KVO,系统内部是利用Runtime动态的生成一个此实例对象的类对象的子类,具体的格式为_NSKVONotifying_XXX,并且让实例对象的isa指针指向这个新生成的类。
重写属性的set方法,当调用set方法时,会调用Foundation框架的NSSetXXXValueAndNotify函数
_NSSetXXXValueAndNotify中会执行以下步骤:

  • 调用willChangeValueForKey:方法;
  • 调用父类的set方法,重新赋值;
  • 调用didChangeValueForKey:方法;
  • didChangeValueForKey:内部会触发监听器的observeValueForKeyPath:ofObject:change:context:方法。

3.2 如何手动触发KVO?

手动调用willChangeValueForKey:didChangeValueForKey:

例:

- (id)init {if (self = [super init]) {self.student1 = [[SXStudent alloc] init];NSKeyValueObservingOptions option = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;[self.student1 addObserver:self forKeyPath:@"age" options:option context:nil];[self.student1 willChangeValueForKey:@"age"];[self.student1 didChangeValueForKey:@"age"];}return self;
}

运行结果:

在这里插入图片描述

虽然是在didChangeValueForKey:内部会触发监听器的observeValueForKeyPath:ofObject:change:context:方法,但是如果不调用willChangeValueForKey:无法就无法触发监听器,这两个必须一起使用。

3.3 直接修改成员变量的值是否会触发KVO?

直接修改成员变量的值不会触发KVO,因为没有触发setter方法。

相关文章:

【iOS】KVOKVC原理

1 KVO 键值监听 1.1 KVO简介 KVO的全称是Key-Value Observing&#xff0c;俗称"键值监听"&#xff0c;可以用于监听摸个对象属性值得改变。 KVO一般通过以下三个步骤使用&#xff1a; // 1. 添加监听 [self.student1 addObserver:self forKeyPath:"age"…...

当机器人变硬核:探索深度学习中的时间序列预测

收藏自&#xff1a;Wed, 15 Sep 2021 10:32:56 UTC 摘要&#xff1a;时间序列预测是机器学习和深度学习领域的一个重要应用&#xff0c;它可以用于预测未来趋势、分析数据模式和做出决策。本文将介绍一些基本概念和常用方法&#xff0c;并结合具体的案例&#xff0c;展示如何使…...

C# Solidworks二次开发:自动创建虚拟零件及使用注意事项

今天要讲的是关于在solidworks中如何自动创建虚拟零件的功能&#xff0c;也就是solidworks中插入新零件这个功能。 实现这个功能需要使用的API如下所示&#xff1a; InsertNewVirtualPart&#xff08;swFaceOrPlane1, out swcomp2&#xff09;&#xff1b; 其中这个方法中使…...

vim工具 windows系统使用

vim常用命令&#xff1a; 编辑–>输入&#xff1a; i: 在当前光标所在字符的前面&#xff0c;转为输入模式&#xff1b; 粘贴命令 p p: 如果删除或复制为整行内容&#xff0c;则粘贴至光标所在行的下方&#xff0c;如果复制或删除的内容为非整行&#xff0c;则粘贴至光标所…...

Tesseract开源的OCR工具及python pytesseract安装使用

一 、介绍 Tesseract是一款由Google赞助的开源OCR。 pytesseract是python包装器&#xff0c;它为可执行文件提供了pythonic API。 Tesseract 已经有 30 年历史&#xff0c;开始它是惠普实验室的一款专利软件&#xff0c;在2005年后由Google接手并进一步开发和完善。Tesseract支…...

【数理知识】自由度 degree of freedom 及自由度的计算方法

放在最前的一句话&#xff1a;自由度是一个存在于两个学科中的概念&#xff0c;一个是存在于统计学中的自由度&#xff0c;另一个是存在于物理学中的自由度。而我本人需要的是研究物理学中的自由度概念&#xff0c;同时本笔记全篇也是在了解物理学中的自由度。 文章目录 自由度…...

苍穹外卖day09——历史订单模块(用户端)+订单管理模块(管理端)

查询历史订单——需求分析与设计 产品原型 业务规则 分页查询历史订单 可以根据订单状态查询 展示订单数据时&#xff0c;需要展示的数据包括&#xff1a;下单时间、订单状态、订单金额、订单明细&#xff08;商品名称、图片&#xff09; 接口设计 查询历史订单——代码开…...

正则表达式 —— Grep

文本处理三剑客&#xff1a;Grep、Sed、Awk 这三个工具都是基于对文本的内容进行增删改查的操作&#xff0c;此篇着重介绍grep与正则表达式的应用&#xff0c;以及扩展正则表达式。 正则表达式 什么是正则表达式&#xff1f; 它是由一类特殊字符以及文本字符所编写的一种模式…...

STC12C5A系列单片机片内看门狗的应用

wdt.c #include "wdt.h"void wdt_init(void) {WDT_CONTR 0x24; // 0010 0100 - 1.1377s }void wdt_feed(void) {WDT_CONTR | 0x10; // 喂狗 }wdt.h #ifndef _WDT_H_ #define _WDT_H_#include "stc12c5a60s2.h"// 函数声明 extern void wdt_init(void); …...

C语言指针详解

目录 指针是什么? 指针和指针类型 指针-整数 指针的解引用 野指针 野指针成因 如何规避野指针 指针运算 指针- 整数 指针-指针 指针的关系运算 指针和数组 二级指针 指针数组 指针数组 模拟二维数组 指针是什么? 指针理解的2个要点: 1. 指针是内存中一个…...

RTPS规范v2.5(中文版)

实时发布订阅协议 DDS互操作性有线协议 &#xff08;DDSI-RTPS&#xff09; 技术规范 V2.5 &#xff08;2022-04-01正式发布&#xff09; https://www.omg.org/spec/DDSI-RTPS/2.5/PDF   目 录 1 范围 8 2 一致性 8 3 规范性参考文献 8 4 术语和定义 9 5 标识 …...

LeetCode102.Binary-Tree-Level-Order-Traversal<二叉树的层序遍历>

题目&#xff1a; 思路&#xff1a; 写过N叉树的层序遍历&#xff0c;(8条消息) LeetCode429.N-Ary-Tree-Level-Order-Traversal&#xff1c;N 叉树的层序遍历&#xff1e;_Eminste的博客-CSDN博客 使用栈保存每一层的结点。然后每次当前层结束。将这一层的值添加进去res中。…...

yolov8系列[五]-项目实战-yolov8模型无人机检测

yolov8系列[五]-项目实战-yolov8模型无人机检测 项目介绍项目展示功能简介代码结构如何启动 开发者模式1. 安装依赖环境2. 启动程序 源代码下载其他 项目介绍 无人机识别项目,无人机搭载nvidia jetson边缘计算板子,进行实时识别。使用yolov8算法&#xff0c;训练了识别无人机的…...

Redis 笔记,基本数据类型、持久化、主从、集群等等问题

标题 &#x1f600;&#x1f600;&#x1f600;创作不易&#xff0c;各位看官点赞收藏. 文章目录 标题Redis 基础笔记1、安装及环境搭建2、Redis 数据类型2.1、String2.2、List2.3、Hash2.4、Set2.5、Zset2.6、BitMap2.7、HyperLogLog2.8、Geospatial2.9、Stream 3、Redis 持久…...

JDK,JRE,JVM三者的关系

JDK&#xff08;全称 Java Development Kit&#xff09;&#xff0c;Java开发工具包&#xff0c;能独立创建、编译、运行程序。 JDK JRE java开发工具&#xff08;javac.exe/java.exe/jar.exe) JRE&#xff08;全称 Java Runtime Environment&#xff09;&#xff0c;能运行…...

行为型-命令模式(Command Pattern)

说明 命令模式&#xff08;Command Pattern&#xff09;是一种行为设计模式&#xff0c;它将请求封装为一个对象&#xff0c;以便在不同的请求者和接收者之间进行解耦、参数化和操作的队列化。命令模式允许你将具体的请求封装为对象&#xff0c;这些对象之间彼此独立&#xff…...

总结942

5:40起床 6:00~7:00单词复习300个&#xff0c;记100个 7:15~8:00早读&#xff0c;《love is as strong as death》第一第二段 8:10~9:10三大计算回顾 9:15~10:06 习题880第一章基础选择纠错 10:10~10&#xff1a;30单词默写 10:30~11:40强化第一讲习题 11:40~12:30继续…...

MFC自定义控件使用

用VS2005新建一个MFC项目,添加一个Custom Control控件在窗体 我们需要为自定义控件添加一个类。项目,添加类,MFC类 设置类名字,基类为CWnd,你也可以选择CDialog作为基类 类创建完成后,在它的构造函数中注册一个新的自定义窗体,取名为"MyWindowClass" WNDCL…...

【学习笔记】「ROI 2018 Day 2」无进位加法

先放一个大佬的博客&#xff1a;「loj - 2850」「ROI 2018 Day 2」无进位加法 用数据结构来优化搜索&#x1f914; 神一样的 Kidulthood 考场上就已经意识到了这道题的正解是搜索&#x1f605; 考虑搜索过程的本质&#x1f914; 首先是找到最小的满足 t i i t_ii ti​i最大…...

分布式I/O,IT和OT融合少不了它

长期以来信息技术IT和操作运营技术OT是相互隔离的&#xff0c;随着大数据分析和边缘计算业务的对现场级实时数据的采集需求&#xff0c;IT和OT有了逐渐融合的趋势。IT与OT融合&#xff0c;它赋予工厂的管理者监控运行和过程的能力大为增强&#xff0c;甚至可以预测到可能发生的…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

深入浅出Diffusion模型:从原理到实践的全方位教程

I. 引言&#xff1a;生成式AI的黎明 – Diffusion模型是什么&#xff1f; 近年来&#xff0c;生成式人工智能&#xff08;Generative AI&#xff09;领域取得了爆炸性的进展&#xff0c;模型能够根据简单的文本提示创作出逼真的图像、连贯的文本&#xff0c;乃至更多令人惊叹的…...

高分辨率图像合成归一化流扩展

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 1 摘要 我们提出了STARFlow&#xff0c;一种基于归一化流的可扩展生成模型&#xff0c;它在高分辨率图像合成方面取得了强大的性能。STARFlow的主要构建块是Transformer自回归流&#xff08;TARFlow&am…...

跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践

在电商行业蓬勃发展的当下&#xff0c;多平台运营已成为众多商家的必然选择。然而&#xff0c;不同电商平台在商品数据接口方面存在差异&#xff0c;导致商家在跨平台运营时面临诸多挑战&#xff0c;如数据对接困难、运营效率低下、用户体验不一致等。跨平台商品数据接口的标准…...

C++11 constexpr和字面类型:从入门到精通

文章目录 引言一、constexpr的基本概念与使用1.1 constexpr的定义与作用1.2 constexpr变量1.3 constexpr函数1.4 constexpr在类构造函数中的应用1.5 constexpr的优势 二、字面类型的基本概念与使用2.1 字面类型的定义与作用2.2 字面类型的应用场景2.2.1 常量定义2.2.2 模板参数…...

深入理解 C++ 左值右值、std::move 与函数重载中的参数传递

在 C 编程中&#xff0c;左值和右值的概念以及std::move的使用&#xff0c;常常让开发者感到困惑。特别是在函数重载场景下&#xff0c;如何合理利用这些特性来优化代码性能、确保语义正确&#xff0c;更是一个值得深入探讨的话题。 在开始之前&#xff0c;先提出几个问题&…...