iOS 初识RunLoop

news2025/5/19 16:05:48

iOS 初识RunLoop

文章目录

  • iOS 初识RunLoop
    • RunLoop的概念
    • RunLoop的功能
    • RunLoop和线程的关系
    • RunLoop的结构
      • Mode
      • Observer
      • Timer 和 source
        • 小结
    • RunLoop的核心
    • RunLoop的流程
    • RunLoop的应用
      • AutoreleasePool
      • 响应触控事件
      • 刷新界面
      • 常驻线程
      • 网络请求
      • NSTimer 和 CADisplayLink
        • NSTimer
        • GCDTimer
        • CADisplayLink

RunLoop的概念

一般来说,一个线程一次只能执行一个任务,执行完成后线程就退出,如果我们需要一个机制,让线程能随时处理事件而不退出.

可以将代码理解成下图:

do {
   //获取消息
   //处理消息
} while (消息 != 退出)

这种模型通常被称作Event Loop很多系统和框架都有实现在iOS和macOS中就被称作RunLoop.

简单来说RunLoop就是一种循环,通过这种循环得以让应用程序持续运行,让线程可以随时处理事件,并且让线程需要进行事件处理的时候忙起来,在不需要事件处理的时候闲下来.

  • RunLoop是通过内部维护的事件循环来实现对于事件/消息进行管理的一个对象
  • 没有消息处理时,休眠以避免资源占用,有消息需要处理的,立刻被唤醒

从代码上来看RunLoop是一个对象:

struct __CFRunLoop {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;  /* locked for accessing mode list */
    __CFPort _wakeUpPort;   // used for CFRunLoopWakeUp 内核向该端口发送消息可以唤醒runloop
    Boolean _unused;
    volatile _per_run_data *_perRunData; // reset for runs of the run loop
    pthread_t _pthread;             //RunLoop对应的线程
    uint32_t _winthread;
    CFMutableSetRef _commonModes;    //存储的是字符串,记录所有标记为common的mode
    CFMutableSetRef _commonModeItems;//存储所有commonMode的item(source、timer、observer)
    CFRunLoopModeRef _currentMode;   //当前运行的mode
    CFMutableSetRef _modes;          //存储的是CFRunLoopModeRef
    struct _block_item *_blocks_head;//doblocks的时候用到
    struct _block_item *_blocks_tail;
    CFTypeRef _counterpart;
};

下面这个图片是一个RunLoop结构的一个概览:

img

RunLoop的功能

  • 保持程序的持续运行
  • 处理app中的各种事件
  • 节省cpu资源,提高程序性能:在需要工作的时候才去工作,在不需要工作的时候就进入休眠状态,在休眠状态是不占用CPU的

RunLoop和线程的关系

RunLoop和线程是一一对应的,app启动之后,程序进入了主线程,apple帮我们在主线程启动了一个RunLoop.如果是我们开辟的子线程,那么需要我们手动开启RunLoop,而且如果你不主动去获取RunLoop的话,那么子线程的RunLoop是不会开启的,他是采用了一个懒加载的形式.

  • 注意:苹果不允许直接创建 RunLoop,只能通过 CFRunLoopGetMain()CFRunLoopGetCurrent() 去获取,其内部会创建一个 RunLoop 并返回给你(子线程),而它的销毁是在线程结束时。

img

RunLoop在线程中的作用主要就是从 input sourcetimer source这里面接受事件,然后在线程中处理事件

输入源 (input source) 异步传递的事件,通常是从其他线程,app发送的消息

计时器源 (Timer source) 同步传递事件,这些事件在计划的时间,间隔触发.

input source 传递异步事件到对应的一个处理程序,并且退出runUntilDate方法.Timer source传递事件到对应处理程序,但是不会退出runLoop.

runUntilDate:方法在指定时间到达前会保持RunLoop 处于运行状态,RunLoop运行时会处理 input source 的各种事件。

这里我们就可以方便理解下面这段代码的意思了

  1. 为什么main函数不会退出
int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

UIApplicationMain内部默认开启了主线程的RunLoop并执行了一段无限循环的代码,UIApplicationMain函数一直没有返回,还在不断的接受处理消息以及等待休眠,所以运行程序之后,会保持持续运行状态.

RunLoop的结构

在 CoreFoundation 里面关于 RunLoop 有5个类:

  • CFRunLoopRef
  • CFRunLoopModeRef
  • CFRunLoopSourceRef
  • CFRunLoopTimerRef
  • CFRunLoopObserverRef

他们之间的关系如下图:

RunLoop_0.png

一个RunLoop中有多个Mode,每一个mode有属于自己的Observer,Timer,Source.

Mode

Mode也就是模式,一个RunLoop当前只能处于一种Mode中,就好比当前只能是白天或者当前是夜晚.图中可以看到不同Mode之间是互不干扰的,处于各自的世界里面,AMode做了什么和BMode都是没有关系的,这也就是apple为什么会设置成两个状态来管理我们的滑动和默认状态.

apple给我提供了一下几种Mode:

  • kCFRunLoopDefaultModeapp默认的Mode,通常主线程是在这个Mode下运行的
  • UITrackingRunLoopMode界面追踪Mode,让UISrcollView处于滑动的时候就处于这个Mode
  • UIInitializationRunLoopMode:刚启动app就进入的第一个Mode.启动后就不在使用
  • GSEventReceiveRunLoopMode接受系统事件的内部Mode.通常情况下是用不到的
  • kCFRunLoopCommonModes:不会是一个真正意义上的一个Mode,但是如果你把事件丢到这里来,那么不管你处于什么mode,都会触发你想要执行的事件

当我们的程序运行起来的时候,我们就不用去动他了,它就处于一个kCFRunLoopDefaultMode的状态,当你滚动他力,他就会处于UITrackingRunLoopMode的一个状态,如果你想要在这两个状态都可以响应同一个事件的话,那么就要把事情都方法到kCFRunLoopCommonModes中去执行

大部分情况我们只用使用下面的几种

  • kCFRunLoopDefaultMode
  • UITrackingRunLoopMode
  • kCFRunLoopCommonModes

Observer

Observer,这个就相当于观察你上班的状态的人,这里用一个枚举来表示当前RunLoop的状态:

/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),           // 即将进入 Loop
    kCFRunLoopBeforeTimers = (1UL << 1),    // 即将处理 Timer
    kCFRunLoopBeforeSources = (1UL << 2),   // 即将处理 Source
    kCFRunLoopBeforeWaiting = (1UL << 5),   // 即将进入休眠
    kCFRunLoopAfterWaiting = (1UL << 6),    // 刚从休眠中唤醒
    kCFRunLoopExit = (1UL << 7),            // 即将退出 Loop
    kCFRunLoopAllActivities = 0x0FFFFFFFU   // 所有的状态
};

Timer 和 source

从结构的那张图可以看到,Mode 中有一个 Timer 的数组,一个 Mode 中可以有多个 Timer。Timer其实就是计时器,工作原理其实是确定一个计时器,然后给一个任务设置开始事件,和多久执行一次的一个Timer,把它注册到RunLoop中去,然后RunLoop就会根据我们注册的一个时间点来实现对应的内容:

注意,RunLoop中的Timer也不一定是准确的,因为RunLoop的执行事件是有一个顺序的,所以我们要处理玩一个事件才可以处理下一个事件,所以按照逻辑上来讲,尽管事件到了我们还是要把上一个事件执行玩才可以执行这个事件,也就是说我们尽管设置了一个是Timer,也要把上一个事件执行完才可以执行下一个任务,所以按道理来说Timer也不一定准确.

Source

Source 是另外一种 RunLoop 要干的活,看源码的话,Source 其实是 RunLoop 的数据源抽象类.

这里apple给我定义了两种Version的source:

  • source0 : 处理 App 内部事件,App 自己负责管理(触发),如 UIEventCFSocket
  • source1:由 RunLoop 内核管理,Mach port 驱动,如 CFMackPortCFMessagePort

Soucre1可以理解成一种线程之间通信的一种方式,在这ARunLoop中,我想让B线程传递东西给我们,那我们就要通过Port来进行一个传递,然后系统将传输的东西包装成 Source1,在线程 A 中监听 port 是否有东西传输过来,接收到后,唤醒 RunLoop 进行处理

小结

上面的 Source/Timer/Observer 被统称为 mode item,一个 item 可以被同时加入多个 mode。但一个 item 被重复加入同一个 mode 时是不会有效果的。如果一个 mode 中一个 item 都没有,则 RunLoop 会直接退出,不进入循环。(所以在下文中我们实现一个常住线程也是给他添加一个item,保证RunLoop不会被退出)

RunLoop的核心

前面我们一直重复的内容是RunLoop的是一个do - while循环,但是按道理来说do-while循环也是会占用cpu的,我们是怎么实现RunLoop的休眠功能呢?

就是它如何在没有消息处理时休眠,在有消息时又能唤醒。这样可以提高CPU资源使用效率 当然RunLoop它不是简单的while循环,不是用sleep来休眠,毕竟sleep这方法也是会占用cpu资源的。那它是如何实现真正的休眠的呢?

那就是:没有消息需要处理时,就会从用户态切换到内核态,用户态进入内核态后,把当前线程控制器交给内核态,这样的休眠线程是被挂起的,不会再占用cpu资源。

img

这里要注意用户态和内核态 这两个概念,还有mach_msg()方法。 内核态 这个机制是依靠系统内核来完成的。

RunLoop是通过通过内部维护的时间循环来对事件/消息进行管理的一个对象

  • 没有消息需要处理时,休眠避免掉资源占用
    • 用户态 -> 内核态
  • 有消息时候,立刻被唤醒
    • 内核态 -> 用户态

RunLoop的流程

Work.png

具体流程:

  1. 通知Observer已经进入RunLoop
  2. 通知 Observer 即将处理 Timer
  3. 通知Observer即将处理source0
  4. 处理sourece0
  5. 如果有soucre1就通知跳转第九步
  6. 通知Observer即将休眠
  7. 线程休眠状态,除非遇到下面的情况:
    • 有Source0
    • Timer到时间执行
    • 外部手动唤醒
    • 为RunLoop设定时间超时
  8. 通知Observer线程刚被唤醒
  9. 处理等待处理事件
    • 如果是 Timer 事件,处理 Timer 并重新启动循环,跳到 2
    • 如果是source1触发,就处理source
    • RunLoop如果手动被触发但尚未超时,重新启动循环
  10. 通知Observer即将退出

在这里插入图片描述

实际上 RunLoop 内部就是一个 do-while 循环。当你调用 CFRunLoopRun() 时,线程就会一直停留在这个循环里,直到超时或手动停止,该函数才会返回。

这里不用担心因为一直处于一个while中的时候,我们的线程就一直在运行,会浪费资源,但是实际上并非如此,我们的RunLoop进入休眠调用的函数mach_msg这个函数是内核记别的指令,可以让这个线程进入一个等待的装入,然后需要的时候再唤醒,所以不用担心这个线程的一个占用问题.

RunLoop的应用

AutoreleasePool

首先我们之前讲过主线程创建的时候,会自动生成RunLoop,而主线程注册了两个Observer,来是实现这里的AutoreleasePool的管理:

  • 第一个Observer,这里的Observer是用来监听一个事件Entry这个事件,即将进入 Loop 的时候,创建一个自动释放池,并且给了一个最高的优先级,保证自动释放池的创建发生在其他回调之前,这是为了保证能管理所有的引用计数。
  • 第二个Observer: 监听两个事件,一个 BeforeWaiting,一个 ExitBeforeWaiting 的时候,干两件事,一个释放旧的池,然后创建一个新的池,所以这个时候,自动释放池就会有一次释放的操作,是在 RunLoop 即将进入休眠的时候。Exit的时候,也释放自动释放池,这里也有一次释放的操作。

BeforeWaiting这个含义其实就是指我们当前的一个RunLoop即将休眠了的状态,我们这里就会释放一个旧池子,然后创建一个新的池子,等到RunLoop退出的时候再次销毁池子.

  • 进入RunLoop的时候会创建一个池子
  • 休眠的时候会销毁该池子,在创建一个新池子
  • 退出的时候再次销毁池子

响应触控事件

苹果提前在 App 内注册了一个 Source1 来监听系统事件。

比如,当一个 触摸/锁屏/摇晃 之类的系统事情产生,系统会先包装,包装好了,通过 mach port 传输给需要的 App 进程,传输后,提前注册的 Source1 就会触发回调,然后由 App 内部再进行分发。

  1. 注册一个Souce1来接受事件
  2. 硬件事件的发生
  3. IOKit.framework 生成 IOHIDEvent 事件并由 SpringBoard 接收
  4. SpringBoard 用 mach port 转发给需要的 App
  5. Soucre1触发回调
  6. 回调中 IOHIDEvent 包装成 UIEvent 进行处理或分发

刷新界面

改变Ui的参数他不会立刻更新,他也是通过RunLoop来进行一个更新的

当 UI 需要更新,先标记一个 dirty,然后提交到一个全局容器中去。然后,在 BeforeWaiting 时,会遍历这个容器,执行实际的绘制和调整,并更新 UI 界面。(在休眠和退出)

常驻线程

我们每一次网络请求的时候都会创建一个新的线程,这里为了减小开销,我们其实可以让一个线程常驻,保证线程不被销毁,这样可以让我们的开销减小.

我们在上面说过,一个RunLoop中如果没有Observer/Timer/Source等items,Runloop会自动退出,因此我们创建一个空的port发送消息给Runloop,以至于Runloop不会退出而是一直常驻

这里笔者给出一个案例:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(runThread) object:nil];
    [self.thread start];
    self.view.backgroundColor = UIColor.redColor;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event  {
    [self performSelector:@selector(runTest) onThread:self.thread withObject:nil waitUntilDone:NO];
}
- (void)runTest {
    NSLog(@"%@", [NSThread currentThread]);
}
- (void)runThread {
    NSLog(@"开启子线程 %@", self.thread);
    [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];
}

然后我们点击空白处

image-20250518135616295

网络请求

网络请求在RunLoop中有以下几个层级:

CFSocket
CFNetwork       ->ASIHttpRequest
NSURLConnection ->AFNetworking
NSURLSession    ->AFNetworking2, Alamofire
  • CFSocket:是最底层的接口,只负责socket通信
  • CFNetwork 是基于 CFSocket 等接口的上层封装,ASIHttpRequest 工作于这一层。
  • 这一次是一个更高级的封装,可以说是新的面向接口的封装
  • NSSURLSession 是 iOS7 中新增的接口,表面上是和 NSURLConnection 并列的,但底层仍然用到了 NSURLConnection 的部分功能 (比如 com.apple.NSURLConnectionLoader 线程),AFNetworking2 和 Alamofire 工作于这一层。

下面来了解一下有关于NSURLConnection的工作流程

使用NSConnection的时候,会传入一个Delegte,当调用了[connection start]后,delegate会不断收到回调.实际上,start 这个函数的内部会会获取 CurrentRunLoop,然后在其中的 DefaultMode 添加了4个 Source0 (即需要手动触发的Source)。CFMultiplexerSource 是负责各种 Delegate 回调的,CFHTTPCookieStorage 是处理各种 Cookie 的

当开始网络传输时,我们可以看到 NSURLConnection 创建了两个新线程:com.apple.NSURLConnectionLoader 和 com.apple.CFSocket.private。其中 CFSocket 线程是处理底层 socket 连接的。NSURLConnectionLoader 这个线程内部会使用 RunLoop 来接收底层 socket 的事件,并通过之前添加的 Source0 通知到上层的 Delegate。

在这里插入图片描述

NSURLConnectionLoader 中的 RunLoop 通过一些基于 mach port 的 Source 接收来自底层 CFSocket 的通知。当收到通知后,其会在合适的时机向 CFMultiplexerSource 等 Source0 发送通知,同时唤醒 Delegate 线程的 RunLoop 来让其处理这些通知。CFMultiplexerSource 会在 Delegate 线程的 RunLoop 对 Delegate 执行实际的回调。

NSTimer 和 CADisplayLink

NSTimer

前面一直有提到Timer source事件源,从上层来看其实他就相当于我们的一个NSTimer

一个NSTimer注册到RunLoop后,RunLoop会为其重复的时间点注册好事件,比方说10 : 00者几个时间点.RunLoop为了节省资源,不会在非常准确的时间点进行一个回调Timer.Timer 有个属性叫做 Tolerance (宽容度),标示了当时间点到后,容许有多少最大误差。由于 NSTimer 的这种机制,因此 NSTimer 的执行必须依赖于 RunLoop,如果没有 RunLoop,NSTimer 是不会执行的。

GCDTimer

使用GCDTimer就不会有这个问题,NSTimer十因为依赖与RunLoop的内容,如果RunLoop的任务繁重,很有可能导致时间不够准确,而采用GCD的计时器就不会有这个问题,因为这里的GCDTimer是依赖于我们的操作系统内核的,所以会更加准确.

CADisplayLink

CADisplayLink是一个执行频率(fps)和屏幕刷新相同(可以修改preferredFramesPerSecond改变刷新频率)的定时器,它也需要加入到RunLoop才能执行。与NSTimer类似,CADisplayLink同样是基于CFRunloopTimerRef实现,底层使用mk_timer(可以比较加入到RunLoop前后RunLoop中timer的变化)。和NSTimer相比它精度更高(尽管NSTimer也可以修改精度),不过和NStimer类似的是如果遇到大任务它仍然存在丢帧现象。通常情况下CADisaplayLink用于构建帧动画,看起来相对更加流畅,而NSTimer则有更广泛的用处。

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

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

相关文章

电子电路仿真实验教学平台重磅上线!——深圳航天科技创新研究院倾力打造,助力高校教学数字化转型

在传统电子电路课堂中&#xff0c;实验室的灯光总与高昂的成本、拥挤的设备、反复的耗材损耗相伴&#xff0c;而教师不得不面对这样的现实&#xff1a;有限的硬件资源束缚着教学深度&#xff0c;不可逆的实验风险制约着创新探索&#xff0c;固化的时空场景阻碍着个性化学习。当…

搭建一个WordPress网站需要多少成本

WordPress 最初可能只是一个简单的博客平台。但近年来&#xff0c;它不仅成为了最好的博客平台&#xff0c;还成为了一个全面的内容管理系统。白宫、jQuery、NGINX、《纽约时报》等企业都把 WordPress 作为自己的网上家园。 不过&#xff0c;它们只是其中的佼佼者。根据 Built…

Python数据可视化 - Pyecharts绘图示例

文章目录 一、Pyecharts简介及安装1. Pyecharts简介2. 安装Pyecharts 二、准备数据三、饼图示例1. 初始化选项配置2. 饼图相关设置3. 全局配置项3.1 标题配置项3.2 图例配置项3.3 提示框配置项3.4 工具箱配置项3.5 视觉映射配置项 4. 系列配置项4.1 标签选项配置4.2 图元样式配…

NC016NC017美光固态芯片NC101NC102

NC016NC017美光固态芯片NC101NC102 在存储技术的演进历程中&#xff0c;美光科技的NC016、NC017、NC101与NC102系列固态芯片&#xff0c;凭借其技术创新与市场适应性&#xff0c;成为行业关注的焦点。本文将从技术内核、产品性能、行业动向、应用场景及市场价值五个维度&#…

[Android] 青木扫描全能文档3.0,支持自动扫描功能

声明&#xff1a;根据许多帖友的反馈&#xff0c;我也根据重新实测得出结论&#xff1a;该app是提供一天的体验时间&#xff0c;后续还是采取收费才能使用功能的措施。因为现在市面上免费使用的扫描工具很少了&#xff0c;所以当初我初步测试感觉软件不错就发布了出来&#xff…

通俗解释Transformer在处理序列问题高效的原因(个人理解)

Transformer出现的背景 CNN 的全局关联缺陷卷积神经网络&#xff08;CNN&#xff09;通过多层堆叠扩大感受野&#xff0c;但在自然语言处理中存在本质局限&#xff1a; 局部操作的语义割裂&#xff1a;每个卷积核仅处理固定窗口&#xff08;如 3-5 词&#xff09;&#xff0c;…

区间带边权并查集,XY4060泄露的测试点

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 码蹄集 二、解题报告 1、思路分析 关于带边权并查集&#xff1a;并查集&…

【数据结构】1-4算法的空间复杂度

数据结构知识点合集 知识点 空间复杂度的定义以及计算 空间复杂度--空间开销&#xff08;内存开销&#xff09;与问题规模 n 之间的关系 无论问题规模怎么变&#xff0c;算法运行所需的内存空间都是固定的常量&#xff0c;算法空间复杂度为S(n) O(1)&#xff0c;S 表示 “Spac…

OpenAI推出Codex — ChatGPT内置的软件工程Agents

OpenAI继续让ChatGPT对开发者更加实用。 几天前,他们增加了连接GitHub仓库的支持,可以"Deep Research"并根据你自己的代码提问。 今天,该公司在ChatGPT中推出了Codex的研究预览版,这是迄今为止最强大的AI编码Agent。 它可以编写代码、修复错误、运行测试,并在…

AI日报 · 2025年5月15日|GPT-4.1 登陆 ChatGPT

AI日报 2025年5月15日&#xff5c;GPT-4.1 登陆 ChatGPT 1、OpenAI 在 ChatGPT 全面开放 GPT-4.1 与 GPT-4.1 mini 北京时间 5 月 14 日晚&#xff0c;OpenAI 在官方 Release Notes 中宣布&#xff1a;专为复杂代码与精细指令场景打造的 GPT-4.1 正式加入 ChatGPT&#xff0…

W5500使用ioLibrary库创建TCP客户端

1、WIZnet全硬件TCP/IP协议栈 WIZnet全硬件TCP/IP协议栈,支持TCP,UDP,IPv4,ICMP,ARP,IGMP以及PPPoE协议。 以太网&#xff1a;支持BSD和WIZCHIP&#xff08;W5500/W5300/W5200/W5100/W5100S&#xff09;的SOCKET APIs驱动程序。 互联网&#xff1a; DHCP客户端 DNS客户端 FTP客…

组态王|如何创建组态王工程?

哈喽,你好啊,我是雷工! 组态王是比较普及的组态软件之一,大部分工控人应该都接触过组态王软件, 最近有个用组态王软件开发上位机,对设备进行集中控制的项目,边开发,顺便记录一些使用方法。 本篇从基础的如何创建组态王工程开始记录,以下为操作笔记。 1 、首先在工程…

mysql数据库-3(备份和恢复)

1. 冷备份和还原的实现 简介:冷备份定义是 读、写操作均不可进行,数据库停止服务 (超级简单) 冷备份 需求 对 10.0.0.13 主机实现冷备操作 关闭 10.0.0.13 主机的服务(ubuntu系统为例) 10.0.0.12为远程主机 systemctl stop mysql.service 备份数据 mkdir /data/…

估分啦~全国青少年信息素养大赛部分赛项已考完~图形化/算法创意实践

2025年全国青少年信息素养大赛-图形化编程挑战赛-小低组真题试卷 全国青少年信息素养大赛&#xff0c;图形化编程和算法创意实践挑战赛已考完&#xff0c;各位可以去题库重新做做下&#xff0c;复盘下&#xff0c;为更好的自己努力~ 配有答案和解析哦~ 2025年全国青少年信息素…

【Linux服务器】-虚拟机安装(CentOS7.9)

【Linux服务器】-虚拟机安装&#xff08;CentOS7.9&#xff09; 需提前准备好环境安装1. 创建新的虚拟机2. 选择默认配置&#xff0c;下一步3. 选择稍后指定操作系统&#xff0c;下一步4. 选择linux操作系统&#xff0c;并选择CentOS 7 64位 &#xff0c;下一步5. 分配磁盘空间…

2025年渗透测试面试题总结-百度面经(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 百度面经 百度安全工程师面试深度复盘与优化指南 一、项目经验反思与优化策略 二、技术问题深度解析 …

分别用 语言模型雏形N-Gram 和 文本表示BoW词袋 来实现文本情绪分类

语言模型的雏形 N-Gram 和简单文本表示 Bag-of-Words 语言表示模型简介 (1) Bag-of-Words (BoW) 是什么&#xff1f; *定义&#xff1a;将文本表示为词频向量&#xff0c;忽略词序和语法&#xff0c;仅记录每个词的出现次数。 **示例&#xff1a; 句子1&#xff1a;I love …

C#.NET 或 VB.NET Windows 窗体中的 DataGridView – 技巧、窍门和常见问题

DataGridView 控件是一个 Windows 窗体控件&#xff0c;它允许您自定义和编辑表格数据。它提供了许多属性、方法和事件来自定义其外观和行为。在本文中&#xff0c;我们将讨论一些常见问题及其解决方案。这些问题来自各种来源&#xff0c;包括一些新闻组、MSDN 网站以及一些由我…

PyTorch音频处理技术及应用研究:从特征提取到相似度分析

文章目录 音频处理技术及应用音频处理技术音视频摘要技术音频识别及应用 梅尔频率倒谱系数音频特征尔频率倒谱系数简介及参数提取过程音频处理快速傅里叶变换(FFT)能量谱处理离散余弦转换 练习案例&#xff1a;音频建模加载音频数据源波形变换的类型绘制波形频谱图波形Mu-Law 编…

VSTO(C#)Excel开发进阶2:操作图片 改变大小 滚动到可视区

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。 源码指引:github源码指引_初级代码游戏的博客-CSDN博客 入…