iOS开发—RunLoop详解

2023-11-02

随着oc语言不断迭代,苹果的API也是逐步完善,RunLoop在实际开发中应用的越来越少。但是在面试中,假如面试官问你RunLoop的相关知识了解,那就相当于面试官在问你从事iOS开发工作的真实年限问题。那么下面我们就详细了解一下RunLoop的相关知识。

RunLoop是一种运行循环机制,其实就是死循环。它有两个作用:1.保证程序不退出 2.负责监听事件。我们可以理解为Runloop在不断的循环中,一直在询问线程队列中是否有待办任务。如果有任务就去处理,没有任务就休息。

Runloop模式:在官方文档介绍中,Runloop有五种应用模式,但是实际开发中,提供给我们开发者使用的就是常见的三种。

Runloop由三种元素组成:Source(事件源) Timer(定时器) Observer(观察),Runloop底层调用的是CoreFoundation框架下的CFRunloop,有兴趣的可以研究下。
Source(事件源):按照函数调用栈Source的分类
Source0:非Source1就是Source0
Source1:系统内核事件

Source

- (void)viewDidLoad {
    [super viewDidLoad];
    // GCD
    _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));

    dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0);

    //设置回调
    dispatch_source_set_event_handler(_timer, ^{
        NSLog(@"@-----%@",[NSThread currentThread]);
    });
    //启动定时器 timer本质上也是一个source
    dispatch_resume(_timer);
}

Observer

- (void)viewDidLoad {
    [super viewDidLoad];
    //获取RunLoop
    CFRunLoopRef runloop = CFRunLoopGetCurrent();
    //runloop是不是指针?是指针
    //需要释放嘛?不需要
    //创建观察者
    CFRunLoopObserverRef runloopObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting, YES, 0, &callBack, NULL);

    //添加观察者
    CFRunLoopAddObserver(runloop, runloopObserver, kCFRunLoopDefaultMode);

    CFRelease(runloopObserver);
//    free(runloopObserver);
//    free和CFRelease(CoreFoundation下的CFRelease)的区别?
    /*
     CFRunLoopObserverCreate开辟堆空间,是个结构体。这个结构体内部有可能仍然有一个指针指向另一个堆空间。如果直接free,其内部的指针指向的堆空间就会内存泄露!
     CFRelease作为CoreFoundation提供给开发者的方法,它会处理上面的问题。
     */
}

RunLoop

- (void)viewDidLoad {
    [super viewDidLoad];
    NSTimer * timer= [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
    //currentRunLoop:获取当前线程的Runloop  mainRunLoop:主线程的Runloop
    /*
     1.默认模式:NSDefaultRunLoopMode
     在默认模式下,RunLoop会一直询问当前队列中是否存在待解决任务,有任务就去执行。但是当主线程中用户触发UI事件,应用此模式的RunLoop就会停止监听,从而不会触发回调事件
     */
    //[[NSRunLoop currentRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
    /*
     2.UI模式:UITrackingRunLoopMode
     在UI模式下,UI模式是优先级最高的,当用户触发UI事件,会即刻触发RunLoop。但是只能被UI事件所唤醒
     */
    //[[NSRunLoop currentRunLoop]addTimer:timer forMode:UITrackingRunLoopMode];
    /*
     3.占位模式:NSRunLoopCommonModes
     占位模式下,上面两种模式都会被触发
     */
    [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
}
-(void)timerMethod{
    NSLog(@"%@--",[NSThread currentThread]);
}

RunLoop与多线程

如果RunLoop的timer回调里面有耗时操作,设置到主线程的RunLoop会使界面造成卡顿感,所以我们要把RunLoop放在子线程中。

- (void)viewDidLoad {
    [super viewDidLoad];

    NSThread * thread =[[NSThread alloc]initWithBlock:^{

        NSTimer * timer= [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
        NSLog(@"来了");
    }];
    [thread start];
}
-(void)timerMethod{
    NSLog(@"come here");
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"%@--",[NSThread currentThread]);
}

那么问题来了,假如我们使用上面的方式,那么log输出应该是怎样的呢?

2018-04-25 11:17:59.551463+0800 多线程[3867:89870] [MC] Lazy loading NSBundle MobileCoreServices.framework
2018-04-25 11:17:59.568394+0800 多线程[3867:89870] [MC] Loaded MobileCoreServices.framework
2018-04-25 11:17:59.665077+0800 多线程[3867:89994] 来了

没错,只是输出了“来了”,timer的timerMethod回调方法并没有被触发,原因是我们创建的thread子线程是个局部变量,在viewDidLoad方法执行完毕后就被释放了。

我们可以通过这样的方式进行验证。创建一个继承自NSThread的WTThread子类,在WTThread的dealloc方法中,设置log输出一下。

#import "WTThread.h"
@implementation WTThread

-(void)dealloc{
    NSLog(@"线程走了");
}
@end

//修改viewDidLoad方法
- (void)viewDidLoad {
    [super viewDidLoad];

    NSThread * thread =[[WTThread alloc]initWithBlock:^{

        NSTimer * timer= [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
        NSLog(@"来了");
    }];
    [thread start];
}
-(void)timerMethod{
    NSLog(@"come here");
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"%@--",[NSThread currentThread]);
}

log输出

2018-04-25 11:17:59.665077+0800 多线程[3867:89994] 来了
2018-04-25 11:17:59.665709+0800 多线程[3867:89994] 线程走了

我们会很明显的看到WTThread的dealloc方法执行了。我们可能会猜测会不会是我们在创建thread子线程的时候创建的是局部变量,才导致的这个问题呢?我们继续验证一下。

@property(nonatomic,strong)WTThread* thread;

- (void)viewDidLoad {
    [super viewDidLoad];
    _thread =[[WTThread alloc]initWithBlock:^{
        NSTimer * timer= [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
        NSLog(@"来了");
    }];

    [_thread start];
}

-(void)timerMethod{
    NSLog(@"come here");
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"%@--",[NSThread currentThread]);
}

那我们这样解决的话,可以触发timerMethod回调方法嘛?答案是依旧不会。

2018-04-25 11:30:00.750498+0800 多线程[4116:97855] [MC] Lazy loading NSBundle MobileCoreServices.framework
2018-04-25 11:30:00.779425+0800 多线程[4116:97855] [MC] Loaded MobileCoreServices.framework
2018-04-25 11:30:00.830370+0800 多线程[4116:98055] 来了

我们就会明白,我们创建的WTThread是走了的,它并没有被释放(因为没走dealloc方法),但是timerMethod回调方法为什么依旧不执行呢?

重点:我们使用strong修饰创建的thread(@property(nonatomic,strong)WTThread* thread;)它实际是属于OC对象,这个OC对象和我们所说的线程是有区别的。我们创建的WTThread* thread它代表着一条线程,但是!这不代表着这个对象就是线程本身!

线程的生命:只会由CPU决定!只和线程的任务有关系,任务没有了,线程就走了。任务如果在,线程就依旧会在!我们接下来继续验证,回到最初的代码。假如我们给创建的子线程一个永远执行不完的任务,那么这条线程就会永远被保存!

- (void)viewDidLoad {
    [super viewDidLoad];
    NSThread * thread =[[WTThread alloc]initWithBlock:^{  
        NSTimer * timer= [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
        NSLog(@"来了");
        while (true) {
            //啥都不干
        }
    }];
    [thread start];
}

-(void)timerMethod{
    NSLog(@"come here");
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"%@--",[NSThread currentThread]);
}

我们这样操作,NSThread * thread这个OC对象不会被释放,因为它代表的线程没有被释放!我们保住了thread子线程,但是!timerMethod回调依旧没有被触发?原因是啥?原因就是([[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];)timer对象被添加进RunLoop当中去了,但是我们写的while循环和RunLoop没有关系啊!那我们该咋办呢?

重点:线程中的RunLoop是不会自己主动跑起来的,它需要我们让它跑起来!但是我们在想在主线程中,RunLoop为什么会自动跑呢?因为在主线程的main函数当中,UIApplication帮我们开启了RunLoop。

NSThread * thread =[[WTThread alloc]initWithBlock:^{

        NSTimer * timer= [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
        [[NSRunLoop currentRunLoop] run];//RunLoop运行起来(死循环,本质上就是do while 让线程保活,只不过跟我们自己写的while (true)相比,它里面做了很多事:当前队列中有没有任务需要处理,要不要处理)
        NSLog(@"来了");
        while (true) {
            //啥都不干
        }
    }];
    [thread start];

这样,我们就可以触发timerMethod回调。那我们继续想,如何取消这个RunLoop呢?

假如我们直接 [[NSRunLoop currentRunLoop] run];那麽我们很难将RunLoop取消掉,我们可以这样做

#import "ViewController.h"
#import "WTThread.h"

@interface ViewController ()
@property(nonatomic,assign)BOOL isFinished;
//@property(nonatomic,strong)WTThread* thread;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _isFinished=NO;
    NSThread * thread =[[WTThread alloc]initWithBlock:^{
        NSTimer * timer= [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
//        [[NSRunLoop currentRunLoop] run];//死循环
        NSLog(@"来了");
        while (!_isFinished) {
            [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.00001]];//每隔0.00001秒执行一次RunLoop
        }
    }];

    [thread start];
}
//触摸开始后停止RunLoop(实际使用时结合业务逻辑使RunLoop停止)
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    _isFinished = YES;
}
-(void)timerMethod{
    NSLog(@"come here");
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"%@--",[NSThread currentThread]);
}

@end

或者这样

#import "ViewController.h"
#import "WTThread.h"

@interface ViewController ()
@property(nonatomic,assign)BOOL isFinished;
//@property(nonatomic,strong)WTThread* thread;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _isFinished=NO;
    NSThread * thread =[[WTThread alloc]initWithBlock:^{
        NSTimer * timer= [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
        [[NSRunLoop currentRunLoop] run];//死循环
        NSLog(@"来了");


    [thread start];
}
//触摸开始后停止RunLoop(实际使用时结合业务逻辑使RunLoop停止)
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    _isFinished = YES;
}
-(void)timerMethod{
    NSLog(@"come here");
    if(_isFinished){
        [NSThread exit];
    }
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"%@--",[NSThread currentThread]);
}

@end

拓展:

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [NSThread exit];
}

touchesBegan这个方法所在的线程是哪个线程呢?当然是主线程,那么在主线程中执行exit,程序会如何运行呢?答案是程序正常运行,但是用户就操作不了UI了。这就涉及到主线程和子线程的区别。
主线程和子线程的区别:
主线程其实就是程序的一个子线程
面试题:UIKit是否是线程安全的?
答案:不是线程安全的。如果出现了多条线程访问,就会出现资源抢夺。因为UIKit没有加锁
所以苹果和开发者就约定用主线程访问UI,主线程就是更新UI的作用。所以我们也叫主线程为UI线程。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

iOS开发—RunLoop详解 的相关文章

  • iOS 批处理脚本打包-从编译到打包

    最近更新了Xcode9 由于项目需要 Xcode9打包出ipa后根据之前的脚本工具 继续我的批处理打包 但是 打包后居然出现了iOS 11 安装后图标没有改变还是原始的图标 为了验证 我再一次用Xcode8 打包 然后脚本打包 没有出现问题
  • iOS app签名机制

    前言 在移动开发中 iOS系统下的app和andorid系统下的app一个很大的区别是 android系统下 app的安装很方便 可以从多个应用商店下载 小米应用商店 华为应用商店 也可以直接下载apk的包安装 而在iOS系统下 对app的
  • NSTextAttachment实现图文混排

    苹果在iOS7中推出了一个新的类NSTextAttachment 它是做图文混排的利器 本文就是用这个类 只用50行代码实现文字与表情混排 当然也可以实现段落中的图文混排 首先说一下文字和表情的混排 先来做点儿准备工作 搞一个存放表情信息的
  • iOS开发_HOOK

    我们在使用NSURLRequest时 传入请求地址URL 但是如果URL中有中文的话 我们会发现NSURLRequest的请求地址会报空 void viewDidLoad super viewDidLoad NSURL url NSURL
  • 如何使用iAd在应用程序中展示Banner广告

    本文由 Da杯柠檬水 微博 翻译自 AppCoda 原文 Using iAd to Display Banner Ad in Your App 虽然 你可能即将开始开发下一个超级应用程序 你的一切规划和设计都已就绪 但还是有一件事可能你并没
  • ios中模拟手势单击或是双击

    1 拓展UITouch类和UIEvent类 通过category 具体见附件TouchSynthesis h和TouchSynthesis m 2 在需要的类中 定义UITouch和UIEvent类的对象 UITouch touch UIT
  • iOS开发之第三方支付支付宝支付教程,史上最新最全第三方支付宝支付方式实现、支付宝集成教程,支付宝实现流程

    本章项目demo https github com zhonggaorong alipayDemo 支付宝支付大致流程为 1 公司与支付宝进行签约 获得商户ID partner 和账号ID seller 和私钥 privateKey 开发中
  • iOS面试小贴士

    最全的iOS面试题及答案 iOS面试小贴士 回答好下面的足够了 多线程 特别是NSOperation 和 GCD 的内部原理 运行时机制的原理和运用场景 SDWebImage的原理 实现机制 如何解决TableView卡的问题 block和
  • 地球坐标,火星坐标,百度坐标转换

    标准地球坐标即GPS设备获得的坐标 该坐标需要经过国家测绘局进行加密后形成火星坐标 WGS 84 我们用的google坐标和高德地图坐标也就是火星坐标 百度地图 在火星坐标的基础上再进行一次加密 形成了百度地图上的坐标 因此 直接将标准地球
  • iOS 15 适配踩坑:NavigationBar、UITabBar失效问题

    苹果前两天推出了iOS 15 秋天都等不及 相关链接 ios 15 0 适配问题 NavigationBar和UITabBar失效问题 Xcode 13 beta版 iOS 15 beta 3的系统 除了客户提出的问题 自己还发现了两处UI
  • objc学习笔记-基础

    1 xcode 4 3 3以后 NSAutoreleasePool 的用法发生改变 由 NSAutoreleasePool pool NSAutoreleasePool alloc init pool drain 变为 autoreleas
  • iOS图标(AppIcon)与启动图(LaunchImage)

    图标 AppIcon 与启动图 LaunchImage 是开发iOS应用程序必不可少的内容 但是在网络上对于这部分的内容讲解的并不详细 所以花些时间写了这篇文章 希望有需要的朋友可以随时查看 想知道AppIcon与LaunchImage的原
  • UITableView性能优化-一次面试后的反思总结

    面试的时候遇到这个问题 竟一时没有全答上来 于是Google了一下 常见的一些譬如Cell重用 设计统一Cell 缓存Cell高度 Cell数据资源缓存 这些其实平时都在用 但因为平时还是缺乏总结 回答这么个问题的时候却只想到说 重用 缓存
  • ios开发教程入门到精通

    第1集 初识macOS 点击观看 第2集 开发工具Xcode 点击观看 第3集 初识Objective C 点击观看 待续
  • iOS开发之内存管理

    iOS开发之内存管理 一 垃圾回收机制 二 内存管理的概念 三 OC内存管理注意事项 四 MRC相关语法 一 垃圾回收机制 与Java语言相同Objective c 2 0之后 也提供了垃圾回收机制 OC是支持垃圾回收机制的 Garbage
  • iOS开发,tableView中cell的重用详解

    注意 原创版权 转载必须标明出处作者 翻版必究 iOS中tableView是一个大的模块组件 它的重要性每个iOSCoder都是了解的 但是tableView中却有个重大的坑 就是cell的重用 每个刚接触iOS开发的人都深受其海 那么经过
  • iOS内存详解

    堆栈 iOS内存条中有一部分是只读的 有一部分是可读可写的 我们操作的是可读可写部分 那么在这块内存当中 我们怎么划分堆和栈呢 我们可以限定死堆栈的内存空间 但是这样显然是不好的 那么可以使用相对弹性的空间 一个从上往下扩展 一个从下往上扩
  • IOS开发系列——异步绘制专题

    异步绘制专题 1 图片处理 1 1 编辑图片的几个方法 第一种 先用UIImage对象加载一张图片 然后转化成CGImageRef放到CGContext中去编辑 第二种 用CGImageCreate函数创建CGImageRef 然后把CGI
  • iOS App打包上架详细流程

    一 前言 作为一名iOS开发者 把辛辛苦苦开发出来的App上传到App Store是件必要的事 但是很多人还是不知道该怎么上传到App Store上 下面就来详细讲解一下具体流程步骤 二 准备 一个已付费的开发者账号 可分为 账号类型分为个
  • iOS代码混淆初探

    iOS代码混淆初探 在没有对应用的代码做过特殊处理的情况下 一般应用的代码中的类名和方法都是明文的 在逆向分析中 通过class dump获取MachO可执行文件中的所有类 定义的方法和属性 就能很快的从名字入手猜到这个方法是做什么用的 所

随机推荐

  • 层次分析法模型(数学建模学习)

    本系列参考清风老师的数学建模课程 层次分析法模型 一 模型介绍 一 模型引入 对于方案选择类问题 评价类问题采用层次分析法 The ayalytic hierarchy process AHP 模型进行评分 之后评分高的就是最佳方案 二 模
  • Wireshark对SMTP抓包分析

    本文主要使用Wireshark对邮件客户端使用SMTP协议发送邮件的过程进行抓包分析并使用telnet命令进行简单操作 1 SMTP简介 简单邮件传输协议 英语 Simple Mail Transfer Protocol 缩写 SMTP 是
  • 《自然语言处理》-文本生成实验(基于MindSpore),避免的坑,保姆式教学

    最近我的导师去了解一下华为的MindSpore框架 觉得很有意思然后就让我跑一下他的文本生成实验 不过其中有很多是他的实验手册没怎么写清楚的点 我在这里为各位一个一个排坑拉 本实验都是来源于华为的文本生成实验手册 只是里面很多内容写的不清楚
  • 声速的测量的实验原理和应用_【声速测量】实验须知

    操作常见错误 1 忘记记录源频率f0 或选错源频率f0 每一台设备的源频率都不相同 在导轨左 右两侧可以找到 如下图为 37420 2 超声波的发射器和接受器端面不平行 甚至不与轨道垂直 解决办法 拧松发射器或接收器后面的螺丝 然后调整端面
  • 清除浮动伪元素/双伪元素

    清除浮动 问题 在做浮动布局的时候 如果父级盒子嵌套了子级盒子 如果父级盒子没有设置固定的高度 那么里面的子级盒子浮动以后 父级盒子的高度就不会被撑开 显示默认的高度为0就会影响后面的盒子显示布局 说明 用浮动布局的时候我们必须要嵌套一个父
  • Linux创建LVM分区与扩容

    1 划分物理磁盘格式 针对要增加的硬盘进行格式化 fdisk dev sdb 欢迎使用 fdisk util linux 2 23 2 更改将停留在内存中 直到您决定将更改写入磁盘 使用写入命令前请三思 Device does not co
  • 通用业务平台设计(三):自动化打包平台建设

    前言 在上家公司 随着业务的不断拓展 需要打多个包来支持业务的快速发展 这篇文章主要为大家分享在构建自动化打包平台过程中一些经验总结以及躺过的坑 通用业务平台系列 通用业务平台设计 一 概览 通用业务平台设计 二 扩展多国家业务 通用业务平
  • 面向对象程序设计实验考试

    1 圆的面积 namespace CircleArea class Program static void Main string args const double PI 3 1415926 double Radius Area Cons
  • blender 入门教程

    目录 blender软件安装 blender python模块 blender 调用python脚本 调用成功 结果不对 blender渲染3d对象 加载背景图不显示的解决方法 背景图不显示原因 关节运动教程 自身旋转动画 导入模型 再导出
  • R语言学习:数据结构7-数据框

    数据框 data frame 主要是用来存储 表格数据tabular data的 可以将数据框视为各元素长度相同的 列表 具体为 每个元素代表一列数据 每个元素的长度代表行数 元素类型可以不同 创建数据框 data frame df lt
  • 模拟html post表单提交

    一 打开自动提交访问 设置好表单账号密码 然后访问指定地址 就能自动提交登录并跳转到首页 1 填写访问表单信息 设置好表单账号密码
  • (Design Patterns)设计模式的重要性

    设计模式使人们 可以更简单方便地复用成功的设计和体系结构 将已证实的技术表叔成设计模式也会使新系统开发者更加容易理解其设计思路 设计模式帮助你做出有利于系统复用性 通过提供一个显式类和对象作用关系以及它们之间潜在的说明规范 设计模式甚至能够
  • 团队管理的五大核心

    在企业成长的过程中 要把企业做大和做强 探究其意义 是一个过程 期间每个阶段所面临的不同问题赋予了企业成长过程中不同的意义 必须不断提升企业的内在素质 形成企业管理的五大核心能力 才能在成长的道路上走的四平八稳 团队管理的五大核心是什么呢
  • ssh错误:unable to authenticate解决办法

    第三方图形化工具SQLyog远程连接Linux数据库 出现ssh错误 unable to authenticate mysql8 0之前没有出现该问题 在mysql8 0后就会出现该问题 MySQL 8 0 之前的版本加密规则是 mysql
  • 特征选择:最大信息系数(MIC;Maximal Information Coefficient)【用于衡量两个变量X和Y之间的关联程度,线性或非线性的强度,常用于机器学习的特征选择】

    一 概念 1 1 MIC MIC 即 Maximal Information Coefficient 最大信息系数 属于Maximal Information based Nonparametric Exploration MINE 最大的
  • MyCobot六轴机械臂的基本操作(二)

    上一讲我们做ssh和vnc的设置 有小伙伴问设置些有什么用 那么这里我先来解释一下这些功能有什么用处 首先我们可以通过ssh在我们的Windows桌面进行程序开发 然后上传到树莓派进行验证 我们也可以在windows平台通过vnc远程操作我
  • Qt发布安装程序(打包依赖库+生成安装程序)

    目录 打包Qt依赖库 打包其它依赖库 生成安装程序 下载并安装Inno Setup 5 使用Inno Setup 5 Qt编译好的项目是不能直接被双击运行的 如下所示 缺少了依赖的库 需要添加进去 打包Qt依赖库 Qt提供了可以打包依赖的Q
  • onnx的VS2022和QT部署中遇到的问题GetInputName()函数报错问题

    这个问题是因为onnxruntime在1 7版本改变了函数名的原因 GetInputName 改名成了GetInputNameAllocated 在修改函数名后需要做一些小调整如下 修改前 修改后 在qt中也遇到了这个问题 修改前 修改后
  • [深入理解NAND Flash (颗粒篇) ] QLC NAND 已来未热,是时候该拥抱了?

    前言 伴随着闪存芯片的发展趋势 现如今便宜 大容量的SSD基本上都需要上QLC闪存芯片了 一时间QLC有山雨欲来之势 大容量QLC SSD的普及似乎已经触手可及 虽然现在主流是 TLC NAND 第三代 但下一代 Q L C N A N D
  • iOS开发—RunLoop详解

    随着oc语言不断迭代 苹果的API也是逐步完善 RunLoop在实际开发中应用的越来越少 但是在面试中 假如面试官问你RunLoop的相关知识了解 那就相当于面试官在问你从事iOS开发工作的真实年限问题 那么下面我们就详细了解一下RunLo