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

EOC第六章《块与中枢派发》

文章目录

    • 第37条:理解block这一概念
    • 第38条:为常用的块类型创建typedef
    • 第39条:用handler块降低代码分散程度
    • 第41条:多用派发队列,少用同步锁
      • 方案一:使用串行同步队列来将读写操作都安排到同一个队列里:
      • 方案二:将写操作放入栅栏快中,让他们单独执行;将读取操作并发执行。
    • 第42条:多用GCD,少用performSelector系列方法
    • 第43条:掌握GCD及操作队列的使用时机
    • 第44条:通过Dispath Group机制,根据系统资源状况来执行任务
    • 第45条:使用dispatch_once来执行只需运行一次的线程安全代码
    • 第46条:不要使用dispatch_get_current_queue

第37条:理解block这一概念

对于“块”的基础知识就不再赘述了,这里强调一下块的种类。

块(Block)分为三类:

  • 栈块
  • 堆块
  • 全局块
  1. 栈block

定义块的时候,其所占内存区域是分配在栈中的,而且只在定义它的那个范围内有效:

void (^block)();if ( /* some condition */ ) {block = ^{NSLog(@"Block A");};} else {block = ^{NSLog(@"Block B");};
}block();

上面定义的两个块只在if else语句范围内有效,一旦离开了最后一个右括号,如果编译器覆写了分配给块的内存,那么就会造成程序崩溃。

  1. 堆block

为了解决这个问题,我们可以给对象发送copy消息,复制一份到堆里,并自带引用计数:

void (^block)();if ( /* some condition */ ) {block = [^{NSLog(@"Block A");} copy];
} else {block = [^{NSLog(@"Block B");} copy];
}block();
  1. 全局block

全局块声明在全局内存里,而不需要在每次用到的时候于栈中创建。

void (^block)() = ^{NSLog(@"This is a block");
};

第38条:为常用的块类型创建typedef

如果我们需要重复创建某种块(相同参数,返回值)的变量,我们就可以通过typedef来给某一种块定义属于它自己的新类型

例如:

int (^variableName)(BOOL flag, int value) =^(BOOL flag, int value){// Implementationreturn someInt;
}

这个块有一个bool参数和一个int参数,并返回int类型。我们可以给它定义类型:

typedef int(^EOCSomeBlock)(BOOL flag, int value);

再次定义的时候,就可以通过简单的赋值来实现:

EOCSomeBlock block = ^(BOOL flag, int value){// Implementation
};

定义作为参数的块:

- (void)startWithCompletionHandler: (void(^)(NSData *data, NSError *error))completion;

这里的块有一个NSData参数,一个NSError参数并没有返回值

typedef void(^EOCCompletionHandler)(NSData *data, NSError *error);
- (void)startWithCompletionHandler:(EOCCompletionHandler)completion;

通过typedef定义块签名的好处是:如果要某种块增加参数,那么只修改定义签名的那行代码即可。

第39条:用handler块降低代码分散程度

下载网络数据时,如果使用代理方法,会使得代码分布不紧凑,而且如果有多个下载任务的话,还要在回调的代理中判断当前请求的类型。但是如果使用block的话,就可以让网络下载的代码和回调处理的代码写在一起,这样就可以同时解决上面的两个问题:

用代理下载:

- (void)fetchFooData {NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/foo.dat"];_fooFetcher = [[EOCNetworkFetcher alloc] initWithURL:url];_fooFetcher.delegate = self;[_fooFetcher start];}- (void)fetchBarData {NSURL *url = [[NSURL alloc] initWithString: @"http://www.example.com/bar.dat"];_barFetcher = [[EOCNetworkFetcher alloc] initWithURL:url];_barFetcher.delegate = self;[_barFetcher start];}- (void)networkFetcher:(EOCNetworkFetcher*)networkFetcher didFinishWithData:(NSData*)data
{   //判断下载器类型if (networkFetcher == _fooFetcher) {_fetchedFooData = data;_fooFetcher = nil;} else if (networkFetcher == _barFetcher) {_fetchedBarData = data;_barFetcher = nil;}
}

用block下载:

- (void)fetchFooData {NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/foo.dat"];EOCNetworkFetcher *fetcher =[[EOCNetworkFetcher alloc] initWithURL:url];[fetcher startWithCompletionHandler:^(NSData *data){_fetchedFooData = data;}];}- (void)fetchBarData {NSURL *url = [[NSURL alloc] initWithString: @"http://www.example.com/bar.dat"];EOCNetworkFetcher *fetcher =[[EOCNetworkFetcher alloc] initWithURL:url];[fetcher startWithCompletionHandler:^(NSData *data){_fetchedBarData = data;}];}

还可以将处理成功的代码放在一个块里,处理失败的代码放在另一个块中:

“#import <Foundation/Foundation.h>@class EOCNetworkFetcher;
typedef void(^EOCNetworkFetcherCompletionHandler)(NSData *data);
typedef void(^EOCNetworkFetcherErrorHandler)(NSError *error);@interface EOCNetworkFetcher : NSObject- (id)initWithURL:(NSURL*)url;
- (void)startWithCompletionHandler: (EOCNetworkFetcherCompletionHandler)completion failureHandler: (EOCNetworkFetcherErrorHandler)failure;@endEOCNetworkFetcher *fetcher =[[EOCNetworkFetcher alloc] initWithURL:url];
[fetcher startWithCompletionHander:^(NSData *data){// Handle success
}failureHandler:^(NSError *error){// Handle failure
}];

这样写的好处是,我们可以将处理成功和失败的代码分开来写,看上去更加清晰。
我们还可以将 成功和失败的代码都放在同一个块里:

“#import <Foundation/Foundation.h>@class EOCNetworkFetcher;
typedef void(^EOCNetworkFetcherCompletionHandler)(NSData *data, NSError *error);@interface EOCNetworkFetcher : NSObject- (id)initWithURL:(NSURL*)url;
- (void)startWithCompletionHandler:(EOCNetworkFetcherCompletionHandler)completion;@endEOCNetworkFetcher *fetcher =[[EOCNetworkFetcher alloc] initWithURL:url];[fetcher startWithCompletionHander:^(NSData *data, NSError *error){if (error) {// Handle failure} else {// Handle success}
}];

这样做的好处是,如果及时下载失败或中断了,我们仍然可以取到当前所下载的data。而且,如果在需求上指出:下载成功后得到的数据很少,也视为失败,那么单一块的写法就很适用,因为它可以取得数据后(成功)再判断其是否是下载成功的。
第40条:用块引用其所属对象时不要出现保留环

如果块捕获的对象直接或间接地保留了块本身,那么就需要小心保留环问题:

@implementation EOCClass {EOCNetworkFetcher *_networkFetcher;NSData *_fetchedData;}- (void)downloadData {NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/something.dat"];_networkFetcher =[[EOCNetworkFetcher alloc] initWithURL:url];[_networkFetcher startWithCompletionHandler:^(NSData *data){NSLog(@"Request URL %@ finished", _networkFetcher.url);_fetchedData = data;}];}

在这里出现了保留环:块要设置_fetchedData变量,就需要捕获self变量。而self(EOCClass实例)通过实例变量保留了获取器_networkFetcher,而_networkFetcher又保留了块。

解决方案是:在块中取得了data后,将_networkFetcher设为nil。


- (void)downloadData {NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/something.dat"];_networkFetcher =[[EOCNetworkFetcher alloc] initWithURL:url];[_networkFetcher startWithCompletionHandler:^(NSData *data){NSLog(@"Request URL %@ finished", _networkFetcher.url);_fetchedData = data;_networkFetcher = nil;}];}

第41条:多用派发队列,少用同步锁

多个线程执行同一份代码时,很可能会造成数据不同步。作者建议使用GCD来为代码加锁的方式解决这个问题。

方案一:使用串行同步队列来将读写操作都安排到同一个队列里:

_syncQueue = dispatch_queue_create("com.effectiveobjectivec.syncQueue", NULL);//读取字符串
- (NSString*)someString {__block NSString *localSomeString;dispatch_sync(_syncQueue, ^{localSomeString = _someString;});return localSomeString;}//设置字符串
- (void)setSomeString:(NSString*)someString {dispatch_sync(_syncQueue, ^{_someString = someString;});
}

这样一来,读写操作都在串行队列进行,就不容易出错。

但是,还有一种方法可以让性能更高:

方案二:将写操作放入栅栏快中,让他们单独执行;将读取操作并发执行。

_syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//读取字符串
- (NSString*)someString {__block NSString *localSomeString;dispatch_sync(_syncQueue, ^{localSomeString = _someString;});return localSomeString;
}
//设置字符串
- (void)setSomeString:(NSString*)someString {dispatch_barrier_async(_syncQueue, ^{_someString = someString;});}

显然,数据的正确性主要取决于写入操作,那么只要保证写入时,线程是安全的,那么即便读取操作是并发的,也可以保证数据是同步的。

这里的dispatch_barrier_async方法使得操作放在了同步队列里“有序进行”,保证了写入操作的任务是在串行队列里。

第42条:多用GCD,少用performSelector系列方法

在iOS开发中,有时会使用performSelector来执行某个方法,但是performSelector系列的方法能处理的选择子很局限:

它无法处理带有多个参数的选择子。
返回值只能是void或者对象类型。
但是如果将方法放在块中,通过GCD来操作就能很好地解决这些问题。尤其是我们如果想要让一个任务在另一个线程上执行,最好应该将任务放到块里,交给GCD来实现,而不是通过performSelector方法。

举几个 来比较这两种方案:

  1. 延后执行某个任务的方法:
// 使用 performSelector:withObject:afterDelay:
[self performSelector:@selector(doSomething) withObject:nil afterDelay:5.0];// 使用 dispatch_after
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^(void){[self doSomething];
});
  1. 将任务放在主线程执行:
// 使用 performSelectorOnMainThread:withObject:waitUntilDone:
[self performSelectorOnMainThread:@selector(doSomething) withObject:nil waitUntilDone:NO];// 使用 dispatch_async
// (or if waitUntilDone is YES, then dispatch_sync)
dispatch_async(dispatch_get_main_queue(), ^{[self doSomething];
});

注意:
如果waitUntilDone的参数是Yes,那么就对应GCD的dispatch_sync方法。
我们可以看到,使用GCD的方式可以将线程操作代码和方法调用代码写在同一处,一目了然;而且完全不受调用方法的选择子和方法参数个数的限制。

第43条:掌握GCD及操作队列的使用时机

除了GCD,操作队列(NSOperationQueue)也是解决多线程任务管理问题的一个方案。对于不同的环境,我们要采取不同的策略来解决问题:有时候使用GCD好些,有时则是使用操作队列更加合理。

使用NSOperation和NSOperationQueue的优点:

可以取消操作:在运行任务前,可以在NSOperation对象调用cancel方法,标明此任务不需要执行。但是GCD队列是无法取消的,因为它遵循“安排好之后就不管了(fire and forget)”的原则。
可以指定操作间的依赖关系:例如从服务器下载并处理文件的动作可以用操作来表示。而在处理其他文件之前必须先下载“清单文件”。而后续的下载工作,都要依赖于先下载的清单文件这一操作。
监控NSOperation对象的属性:可以通过KVO来监听NSOperation的属性:可以通过isCancelled属性来判断任务是否已取消;通过isFinished属性来判断任务是否已经完成。
可以指定操作的优先级:操作的优先级表示此操作与队列中其他操作之间的优先关系,我们可以指定它。

第44条:通过Dispath Group机制,根据系统资源状况来执行任务

有时需要等待多个并行任务结束的那一刻执行某个任务,这个时候就可以使用dispath group函数来实现这个需求:

通过dispath group函数,可以把并发执行的多个任务合为一组,于是调用者就可以知道这些任务何时才能全部执行完毕。

//一个优先级低的并发队列
dispatch_queue_t lowPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);//一个优先级高的并发队列
dispatch_queue_t highPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);//创建dispatch_group
dispatch_group_t dispatchGroup = dispatch_group_create();//将优先级低的队列放入dispatch_group
for (id object in lowPriorityObjects) {dispatch_group_async(dispatchGroup,lowPriorityQueue,^{ [object performTask]; });
}//将优先级高的队列放入dispatch_group
for (id object in highPriorityObjects) {dispatch_group_async(dispatchGroup,highPriorityQueue,^{ [object performTask]; });
}//dispatch_group里的任务都结束后调用块中的代码
dispatch_queue_t notifyQueue = dispatch_get_main_queue();
dispatch_group_notify(dispatchGroup,notifyQueue,^{// Continue processing after completing tasks
});

第45条:使用dispatch_once来执行只需运行一次的线程安全代码

有时我们可能只需要将某段代码执行一次,这时可以通过dispatch_once函数来解决。

dispatch_once函数比较重要的使用例子是单例模式:
我们在创建单例模式的实例时,可以使用dispatch_once函数来令初始化代码只执行一次,并且内部是线程安全的。

而且,对于执行一次的block来说,每次调用函数时传入的标记都必须完全相同,通常标记变量声明在static或global作用域里。

  • (id)sharedInstance {

    static EOCClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
     sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
    }
    我们可以这么理解:在dispatch_once块中的代码在程序启动到终止的过程里,只要运行了一次后,就给自己加上了注释符号,不再存在了。

第46条:不要使用dispatch_get_current_queue

我们无法用某个队列来描述“当前队列”这一属性,因为派发队列是按照层级来组织的。

那么什么是队列的层级呢?
在这里插入图片描述

安排在某条队列中的快,会在其上层队列中执行,而层级地位最高的那个队列总是全局并发队列。

在这里,B,C中的块会在A里执行。但是D中的块,可能与A里的块并行,因为A和D的目标队列是并发队列。

正因为有了这种层级关系,所以检查当前队列是并发的还是非并发的就不会总是很准确。

相关文章:

EOC第六章《块与中枢派发》

文章目录第37条&#xff1a;理解block这一概念第38条&#xff1a;为常用的块类型创建typedef第39条&#xff1a;用handler块降低代码分散程度第41条&#xff1a;多用派发队列&#xff0c;少用同步锁方案一&#xff1a;使用串行同步队列来将读写操作都安排到同一个队列里&#x…...

八、Git远程仓库操作——跨团队成员的协作

前言 前面一篇博文介绍了git团队成员之间的协作&#xff0c;现在在介绍下如果是跨团队成员的话&#xff0c;如何协作&#xff1f; 跨团队成员协作&#xff0c;其实就是你不属于那个项目的成员&#xff0c;你没有权限向那个仓库提交代码。但是github还有另一种 pull request&a…...

算法刷题打卡第88天:字母板上的路径

字母板上的路径 难度&#xff1a;中等 我们从一块字母板上的位置 (0, 0) 出发&#xff0c;该坐标对应的字符为 board[0][0]。 在本题里&#xff0c;字母板为board ["abcde", "fghij", "klmno", "pqrst", "uvwxy", "…...

UVa The Morning after Halloween 万圣节后的早晨 双向BFS

题目链接&#xff1a;The Morning after Halloween 题目描述&#xff1a; 给定一个二维矩阵&#xff0c;图中有障碍物和字母&#xff0c;你需要把小写字母移动到对应的大写字母位置&#xff0c;不同的小写字母可以同时移动&#xff08;上下左右四个方向或者保持不动 &#xff0…...

Connext DDS属性配置参考大全(3)

Transport传输dds.participant.logging.time_based_logging.process_received_messagedds.participant.logging.time_based_logging.process_received_message.timeout...

Docker-安装Jenkins-使用jenkins发版Java项目

文章目录0.前言环境背景1.操作流程1.1前期准备工作1.1.1环境变量的配置1.2使用流水线的方式进行发版1.2.1新建流水线任务1.2.2流水线操作工具tools步骤stages步骤1:拉取代码编译步骤2:发送文件并启动0.前言 学海无涯&#xff0c;旅“途”漫漫&#xff0c;“途”中小记&#xff…...

spring 中的 Bean 是否线程安全

文章目录结论1、spring中的Bean从哪里来&#xff1f;2、spring中什么样的Bean存在线程安全问题&#xff1f;3、如何处理spring Bean的线程安全问题&#xff1f;结论 其实&#xff0c;Spring 中的 Bean 是否线程安全&#xff0c;其实跟 Spring 容器本身无关。Spring框架中没有提…...

微电网两阶段鲁棒优化经济调度方法[3]【升级优化版本】(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑…...

C++入门教程||C++ 数据类型||C++ 变量类型

C 数据类型 使用编程语言进行编程时&#xff0c;需要用到各种变量来存储各种信息。变量保留的是它所存储的值的内存位置。这意味着&#xff0c;当您创建一个变量时&#xff0c;就会在内存中保留一些空间。 您可能需要存储各种数据类型&#xff08;比如字符型、宽字符型、整型…...

【visio使用技巧】图片导出pdf时去掉多余空白

问题 在visio导出pdf格式的图片时&#xff0c;往往会存在多余的白边&#xff0c;如下图所示&#xff1a; 解决方法 依次点击&#xff1a;菜单栏→文件→选项→自定义功能区→勾选“开发工具”→确定。 依次点击菜单栏→开发工具→显示ShapeSheet→页→Print Properties→将…...

Rust语言之Option枚举类型

概述 Option是Rust语言设计中最重要的枚举类型之一&#xff0c;它编码了其它语言中空值与非空值的概念&#xff0c;差异在于&#xff0c;Rust不会允许你像其它语言一样以非空值的方式来使用一个空值&#xff0c;这避免了很多错误。Option在标准库中的定义如下&#xff1a; pu…...

基于TimeQuest时序优化原理和方法

&#x1f4a1; 回顾基于RTL逻辑时序优化的基本思路&#xff0c;在关键路径中插入寄存器来优化时序 分析最坏路径 通过前面对TimeQuest软件的理解&#xff0c;基本上可以找到关键路径&#xff0c;此文章主要对关键路径时序进行优化&#xff0c;使设计达到时序要求&#xff0c;以…...

LeetCode第332场周赛

2023.2.12LeetCode第332场周赛 6354. 找出数组的串联值 思路 双指针模拟&#xff0c;两个指针相遇的时候要特判 算法 class Solution { public:long long findTheArrayConcVal(vector<int>& nums) {long long ans 0;int i 0, j nums.size() - 1;while (i <…...

2023-2-12刷题情况

字母板上的路径 题目描述 我们从一块字母板上的位置 (0, 0) 出发&#xff0c;该坐标对应的字符为 board[0][0]。 在本题里&#xff0c;字母板为board [“abcde”, “fghij”, “klmno”, “pqrst”, “uvwxy”, “z”]&#xff0c;如下所示。 我们可以按下面的指令规则行动…...

拉普拉斯矩阵

拉普拉斯算子 Δff(xi1,yj)f(xi−1,yj)f(xi,yj1)f(xi,yj−1)−4f(xi,yj)∑(k,l)∈N(i,j)(f(xk,yl)−f(xi,yj))\begin{aligned} \Delta f & f\left(x_{i1}, y_j\right) f\left(x_{i-1},y_j\right) f\left(x_i,y_{j1}\right)f\left(x_i,y_{j-1}\right) - 4f\left(x_i,y_j\r…...

Top-1错误率、Top-5错误率等常见的模型算法评估指标解析

Top-1 错误率&#xff1a;指预测输出的概率最高的类别与人工标注的类别相符的准确率&#xff0c;就是你预测的label取最后概率向量里面最大的那一个作为预测结果&#xff0c;如过你的预测结果中概率最大的那个分类正确&#xff0c;则预测正确&#xff0c;否则预测错误。比如预测…...

Urho3D 容器类型

Urho3D实现了自己的字符串类型和模板容器&#xff0c;而不是使用STL。其基本原理如下&#xff1a; 在某些情况下提高了性能&#xff0c;例如使用PODVector类时。保证字符串和容器的二进制大小&#xff0c;以允许例如嵌入Variant对象内。减少了编译时间。直接命名和实现&#x…...

C语言学习笔记(四): 循环结构程序设计

while语句 定义 While语句是C语言中的循环语句&#xff0c;它按条件循环执行语句&#xff0c;直到条件不满足为止 语法格式如下: while(condition) {//循环体内容; }使用实例 求123…100 include <stdio.h> int main(){int i 1, sum 0;while (i<100){sum i …...

02 OpenCV图像通道处理

1 通道提取与合并 在数字图像处理中&#xff0c;图像通道是指一个图像中的颜色信息被分离为不同的颜色分量。常见的图像通道包括RGB通道、灰度通道、HSV通道等。 RGB通道是指将图像分离为红色、绿色和蓝色三个颜色通道&#xff0c;每个通道表示相应颜色的亮度。这种方式是最常…...

微信小程序图书馆座位预约管理系统

开发工具&#xff1a;IDEA、微信小程序服务器&#xff1a;Tomcat9.0&#xff0c; jdk1.8项目构建&#xff1a;maven数据库&#xff1a;mysql5.7前端技术&#xff1a;vue、uniapp服务端技术&#xff1a;springbootmybatis本系统分微信小程序和管理后台两部分&#xff0c;项目采用…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

日常一水C

多态 言简意赅&#xff1a;就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过&#xff0c;当子类和父类的函数名相同时&#xff0c;会隐藏父类的同名函数转而调用子类的同名函数&#xff0c;如果要调用父类的同名函数&#xff0c;那么就需要对父类进行引用&#…...

OCR MLLM Evaluation

为什么需要评测体系&#xff1f;——背景与矛盾 ​​ 能干的事&#xff1a;​​ 看清楚发票、身份证上的字&#xff08;准确率>90%&#xff09;&#xff0c;速度飞快&#xff08;眨眼间完成&#xff09;。​​干不了的事&#xff1a;​​ 碰到复杂表格&#xff08;合并单元…...

Vue 3 + WebSocket 实战:公司通知实时推送功能详解

&#x1f4e2; Vue 3 WebSocket 实战&#xff1a;公司通知实时推送功能详解 &#x1f4cc; 收藏 点赞 关注&#xff0c;项目中要用到推送功能时就不怕找不到了&#xff01; 实时通知是企业系统中常见的功能&#xff0c;比如&#xff1a;管理员发布通知后&#xff0c;所有用户…...