一文读懂iOS中的Crash捕获、分析以及防治
Crash系统性总结
- Crash捕获与分析
- Crash收集
- 符号化分析
- Crash类别以及解法分析
- 子线程访问UI而导致的崩溃
- unrecognized selector send to instance xxx
- KVO crash
- KVC造成的crash
- NSTimer导致的Crash
- 野指针
- Watch Dog超时造成的crash
- 其他crash待补充
- 参考文章:
对于iOS端开发,定位和解决Crash毕竟两个流程,首先是根据线索来分析和定位问题,得到一个大概的猜想,之后按照自己的猜想去提供外部条件,来尝试复现问题,如果问题能够成功复现并复原与线程问题相似的堆栈现场,则基本完成了90%的工作,剩下的10%才是修复此问题。对于crash比例极低的,例如没有版本相关性的,对我们的应用影响极小的,我们可以通过去做AB实验尝试去修复。
大家先思考下以下问题然后阅读文章找到答案
- bad_access 的排查途径有哪些 ?
- 什么情况下会产生 bad_access ?
- 不同的bad_access有什么方案可以完美解决?
Crash捕获与分析
Crash收集
收集方式:
- 利用Xcode获取
- 将iOS设备连接到Mac电脑。
- 打开Xcode,选择顶部菜单栏的“Window”。
- 选中“Organizer”,然后选择“Crashes”标签。
- 在这里,你可以看到与你的APP关联的所有崩溃日志,选择APP名字以及版本等,就可以查看各种崩溃日志。
- 友盟、bugly、Sentry(目前我公司使用的就是这个https://sentry.io/for/ios/)等获取。
- 通过iOS SDK中提供的线程的函数
NSSetUncaughtExceptionHandler
用来做异常处理,利用NSSetUncaughtExceptionHandler
,当程序退出的时候,可以先进行处理,然后做一些自定义的动作,并通知开发者。(例如:我们把崩溃存在沙盒,等下次用户打开应用的时候,把crash数据上传到我们的服务器)
下面介绍如何自己手动的获取日志(也就是利用NSSetUncaughtExceptionHandler自己实现):
MyUncaughtExceptionHandler.h文件
MyUncaughtExceptionHandler.m文件
AppDelegate.m
在appledelegate导入头文件加上一个异常捕获监听,用来处理程序崩溃时的回调动作 在这里也要判断一下之前有没有崩溃日志 如果有发送给服务器 。
上方代码就已经 可以获取到 carsh日志了。我们现在来尝试一下,做一个crash代码,然后打开沙盒的log日志。
Carsh代码如下(实现一个kvc中的key为nil的crash):
取出沙盒的日志如下:
我们可以通过该表大致的得到 崩溃的原因。
符号化分析
当应用程序在IOS 设备上崩溃(例如,闪退)时,一份“Crash崩溃报告”将在该设备上创建并存储起来。崩溃报告描述了应用程序是在何种条件下崩溃的,大部分情况下包含一份当前正在运行线程的完整堆栈跟踪。
如果设备就在身边,可以连接设备,打开Xcode - Window - Organizer,在左侧面板中选择Device Logs(可以选择具体设备的Device Logs或者Library下所有设备的Device Logs),然后根据时间排序查看设备上的crash日志。这是开发、测试阶段最经常采用的方式。
如果应用程序已经提交到App Store发布,用户已经安装使用了,那么开发者可以 通过iTunes Connect (Manage Your Applications - View Details - Crash Reports)获取用户的crash日志。不过这并不是100%有效的,而且大多数开发者并不依赖于此,因为这需要用户设备同意上传相关信息。然后呢。。。
我们其实在获取到崩溃日志以后,是不知道具体哪行代码崩溃的。这个时候我们就需要获取到dsYM文件,利用dsYM符号化调用栈,找到具体代码行
长话短说就是将运行时信息转换为源码信息,符号化是一种机制,将我们在设备运行时 App 的内存地址和关联的指令信息转换为源码文件中具体文件名、方法名、行数等;可以理解为将运行时机器如何看待处理我们 App 的信息转换成我们开发者如何看待处理我们的 App(源码)。如果缺少这层转换,哪怕只有几行的代码的 App,bug 定位也变得难以进行;一般第三方的crash收集后,我们在集成SDK后,开发者需要在第三方服务的后台配置他们的应用信息,包括应用的标识符、dSYM文件的上传方式等。有些服务允许开发者通过API上传dSYM文件,而有些则要求开发者在构建应用时手动上传。
当应用发生崩溃时,第三方服务会捕获崩溃日志,并使用开发者提供的dSYM文件对日志进行解析。解析后的崩溃报告会包含崩溃发生的文件名、函数名和行号等详细信息,这些信息对于开发者来说是非常有价值的。
下方为博客找到的某个截图示例:
Crash类别以及解法分析
子线程访问UI而导致的崩溃
Objective-C是一种动态语言,它具有强大的运行时特性。我们可以利用这些特性,设计一套防护系统,以降低应用程序的崩溃率。具体来说,我们可以利用Method Swizzling等技术,对容易造成崩溃的系统方法进行拦截和修改,以达到避免和修复崩溃的目的。
例如,我们可以拦截UIView的setNeedsLayout和setNeedsDisplay方法,确保这些方法只在主线程中被调用。如果它们在子线程中被调用,程序将抛出异常或进行其他错误处理。这样就可以避免因子线程访问UI而导致的崩溃问题。不过我们尽量在做UI操作的时候,转到主线程去做处理。
unrecognized selector send to instance xxx
这样的错误,你可能并不陌生。
这种错误通常是因为调用了某个对象或者某个类里不存在的方法,从而触发了消息转发机制,最终把这个未识别的消息发送给了NSObject的默认实现。
例如调用以下一段代码就会产生crash
//test code
UIButton * testObj = [[UIButton alloc] init];
[testObj performSelector:@selector(someMethod:)];
报错如下:
runtime中具体的方法调用流程大致如下:
- 首先,在相应操作的对象中的缓存方法列表中找调用的方法,如果找到,转向相应实现并执行。
- 如果没找到,在相应操作的对象isa指针指向的类中的方法列表中找调用的方法,如果找到,转向相应实现执行。
- 如果没找到,去父类指针所指向的对象中执行1,2.
- 以此类推,如果一直到根类还没找到,转向拦截调用,走消息转发机制。
如果没有重写拦截调用的方法,程序报错。
所以,此类问题解决方案: 拦截调用
在方法调用中说到了,如果没有找到方法就会转向拦截调用。
那么什么是拦截调用呢?
拦截调用就是,在找不到调用的方法程序崩溃之前,你有机会通过重写NSObject的四个方法来处理:
+ (BOOL)resolveClassMethod:(SEL)sel;
+ (BOOL)resolveInstanceMethod:(SEL)sel;
//后两个方法需要转发到其他的类处理
- (id)forwardingTargetForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)anInvocation;
由上图可见,在一个函数找不到时,runtime提供了三种方式去补救:
- 调用resolveInstanceMethod给个机会让类动态添加一个该函数的实现。
- 调用forwardingTargetForSelector让别的对象去执行这个函数(动态新建类,并给该类创建一个函数实现)
- 调用forwardInvocation(函数执行器)灵活的将目标函数分发给其他类来处理。
如果都不中,调用doesNotRecognizeSelector抛出异常。
unrecognized selector crash 防护方案
既然可以补救,我们完全也可以利用消息转发机制来做文章。那么问题来了,在这三个步骤里面,选择哪一步去改造比较合适呢。
这里我们选择了第二步forwardingTargetForSelector来做文章。原因如下:
resolveInstanceMethod 需要在类的本身上动态添加它本身不存在的方法,这些方法对于该类本身来说冗余的
forwardInvocation可以通过NSInvocation的形式将消息转发给多个对象,但是其开销较大,需要创建新的NSInvocation对象,并且forwardInvocation的函数经常被使用者调用,来做多层消息转发选择机制,不适合多次重写
forwardingTargetForSelector可以将消息转发给一个对象,开销较小,并且被重写的概率较低,适合重写
选择了forwardingTargetForSelector之后,可以将NSObject的该方法重写,做以下几步的处理:
- 动态创建一个桩类
- 动态为桩类添加对应的Selector,用一个通用的返回0的函数来实现该SEL的IMP
- 将消息直接转发到这个桩类对象上。
下方是一个动态创建类的代码示例:
#import <objc/runtime.h>
#import <Foundation/Foundation.h>int main(int argc, const char * argv[]) {@autoreleasepool {// 动态创建一个类Class dynamicClass = objc_allocateClassPair([NSObject class], "DynamicClass", 0);if (!dynamicClass) {NSLog(@"Failed to allocate class pair");return -1;}// 注册这个类objc_registerClassPair(dynamicClass);// 动态创建一个实例id instance = [[dynamicClass alloc] init];NSLog(@"Instance of DynamicClass: %@", instance);// 动态添加方法class_addMethod(dynamicClass, @selector(sayHello), (IMP)sayHelloIMP, "v@:");// 调用动态添加的方法[instance sayHello];}return 0;
}// 方法的实现
void sayHelloIMP(id self, SEL _cmd) {NSLog(@"Hello from DynamicClass!");
}
在这个示例中,我们首先使用objc_allocateClassPair创建一个新的类,然后使用objc_registerClassPair注册这个类。接着,我们动态添加了一个名为sayHello的方法,并调用它。
解释参数
objc_allocateClassPair
:用于分配一个新的类对,第一个参数是父类,第二个参数是新类的名称,第三个参数是额外的内存大小(通常为0)。
objc_registerClassPair
:用于注册这个类,使其可以被使用。
class_addMethod
:用于向类添加一个新的方法。第一个参数是目标类,第二个参数是选择器(SEL),第三个参数是方法的实现(IMP),第四个参数是方法的签名(type encoding)。
KVO crash
KVO的addObserver和removeObserver需要是成对的,如果重复remove则会导致NSRangeException类型的Crash,如果忘记remove则会在观察者释放后再次接收到KVO回调时Crash。
苹果官方推荐的方式是,在init的时候进行addObserver,在dealloc时removeObserver,这样可以保证add和remove是成对出现的,是一种比较理想的使用方式。
1、注册观察
2、实现回调方法
3、移除观察
KVO举例以及注意事项
//被观察者 StockData.m
#import "StockData.h"
@interface StockData()
@property(nonatomic, strong)NSString *stockName;
@property(nonatomic, strong)NSString *price;
@end//观察者 SLVKVOController.m
#import "SLVKVOController.h"
#import "StockData.h"- (void)viewDidLoad {[super viewDidLoad];[self.stockData setValue:@"searph" forKey:@"stockName"];[self.stockData setValue:@"10.0" forKey:@"price"];[self.stockData addObserver:self forKeyPath:@"price" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:SLVKVOContext];
}-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {if(context == SLVKVOContext && object == self.stockData && [keyPath isEqualToString:@"price"]) {NSString * oldValue = [change objectForKey:NSKeyValueChangeOldKey];NSString * newValue = [change objectForKey:NSKeyValueChangeNewKey];self.myLabel.text = [NSString stringWithFormat:@"oldValue:%@ , newValue:%@",oldValue,newValue];} else {[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];}
}-(void)dealloc {[self.stockData removeObserver:self forKeyPath:@"price" context:SLVKVOContext];
}
KVO常见crash及防护方案
KVO常见crash类型:
1.不能对不存在的属性进行kvo观测,否则会报crash:uncaught exception 'NSUnknownKeyException', reason: '[<StockData 0x600000203d50> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key stockName.'
2. 订阅者必须写observeValueForKeyPath:ofObject:change:context:
方法,否则crash。
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '<SLVKVOController: 0x7f811372ff70>: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
3.移除观察,超过addObserver的次数就会 crash:Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <SLVKVOController 0x7ff8e8703100> for the key path "price" from <StockData 0x60800003d000> because it is not registered as an observer.'
KVO crash解决方案:
首先为 NSObject 建立一个分类,利用 Method Swizzling,实现自定义的 BMP_addObserver:forKeyPath:options:context:
、BMP_removeObserver:forKeyPath:
、BMP_removeObserver:forKeyPath:context:
、BMPKVO_dealloc
方法,用来替换系统原生的添加移除观察者方法的实现。
然后在观察者和被观察者之间建立一个 KVODelegate 对象,两者之间通过 KVODelegate 对象 建立联系。然后在添加和移除操作时,在自定义的方法交换内部将 KVO 的相关信息例如 observer、keyPath、options、context 保存为 KVOInfo 对象,并添加到 KVODelegate 对象 中对应 的 关系哈希表 中,对应原有的添加观察者。
关系哈希表的数据结构:{keypath : [KVOInfo 对象1, KVOInfo 对象2, … ]}
在添加和移除操作的时候,利用 KVODelegate 对象 做转发,把真正的观察者变为 KVODelegate 对象,而当被观察者的特定属性发生了改变,再由 KVODelegate 对象 分发到原有的观察者上。
那么,BayMax 系统是如何避免 KVO 崩溃的呢?
添加观察者时:通过关系哈希表判断是否重复添加,只添加一次。
移除观察者时:通过关系哈希表是否已经进行过移除操作,避免多次移除。
观察键值改变时:同样通过关系哈希表判断,将改变操作分发到原有的观察者上。
另外,为了避免被观察者提前被释放,被观察者在 dealloc 时仍然注册着 KVO 导致崩溃。BayMax 系统还利用 Method Swizzling 实现了自定义的 dealloc,在系统 dealloc 调用之前,将多余的观察者移除掉。
KVC造成的crash
场景1:key 不存在
防护方法:进行 KVC Crash 防护,我们就需要重写 setValue: forUndefinedKey:
方法和 valueForUndefinedKey:
方法。重写这两个方法之后,就可以防护key不存在的情况了。
场景2:key为nil
**防护方法:**可以利用 Method Swizzling
方法,在 NSObject 的分类中将setValue:forKey:
和自定义的 ysc_setValue:forKey:
进行方法交换。然后在自定义的方法中,添加对 key 为 nil 这种类型的判断。
Person * person = [[Person alloc] init];
[person setValue:nil forKey:@“name”];
当value为nil的时候不会Crash.
NSTimer导致的Crash
NSTimer、CADisplayLink会对target产生强引用,如果target又对它们产生强引用,那么就会引发循环引用。
先来看看timer最常用的写法
@interface TimerViewController ()
@property (nonatomic, strong) NSTimer *timer;
@end
@implementation TimerViewController
- (void)viewDidLoad {[super viewDidLoad];self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerRun) userInfo:nil repeats:YES];[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
}
- (void)timerRun {NSLog(@"%s", __func__);
}
- (void)dealloc {[self.timer invalidate];NSLog(@"%s", __func__);
}
@end
循环引用了
解决方案1:使用weakSelf (这里使用的是timer的block方法)
- (void)viewDidLoad {[super viewDidLoad];__weak typeof(self) weakSelf = self;self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {[weakSelf timerRun];}];
}
解决方案2:加入了一个中间代理对象LJProxy,timer的target不直接是TimerViewController,而是持有LJProxy实例,让LJProxy实例来弱引用TimerViewController,timer强引用LJProxy实例. 而且代理类里面要重写消息转发方法去处理一下,要不然消息传递会找不到方法导致崩溃。
LJProxy可以继承自NSObject,也可以继承自NSProxy,但是内部代码处理会有所不同。
如果继承自NSProxy,会实现下面的方法,消息转发方法需要实现这两个。
如果继承自NSObject,会实现下面的方法,消息转发方法需要实现这一个即可。
@interface LJProxy : NSObject
+ (instancetype) proxyWithTarget:(id)target;
@property (weak, nonatomic) id target;
@end
@implementation LJProxy
+ (instancetype) proxyWithTarget:(id)target
{LJProxy *proxy = [[LJProxy alloc] init];proxy.target = target;return proxy;
}- (id)forwardingTargetForSelector:(SEL)aSelector
{return self.target;//如果当前对象没有实现这个方法,系统会到这个方法里来找实现对象。
}
@end
- (void)viewDidLoad {[super viewDidLoad];// 这里的target发生了变化self.timer = [NSTimer timerWithTimeInterval:1.0 target:[LJProxy proxyWithTarget:self] selector:@selector(timerRun) userInfo:nil repeats:YES];[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
}
由于NSProxy专门用来做消息转发的,效率高,因为这个内部直接去消息转发,调用methodSignature…,如果继承自NSObject,里面调用会先去父类里面搜索,没有的话才会去做消息转发。
所以这里建议做代理类的时候直接继承自NSProxy的就可以,这样子是最好的。
解决方案3: 及时的把timer销毁,即:调用 [self.timer inbalidate]; 例如 识别到当前控制器返回按钮点击的时候 等等。 (该方法不推荐,代码混乱,不具有统一性。)
解决方案4:「出自 高性能iOS开发 一书」。
这里的间接层和 方案2类似,但是 只不过是在间接层里边进行 timer的创建以及timer的销毁。
控制器直接调用间接层并传入self「后边会赋给delegate<weak引用>」和selector。 间接层会创建timer并倒计时处理事件。 处理事件之后会 通过delegate 调用selector. [self.delegate performSelector:@selector(self.selector) withObject:
想要传的值];
这里我觉得方案4是最容易理解的。也是最容易实施的。
方案4代码:
野指针
- 野指针就是指向一个被释放或者被回收的对象,但是指向该对象的指针没有任何修改,以致于该指针让指向已经回收后的内存地址。
- 其中访问野指针是没有问题的,使用野指针的时候会出现Crash,样例如下:
这是网友总结的,有兴趣的可以看下:www.jianshu.com/p/9fd4dc046… 本人,也就是看看乐呵,其原理啥的,见仁见智吧。开发行业太j8难了!
Watch Dog超时造成的crash
这种崩溃通常比较容易分辨,因为错误码是固定的0x8badf00d
。(程序员也有幽默的一面,他们把它读作Ate Bad Food。)在iOS上,它经常出现在执行一个同步网络调用而阻塞主线程的情况。因此,永远不要进行同步网络调用。
其他crash待补充
参考文章:
crash收集:https://sentry.io/for/ios/
crash日志分析:https://developer.volcengine.com/articles/7062608853434630152
方法找不到解决方案:https://neyoufan.github.io/2017/01/13/ios/BayMax_HTSafetyGuard/
消息转发机制以及避免崩溃方案:https://blog.csdn.net/mumubumaopao/article/details/108113405
kvo crash解决方案:https://juejin.cn/post/6844903927469588488
https://github.com/itcharge/YSC-Avoid-Crash
kvc 防护:https://blog.csdn.net/lianai911/article/details/103400862
NSTimer循环引用问题处理:https://www.jianshu.com/p/d4589134358a
野指针定位:https://www.jianshu.com/p/9fd4dc046046?utm_source=oschina-app
相关文章:

一文读懂iOS中的Crash捕获、分析以及防治
Crash系统性总结 Crash捕获与分析Crash收集符号化分析 Crash类别以及解法分析子线程访问UI而导致的崩溃unrecognized selector send to instance xxxKVO crashKVC造成的crashNSTimer导致的Crash野指针Watch Dog超时造成的crash其他crash待补充 参考文章: 对于iOS端开…...

代码随想录刷题day11|(链表篇)206.翻转链表
目录 一、链表理论基础 二、翻转链表思路 双指针解法 递归解法 三、相关算法题目 四、总结 一、链表理论基础 代码随想录 (programmercarl.com) 二、翻转链表思路 两种方法:双指针解法和递归解法 双指针解法 首先定义一个指针curr,初始化为原…...

【STM32-学习笔记-8-】I2C通信
文章目录 I2C通信Ⅰ、硬件电路Ⅱ、IIC时序基本单元① 起始条件② 终止条件③ 发送一个字节④ 接收一个字节⑤ 发送应答⑥ 接收应答 Ⅲ、IIC时序① 指定地址写② 当前地址读③ 指定地址读 Ⅳ、MPU6050---6轴姿态传感器(软件I2C)1、模块内部电路2、寄存器地…...
2025年1月17日(点亮三色LED)
系统信息: Raspberry Pi Zero 2W 系统版本: 2024-10-22-raspios-bullseye-armhf Python 版本:Python 3.9.2 已安装 pip3 支持拍摄 1080p 30 (1092*1080), 720p 60 (1280*720), 60/90 (640*480) 已安装 vim 已安装 git 学习目标:…...

ASP .NET Core 学习 (.NET 9)- 创建 API项目,并配置Swagger及API 分组或版本
本系列为个人学习 ASP .NET Core学习全过程记录,基于.NET 9 和 VS2022 ,实现前后端分离项目基础框架搭建和部署,以简单、易理解为主,注重页面美观度和后台代码简洁明了,可能不会使用过多的高级语法和扩展,后…...

mysql-5.7.18保姆级详细安装教程
本文主要讲解如何安装mysql-5.7.18数据库: 将绿色版安装包mysql-5.7.18-winx64解压后目录中内容如下图,该例是安装在D盘根目录。 在mysql安装目录中新建my.ini文件,文件内容及各配置项内容如下图,需要先将配置项【skip-grant-tab…...

RK3588平台开发系列讲解(NPU篇)NPU 驱动的组成
文章目录 一、NPU 驱动组成二、查询 NPU 驱动版本三、查询 rknn_server 版本四、查询 librknn_runtime 版本沉淀、分享、成长,让自己和他人都能有所收获!😄 一、NPU 驱动组成 NPU 驱动版本、rknn_server 版本、librknn_runtime 版本以及 RKNN Toolkit 版本的对应关系尤为重…...
ESP32学习笔记_FreeRTOS(6)——Event and Notification
摘要(From AI): 这篇博客详细介绍了 FreeRTOS 中的事件组和任务通知机制,讲解了事件组如何通过位操作实现任务间的同步与通信,以及任务如何通过通知机制进行阻塞解除和数据传递。博客提供了多个代码示例,展示了如何使用事件组和任务通知在多任…...
力扣-数组-350 两个数组的交集Ⅱ
解析 与刚刚的《两个数组的交集》一样,只是这道题允许重复,将上一题的set去除即可。 代码 class Solution { public:vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {vector<int> res;int index1 …...
云原生第二次练习
1.判断192.168.1.0/24网络中,当前在线的ip有哪些,并编写脚本打印出来。 #!/bin/bash for ip in $(seq 1 254); doping -c 1 -W 1 "192.168.1.$ip" > /dev/null 2>&1if [ $? -eq 0 ]; thenecho "192.168.1.$ip is online&qu…...

SpringMVC复习笔记
文章目录 SpringMVC 概念和基本使用SpringMVC 简介SpringMVC 核心组件和调用流程SpringMVC 基本使用第一步:导入依赖第二步:Controller 层开发第三步:SpringMVC 配置类配置核心组件第四步:SpringMVC 环境搭建第五步:部…...

前端小案例——网页井字棋
前言:我们在学习完了HTML、CSS和JavaScript之后,就会想着使用这三个东西去做一些小案例,不过又没有什么好的案例让我们去练手,本篇文章就提供里一个案例——网页井字棋。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可…...

ComfyUI-PromptOptimizer:文生图提示优化节点
ComfyUI-PromptOptimizer 是 ComfyUI 的一个自定义节点,旨在优化文本转图像模型的提示。它将用户输入的提示转换为更详细、更多样化、更生动的描述,使其更适合生成高质量的图像。无需本地模型。 1、功能 提示优化:优化用户输入的提示以生成…...
AudioGPT全新的 音频内容理解与生成系统
AudioGPT全新的 音频内容理解与生成系统 ChatGPT、GPT-4等大型语言模型 (LLM) 在语言理解、生成、交互和推理方面表现出的非凡能力,引起了学界和业界的极大关注,也让人们看到了LLM在构建通用人工智能 (AGI) 系统方面的潜力。 现有的GPT模型具有极高的语言生成能力,是目前最…...

thinkphp6 + redis实现大数据导出excel超时或内存溢出问题解决方案
redis下载安装(window版本) 参考地址:https://blog.csdn.net/Ci1693840306/article/details/144214215 php安装redis扩展 参考链接:https://blog.csdn.net/jianchenn/article/details/106144313 解决思路:࿰…...

Hexo + NexT + Github搭建个人博客
文章目录 一、 安装二、配置相关项NexT config更新主题主题样式本地实时预览常用命令 三、主题设置1.侧边栏2.页脚3.帖子发布字数统计 4.自定义自定义页面Hexo 的默认页面自定义 404 页自定义样式 5.杂项搜索服务 四、第三方插件NexT 自带插件评论系统阅读和访问人数统计 五、部…...
使用Sum计算Loss和解决梯度累积(Gradient Accumulation)的Bug
使用Sum计算Loss和解决梯度累积的Bug 学习 https://unsloth.ai/blog/gradient:Bugs in LLM Training - Gradient Accumulation Fix 这篇文章的记录。 在深度学习训练过程中,尤其是在大批量(large batch)训练中,如何高…...
基于本地消息表实现分布式事务
假设我们有一个电商系统,包含订单服务和库存服务。当用户下单时,需要在订单服务中创建订单,同时在库存服务中扣减库存。这是一个典型的分布式事务场景,我们需要保证这两个操作要么都成功,要么都失败,以保证数据的最终一致性。 项目结构: 订单服务(Order Service)库存服务(Inv…...

Web3与加密技术的结合:增强个人隐私保护的未来趋势
随着互联网的快速发展,个人隐私和数据安全问题越来越受到关注。Web3作为新一代互联网架构,凭借其去中心化的特性,为个人隐私保护提供了全新的解决方案。而加密技术则是Web3的重要组成部分,进一步增强了隐私保护的能力。本文将探讨…...

广播网络实验
1 实验内容 1、构建星性拓扑下的广播网络,实现hub各端口的数据广播,验证网络的连通性并测试网络效率 2、构建环形拓扑网络,验证该拓扑下结点广播会产生数据包环路 2 实验流程与结果分析 2.1 实验环境 ubuntu、mininet、xterm、wireshark、iperf 2.2 实验方案与结果分析…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...

给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...

spring Security对RBAC及其ABAC的支持使用
RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型,它将权限分配给角色,再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...
【实施指南】Android客户端HTTPS双向认证实施指南
🔐 一、所需准备材料 证书文件(6类核心文件) 类型 格式 作用 Android端要求 CA根证书 .crt/.pem 验证服务器/客户端证书合法性 需预置到Android信任库 服务器证书 .crt 服务器身份证明 客户端需持有以验证服务器 客户端证书 .crt 客户端身份…...