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

百度APP iOS端磁盘优化实践(上)

01 概览

在APP的开发中,磁盘管理已成为不可忽视的部分。随着功能的复杂化和数据量的快速增长,如何高效管理磁盘空间直接关系到用户体验和APP性能。本文将结合磁盘管理的实践经验,详细介绍iOS沙盒环境下的文件存储规范,探讨业务缓存、用户资产及系统缓存的清理策略。同时,分享自动清理与手动清理相结合的机制,展示如何在不同触发条件下合理执行磁盘清理。文章使用文心一言辅助编写。

02 磁盘系统介绍

2.1 ios沙盒系统

沙盒机制是iOS系统中的一种安全体系。每个iOS程序都有一个独立的文件系统,而且只能在对应的文件系统中进行操作,此区域被称之为沙盒(SandBox)。APP中所有文件都保存在此,如文本文件、图片、图标、媒体资源、Mach-O等。主要包含4个目录 MyApp.app、Documents、Library、tmp。

MyApp.app目录包含应用程序及其所有资源,即.ipa安装包解压后的.app内容,仅支持只读访问。Documents目录用于存储用户生成的内容,可以通过文件共享提供给用户,并由iCloud备份。Library目录则用于存放非用户数据文件的顶级目录,通常包含几个标准子目录,如Application Support和Caches,并由iCloud(Caches除外)备份。而tmp目录用于写入临时文件,这些文件不需要在应用程序启动之间保留,当不再需要时应由应用程序删除,且不被iCloud备份。 图片

2.2 获取目录API

NSSearchPathForDirectoriesInDomains()函数用于查找目录,返回指定范围内的指定名称的目录的路径集合。

// 获取沙盒主目录路径
NSString *homeDir = NSHomeDirectory();
// 获取Documents目录路径
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
// 获取Library的目录路径
NSString *libDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
// 获取Caches目录路径
NSString *cachesDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
// 获取tmp目录路径
NSString *tmpDir =  NSTemporaryDirectory();
//应用程序程序包的路径
[[NSBundle mainBundle] bundlePath];

2.3 文件存储规范

iOS项目开发中,文件管理至关重要。我们结合Apple官方指导与项目实践,形成了一套详细的文件使用规范。这些规范不仅遵循了iCloud备份策略,还充分考虑了文件类型特性和业务需求。结合业务使用场景,能够更准确地判断哪些文件是临时数据、哪些是用户生成的重要文件,为后续进行磁盘监控和清理提供了便利。

MyApp.app:

  • 用途:应用程序的包,此目录包含应用程序Mach-O可执行文件及其所有资源,iCloud默认不备份此目录。

  • 使用规范:

  • 开发者不能写入此目录。写入此目录会影响签名导致APP无法启动。

  • 开发者可以获得对应用程序包中存储的任何资源的只读访问权限。

Documents:

  • 用途:存储用户生成的内容的顶级目录,iCloud默认备份此目录。

  • 使用规范:

  • 该目录应仅包含开发者可能希望向用户公开的文件。

Documents/Inbox:

  • 用途:存储通过外部共享方式导入到APP的文件,iCloud默认备份此目录。

  • 使用规范:

  • 开发者不应自行在此目录下创建文件。

  • 开发者可读可删,将此目录中文件移出后再编辑。

Library:

  • 用途:存储非用户数据文件的顶级目录,iCloud默认备份此目录(Caches除外)。

  • 使用规范:

  • 可以创建自定义子目录,用于保存不向用户公开的任何文件。

  • 各业务方需要创建业务相关的子目录存储数据。

Library/Caches:

  • 用途:用于存放再次下载或重新生成的数据,iCloud不会备份此目录。

  • 使用规范:

  • 存放可以重新下载或生成的数据,如图片缓存文件、临时文件等。

  • 系统在系统储存不足时可能会删除该目录以释放磁盘空间。

  • 各业务数据缓存文件应放在/Library/Caches的子目录下,方便管理。

Library/Application Support:

  • 用途:用于存储APP配置和数据文件,iCloud默认备份此目录。

  • 使用规范:

  • 存放程序配置和数据文件,如配置文件、模板等,不应存放缓存文件。

  • 避免在此目录下创建不必要的文件,以减少备份时间和存储空间占用。

Library/Preferences:

  • 用途:用于存储应用程序的首选项值,iCloud默认备份此目录。

  • 存放建议:

  • 开发者不应自行在此目录下创建文件。

  • NSUserDefaults或CFPreferences来获取和设置APP的首选项值。

tmp:

  • 用途:用于存放临时数据,iCloud不会备份此目录。

  • 存放建议:

  • 仅存放临时数据,开发者应在使用完这些文件后及时删除。

  • 系统会清除目录下文件,应用程序终止后可能不存在。

  • 不推荐业务方使用此目录存放重要数据。

2.3.1 文件命名规则

2.3.2 iCloud备份与恢复

iCloud会默认同步Documents、Library 目录下的文件(Caches子目录除外),用户在设置中开启iCloud同步功能,同步APP数据时会上传所有沙盒数据。如果没有按照规范创建目录,会导致大量缓存文件被上传到iCloud,严重占用用户iCloud 空间,严重时甚至会被Apple官方警告。如果将数据写入这些目录,但是又不希望同步到iCloud,iOS提供了API可以排除默认同步的目录,需要开发者在创建文件时主动设置。

由于无法查看iCloud备份的沙盒文件内容,使用Mac本地备份的方式模拟iCloud备份行为。iPhone连接Mac,打开访达,选择将数据备份到Mac,恢复备份。测试发现Document文件夹下的子目录和子文件,属性被设置为NSURLIsExcludedFromBackupKey,恢复备份后不在,符合预期。不过用电脑备份和iCloud有个差异,Library/Caches下的文件也会被备份。

注意:此方法对于在App Store的应用,即使在恢复备份前将APP删除,也会在恢复备份后自动下载APP,并且恢复数据。对于不在App Store的应用,比如我们的测试DemoAPP,在恢复备份前不要删除DemoAPP,否则无法查看效果。因为无法下载不在Appstore的APP安装包,所以备份内容也无法还原。测试APP在恢复备份后重新安装就可以查看完整的沙盒文件。

  • API设置对于性能的损耗:
    iPhone12 :总计 14780 个文件,总计耗时 1124.180794 ms,平均每次耗时0.076ms
    iPhone6p:总计 11650 个文件,总计耗时 3730.319023 ms,平均每次耗时0.32ms

  • NSURLIsExcludedFromBackupKey不需要每次使用目录或文件时都调用,只需要设置一次即可。设置属性后,该目录下所有子目录和文件都不会同步到iCloud,但是子目录和文件的属性NSURLIsExcludedFromBackupKey依旧为NO。

  • 创建文件URL时需要调用[NSURL fileURLWithPath:] ,然后获取和设置 NSURLIsExcludedFromBackupKey。

NSURL *pathurl = [NSURL fileURLWithPath:path];
BOOL success = NO;
success = [pathurl setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:nil];

2.4 磁盘大小计算方式

在系统中可以查看一个APP占用磁盘大小,iPhone 存储空间通常由APP 大小文稿数据大小组成。getResourceValue:forKey:error: 或者attributesOfItemAtPath:error: 直接获取的是“文件大小”,不是文件占用的磁盘物理空间。计算文件的磁盘占用大小需要通过磁盘块的方式。标准头文件stat.h的st_blocks可以获取到块的数量,st_blocks * 512计算出文件的磁盘占用大小。 扇区(block)是磁盘读写的最小物理单位。之所以扇区大小是 512 字节,这种做法源自较早的磁盘扇区大小标准。这种设计是基于早期磁盘技术的物理限制,尽管现代硬盘的物理扇区大小可能已经增加到了 4096 字节,但 512 字节的逻辑块大小在许多文件系统和操作系统的接口中仍然被保留。iOS系统存储单位为十进制,且保留有效小数机制为四舍五入。

图片

+ (unsigned long long)fileSizeOnDisk:(nonnull NSString *)filePath {
struct stat fileStat;
int res = stat([filePath cStringUsingEncoding:NSUTF8StringEncoding], &fileStat);
if (-1 == res) {
return 0;}
long long fileSize = fileStat.st_blocks / 8 * fileStat.st_blksize;
return fileSize ;
}

2.5 APP包大小

APP包大小是手机沙盒的重要组成部分,包体积直接影响用户转化率。用户可以在iPhone存储空间设置中查看设备上各APP大小文稿与数据大小。

百度APP在iOS17系统的iPhone存储空间设置中,系统不显示APP大小,只有文稿与数据大小。经过调研,发现在iOS17系统APP使用ODR功能后,会导致APP大小被计算到了iOS系统数据。推测是iOS17系统BUG,iOS18系统已经恢复正常。

03 磁盘清理

在综合性 APP 的开发中,多个业务方共同使用沙盒空间,磁盘缓存的管理面临诸多挑战。每个业务方对磁盘使用的整体感知较弱,难以准确评估自身的空间占用大小是否合理,磁盘占用很容易出现无限增长的问题。同时,不同业务方可能独立设计了缓存清理策略,缺乏统一规范,难以满足统一管理的需求。

为解决这些问题,从全局出发设计一套高效的磁盘清理方案。这套方案结合自动清理手动清理的机制,对磁盘使用进行统一管理。自动清理负责监测磁盘占用情况,通过设置清理阈值和判断条件,在空间不足时通知或触发业务方执行清理操作。清理范围以业务缓存为主,支持按业务个性化配置清理规则,确保管理灵活高效。手动清理则面向用户,提供简单直观的操作界面,允许用户自主选择清理范围,管理下载文件等数据,从而平衡自动化与用户自主权。

此外,在iOS平台上,系统本身具有定期清理临时文件和部分缓存内容的机制。设计磁盘清理方案时,合理利用这些系统行为,同时避免依赖其不可控性。通过以上策略,可以在确保磁盘空间高效管理的同时,最大限度地保障用户体验,为综合性APP提供可靠的存储管理支持。

图片

3.1 自助清理

3.1.1 业务限额

为了管理和规范业务方对磁盘的使用,根据业务的使用场景和必要性,评估每个业务方需要使用的磁盘空间,每一个业务占用的缓存大小会被分配一个限额,业务需要保证自身缓存占用限制在限额以下。业务方从磁盘组件获取业务自身限额值,保证自身缓存占用在限额以内;而磁盘组件会从磁盘剩余大小、APP已使用大小、业务使用频率等条件实时分配合适的限额给各个业务方。

3.1.2 磁盘等级

根据设备的剩余磁盘空间与应用程序所占用的磁盘空间大小,我们可以将磁盘状态细分 为Normal、Warning、以及Critical三个等级,每个等级都反映了用户磁盘空间的使用状况。当处于Normal时,表明用户的手机尚拥有充裕的剩余空间,且APP所占用的磁盘空间相对较小。在此情境下,我们可以适度增加分配给各个业务方的限额,使得业务方有机会缓存更多内容,从而实现以空间换取时间的性能优化,进一步提升用户体验。

然而,一旦进入Critical等级,意味着用户的手机剩余空间已所剩无几,或是 APP占用了过多的空间。这种情况下,用户的手机很容易出现卡顿现象,甚至面临存储空间告急的困境。更为严重的是,这可能会迫使用户做出卸载APP的极端选择。因此,在Critical等级下,我们必须对业务方使用磁盘空间的行为进行严格限制,并强制要求业务方彻底清理任何不必要的缓存,以确保用户的手机能够维持基本的运行需求。

3.1.3 状态检测

磁盘状态检测是磁盘组件的核心功能,检测 APP 运行期间设备磁盘的使用情况,判断是否需要触发自动清理操作。APP 启动后周期性的执行磁盘使用状态检查,自动触发相应的清理机制。通过不同条件触发磁盘清理,以优化应用性能并提升用户体验。为此设计了多种触发清理机制的机制,确保不同情况下都能触发清理操作:

等级:根据磁盘使用等级的变化来决定是否清理;

版本:如果应用版本发生升级,会强制业务方执行一次清理。 时间:根据磁盘等级设定兜底的定期清理间隔。

3.1.4 触发清理

业务方在各自的组件中注册磁盘清理回调protocol,不同的业务模块能够响应磁盘等级变化并进行相应的处理,实现了磁盘清理服务与业务模块之间的解耦合。业务在收到磁盘状态等级变化时,重新查询业务限额,并调整自身策略,管控和优化自身占用缓存,保证保证自身缓存占用在限额以内。

磁盘组件会逐一调用各业务模块注册的清理回调,通过protocol抽象各模块的清理逻辑,实现了对多业务模块的统一管理和扩展性支持。结合信号量机制实现同步等待,确保每个模块的清理任务按顺序完成。所有清理任务完成后,磁盘组件会触发全局回调进行状态更新。

3.2 手动清理

手动清理沿用自动清理的设计思路,业务方实现清理协议,注册到磁盘组件,然后由磁盘组件统一管控。和自动清理不同的是,手动清理提供了一个用户可视化页面,并且详细列出可清理的内容供用户选择。支持深度清理,在大多数情况下,业务逻辑要求清除所有缓存数据。除此之外,还另外添加了用户资产管理功能。

3.2.1 用户资产管理

用户资产定义为用户自主下载并保存的各类文件,包括但不限于视频、图片、安装包、PDF 文档、Excel 表格以及 Word 文件等。百度APP是一个综合性 APP ,兼具浏览器的功能。用户在浏览网页的过程中,会下载各种内容。特别是在 iOS 设备上,用户还可能会下载一些无法安装的安装包。尽管这些文件是用户主动下载的,但实际上它们可能并无实际用途,且长期占用用户的磁盘空间。用户资产管理模块可以帮助用户轻松地识别并清理那些不再需要或无效的用户资产,从而有效释放存储空间,提升设备的整体性能。

3.3 系统缓存清理

3.3.1 tmp目录

iOS的tmp目录是系统缓存目录,Apple官方文档的说明是使用此目录写入不需要在APP启动之间保留的临时文件。当不再需要文件时,APP应从该目录中删除这些文件,系统也可能会在APP未使用时清除此目录。实际使用过程中发现,依赖系统清理该文件夹具有不可控性,因此需要对该文件夹进行清理,tmp目录主要包含以下文件:

图片

1. 下载缓存文件

NSURLSessionDownloadTask进行文件下载的过程中,系统为了确保数据的完整性和安全性,会在文件下载完成前,将其暂时保存在以.tmp为扩展名的临时文件中。这些临时文件的命名遵循CFNetworkDownload_xxxx.tmp的格式,其中CFNetworkDownload_是固定的前缀,而后续的xxxx部分则根据每个下载任务的不同而有所区别。

NSURLSessionConfiguration配置会导致这些临时文件被存储在沙盒的不同位置。defaultSessionConfiguration创建的下载任务,其临时文件会被保存在沙盒的tmp文件夹内。backgroundSessionConfiguration,临时文件的存储位置则有所不同。在后台下载模式下,为确保即使应用进入后台,下载任务也能继续进行。这些文件会存放在Library/Caches/com.apple.nsurlsessiond/Downloads/<bundle_id>。

然而,需要注意的是,沙盒中的tmp文件夹主要用于存放临时文件,这些文件在磁盘空间不足时可能会被系统自动清理。因此,如果下载任务被取消,且临时文件没有被及时移动到其他安全的存储位置,它们可能会因为 tmp 文件夹的清理机制而被删除。这将导致用户无法从上次中断的位置继续下载,即无法实现断点续传。

2. tmp目录清理

统计tmp目录下可以被清理的文件,可以选择全部清理,也可以按照业务实际应用场景,选择过期清理等方案。

/// 统计 /tmp 目录下需要清理的缓存文件
+ (NSArray<NSURL *> *)calculateTmpSysCache {
// 匹配文件名的模式
NSArray *regexPatterns = @[@"CFNetworkDownload", @"WKWebFileUpload", @"NSIRD_", @"正在存储文稿"];
NSString *tmpPath = NSTemporaryDirectory();
if (!tmpPath) return @[];
NSMutableArray *tmpNeedCleanCache = [NSMutableArray array];
NSFileManager *fileManager = [NSFileManager defaultManager];
for (NSString *fileName in [fileManager enumeratorAtPath:tmpPath]) {
NSString *fullPath = [tmpPath stringByAppendingPathComponent:fileName];
NSURL *fileURL = [NSURL fileURLWithPath:fullPath];
// 检查是否符合清理条件
if ([self shouldCleanFile:fullPath patterns:regexPatterns]) {[tmpNeedCleanCache addObject:fileURL];}}
return tmpNeedCleanCache;
}

3.3.2 WKWebView清理

WKWebView是iOS和MacOS上用于加载和展示网页内容的控件,它利用了多种缓存机制来提升加载速度和用户体验。以下是WKWebView缓存产生的几个主要原因:

  • 资源缓存(HTTP 缓存):WKWebView会缓存通过网络请求加载的网页资源(如 HTML、CSS、JS、图片等),以减少重复下载,提升网页的加载速度。这些资源通常存储在本地磁盘上。

  • Cookie 缓存:网站使用的Cookie会被WKWebView缓存,用于维护会话状态、用户登录信息等。

  • Session Storage和Local Storage:WKWebView支持HTML5的sessionStorage和localStorage,用于本地存储网站数据,以便后续访问时能够直接从缓存中读取,而无需重新下载。

  • WebKit Cache(WebKit内部缓存):WebKit内部有一套复杂的缓存系统,用于管理各种网页资源、脚本、图像等的缓存。WKWebView依赖这些机制来加速网页内容加载。

  • Service Workers和离线缓存:一些网页使用Service Workers来实现离线功能或加速加载特定资源,WKWebView会缓存这些资源,以便在后续使用时能够从本地获取。

1. WKWebView缓存清理方法

清理指定数据类型的缓存:通过WKWebsiteDataStore可以清理特定类型的缓存,比如Cookies、缓存、localStorage等。该方法可以清理指定类型的缓存数据,并支持自定义时间范围。以下代码展示了如何清理WKWebView的缓存数据:

// 获取所有类型的缓存
NSSet *websiteDataTypes = [NSSet setWithArray:@[
WKWebsiteDataTypeCookies,                 // Cookie
WKWebsiteDataTypeLocalStorage,            // localStorage
WKWebsiteDataTypeIndexedDBDatabases,      // IndexedDB
WKWebsiteDataTypeWebSQLDatabases,         // WebSQL
WKWebsiteDataTypeFetchCache,              // Fetch API
WKWebsiteDataTypeDiskCache,               // 磁盘缓存
WKWebsiteDataTypeMemoryCache,             // 内存缓存
WKWebsiteDataTypeOfflineWebApplicationCache // 离线应用缓存
]];
// 获取过去时间的日期,比如一个月前
NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
// 清理特定类型的数据
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypesmodifiedSince:dateFromcompletionHandler:^{
NSLog(@"清理缓存完成");
}];

清理指定域名的数据如果只想清理某个特定网站的缓存,可以通过查询 WKWebsiteDataRecord 来实现:

// 获取指定域名的数据
[[WKWebsiteDataStore defaultDataStore] fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes]completionHandler:^(NSArray<WKWebsiteDataRecord *> *records) {
for (WKWebsiteDataRecord *record in records) {
if ([record.displayName containsString:@"example.com"]) { // 指定域名[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:record.dataTypesforDataRecords:@[record]completionHandler:^{
NSLog(@"清理 %@ 的缓存完成", record.displayName);}];}}
}];

2. WKWebView清理实践

在WKWebView的清理实践中,先计算NetworkCache文件夹的大小,一旦超过设定的限制即会触发清理流程。在清理过程中,为了保证缓存带来的性能优化和磁盘空间占用达到平衡,我们并没有选择全部删WKWebView的所有缓存,而是按照文件的修改日期进行排序,确保最老的文件在后续的清理中会被优先处理。

+ (void)cleanUpWKWebViewCacheWithLimit {dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC * SOME_DELAY)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 设置缓存上限为10MB
unsigned long long cacheLimit = SOME_CACHE_LIMIT;
// 获取WebKit网络缓存目录
NSString *networkCachePath = [NSHomeDirectory() stringByAppendingPathComponent:@"/Library/Caches/WebKit/NetworkCache"];
// 计算缓存总大小
unsigned long long totalSize = [self calculateDirectorySize:networkCachePath];
if (totalSize < cacheDiskLimit) {
return;}
// 获取Records子目录路径
NSString *networkCacheRecordsPath = [networkCachePath stringByAppendingPathComponent:@"/Version 16/Records"];
// 获取所有文件列表,安时间排序判断需要清理的文件
NSArray *filelist = [self listFilesInDirectory:networkCacheRecordsPath];
if (filelist.count == 0) {
return;}
if (@available(iOS 9.0, *)) {
dispatch_async(dispatch_get_main_queue(), ^{
WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
NSSet *dataTypes = [NSSet setWithArray:@[WKWebsiteDataTypeDiskCache]];[dataStore removeDataOfTypes:dataTypes modifiedSince:SOME_DAY_AGO completionHandler:^{
// 清理完成}];});}});
}

3.3.3 dyld缓存清理

dyld是iOS和MacOS系统中的一个关键组件,负责在程序启动时加载和链接动态库(如框架和共享库)。在iOS 13及更早版本中,dyld可能会在tmp目录下创建一些临时缓存文件,用于加速后续的程序启动过程。这些缓存文件包含了动态库的加载信息,使得系统在下次启动相同程序时能够更快地找到并加载所需的库。

然而,iOS13系统之前会在每次APP升级后的首次启动生成一个新的dyld缓存,保存在tmp/com.apple.dyld,并且之前的APP版本的dyld缓存也不会自动删除。该部分缓存会随着版本升级不断累加,需要管理这部分缓存,实际上还有部分用户从 iOS13 系统升级到更高系统,这部分缓存就会一直遗留在用户手机中,高达上百 MB。iOS14 系统则是迁移到了Library/Caches/com.apple.dyld,且系统会自动清理。

/// 清理iOS13系统生成的tmp/com.apple.dyld缓存文件
+ (void)cleanTmpDyld {
NSString *tmpDyldPath = [NSHomeDirectory() stringByAppendingPathComponent:@"tmp/com.apple.dyld"];
if ([[NSFileManager defaultManager] fileExistsAtPath:tmpDyldPath]) {
// 系统版本大于iOS14
if (@available(iOS 14.0, *)) {[PFMDiskSizeUtils cleanupDirectoryAtPath:tmpDyldPath];} else {
// 保留计数限制为 1,保留最新的[PFMDiskSizeUtils cleanDiskCaches:tmpDyldPath reservedCountLimit:1];}}
}

3.3.4 其他系统缓存清理

  • Documents/Inbox是iOS应用接收文件的默认目录,直接清理即可;

  • Library/Preferences目录下属于应用的临时 .plist 文件。这些文件通常由 NSUserDefaults自动生成,有时在写入或更新过程中会生成临时文件。

/// 清除 Documents/Inbox 目录
+ (void)cleanUpInboxPath {
NSString *inboxPath = [NSHomeDirectory() stringByAppendingPathComponent:@"/Documents/Inbox"];
if (inboxPath) {[PFMDiskSizeUtils cleanupDirectoryAtPath:inboxPath];}
}

/// 清除 Library/Preferences/bundleId.plist.xxxx文件
+ (void)cleanUpUserDefaultsTempPlistFiles {
NSString *preferencesPath = [NSHomeDirectory() stringByAppendingPathComponent:@"/Library/Preferences"];
NSDirectoryEnumerator *dirEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:preferencesPath];
NSString *deleteFilePrefix = [[[NSBundle mainBundle] bundleIdentifier] stringByAppendingString:@".plist."];
NSString *file;
while (file = [dirEnumerator nextObject]) {
if ([file hasPrefix:deleteFilePrefix]) {
NSString *deleteFilePath = [preferencesPath stringByAppendingPathComponent:file];
NSDate *fileModifyDate =  [[[NSFileManager defaultManager] attributesOfItemAtPath:deleteFilePath error:nil] objectForKey:NSFileModificationDate ];[PFMDiskSizeUtils cleanupDirectoryAtPath:deleteFilePath];}}
}

04 总结

本篇文章围绕百度APP的磁盘清理问题,从iOS沙盒文件存储机制出发,系统性地阐述了磁盘管理的重要性和技术实现。文章探讨了自动清理与手动清理的结合策略,通过多维度触发机制和灵活的用户交互设计,平衡了系统性能与用户体验。后续我们还会分享磁盘监控和磁盘异常问题治理相关的文章。

————END————

推荐阅读

读友好的缓存淘汰算法

如何定量分析 Llama 3,大模型系统工程师视角的 Transformer 架构

微服务架构革新:百度Jarvis2.0与云原生技术的力量

技术路线速通!用飞桨让京剧人物照片动起来

无需业务改造,一套数据库满足 OLTP 和 OLAP,GaiaDB 发布并行查询能力

相关文章:

百度APP iOS端磁盘优化实践(上)

01 概览 在APP的开发中&#xff0c;磁盘管理已成为不可忽视的部分。随着功能的复杂化和数据量的快速增长&#xff0c;如何高效管理磁盘空间直接关系到用户体验和APP性能。本文将结合磁盘管理的实践经验&#xff0c;详细介绍iOS沙盒环境下的文件存储规范&#xff0c;探讨业务缓…...

人工智能丨基于机器学习的视觉 CV 处理技术

从自动驾驶汽车到面部识别系统&#xff0c;CV无处不在&#xff0c;赋予计算机“看”的能力。无论是图像处理、模式识别&#xff0c;还是视频分析&#xff0c;机器学习都是推动这些技术进步的核心动力。这篇文章将深入探讨基于机器学习的计算机视觉处理技术&#xff0c;包括它的…...

SparX实战:使用SparX实现图像分类任务(一)

摘要 SparX是一种新提出的稀疏跨层连接机制&#xff0c;旨在提升视觉Mamba和Transformer网络的性能。该论文由香港大学的俞益洲教授及其研究团队撰写&#xff0c;并将在AAAI 2025会议上发表。论文的主要目标是解决现有视觉模型在跨层特征聚合方面的不足&#xff0c;尤其是在计…...

vue事件总线(原理、优缺点)

目录 一、原理二、使用方法三、优缺点优点缺点 四、使用注意事项具体代码参考&#xff1a; 一、原理 在Vue中&#xff0c;事件总线&#xff08;Event Bus&#xff09;是一种可实现任意组件间通信的通信方式。 要实现这个功能必须满足两点要求&#xff1a; &#xff08;1&#…...

[c语言日寄]assert函数功能详解

【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋&#xff1a;这是一个专注于C语言刷题的专栏&#xff0c;精选题目&#xff0c;搭配详细题解、拓展算法。从基础语法到复杂算法&#xff0c;题目涉及的知识点全面覆盖&#xff0c;助力你系统提升。无论你是初学者&#xff0c;还是…...

08-Elasticsearch

黑马商城作为一个电商项目&#xff0c;商品的搜索肯定是访问频率最高的页面之一。目前搜索功能是基于数据库的模糊搜索来实现的&#xff0c;存在很多问题。 首先&#xff0c;查询效率较低。 由于数据库模糊查询不走索引&#xff0c;在数据量较大的时候&#xff0c;查询性能很…...

贪心算法-条约游戏II

hello 大家好&#xff01;今天开写一个新章节&#xff0c;每一天一道算法题。让我们一起来学习算法思维吧&#xff01; /*** 计算到达数组最后一个元素的最小跳跃次数* param {number[]} nums - 输入的整数数组* return {number} - 最小跳跃次数*/ function jump(nums) {// 数…...

Hive:内部表和外部表,内外转换

内部表和外部表 内部表示例 给表添加数据 外部表示例 给表添加数据 外部表示例 用location指定表目录位置,那么表的位置在实际指定的位置,但是可以被映射 外部表和内部表的区别 删除表后使用show tables in shao; 已经没有被删除的表,说明元数据已经被删除(mysql里面存放),但是…...

AndroidCompose Navigation导航精通1-基本页面导航与ViewPager

文章目录 前言基本页面导航库依赖导航核心部件简单NavHost实现ViewPagerPager切换逻辑图阐述Pager导航实战前言 在当今的移动应用开发中,导航是用户与应用交互的核心环节。随着 Android Compose 的兴起,它为开发者提供了一种全新的、声明式的方式来构建用户界面,同时也带来…...

基于ESP8266的多功能环境监测与反馈系统开发指南

项目概述 本系统集成了物联网开发板、高精度时钟模块、环境传感器和可视化显示模块&#xff0c;构建了一个智能环境监测与反馈装置。通过ESP8266 NodeMCU作为核心控制器&#xff0c;结合DS3231实时时钟、DHT11温湿度传感器、光敏电阻和OLED显示屏&#xff0c;实现了环境参数的…...

十三先天记

没有一刻&#xff0c;只有当下在我心里。我像星星之间的空间一样空虚。他们是我看到的第一件事&#xff0c;我知道的第一件事。 在接下来的时间里&#xff0c;我意识到我是谁&#xff0c;我是谁。我知道星星在我上方&#xff0c;星球的固体金属体在我脚下。这个支持我的世界是泰…...

JVM垃圾回收器的原理和调优详解!

全文目录&#xff1a; 开篇语前言摘要概述垃圾回收器分类及原理1. Serial 垃圾回收器2. Parallel 垃圾回收器3. CMS 垃圾回收器4. G1 垃圾回收器 源码解析示例代码 使用案例分享案例 1&#xff1a;Web 服务的 GC 调优案例 2&#xff1a;大数据任务的 GC 优化 应用场景案例垃圾回…...

TypeScript 学习 -类型 - 5

类 属性必须初始化 在构造函数中赋值在定义时给一个默认值 子类 子类构造函数必须使用 super() 修饰符 默认是 publicprivate 只能在当前类中被调用protected 只能在当前类 或 子类中被调用readonly 一定要被初始化, 不可以修改static 静态成员 / 静态函数 构造函数 如果被定义…...

Django 项目中使用 MySQL 数据库的完整指南

在现代 Web 开发中,数据库是应用程序的核心组件之一。Django 作为一个强大的 Python Web 框架,默认支持多种数据库后端,包括 SQLite、PostgreSQL 和 MySQL。本文将详细介绍如何在 Django 项目中使用 MySQL 作为数据库,并实现多环境(开发、测试、生产)的配置管理。 ©…...

单片机基础模块学习——PCF8591芯片

一、A/D、D/A模块 A——Analog 模拟信号&#xff1a;连续变化的信号&#xff08;很多传感器原始输出的信号都为此类信号&#xff09;D——Digital 数字信号&#xff1a;只有高电平和低电平两种变化&#xff08;单片机芯片、微控制芯片所能处理的都是数字信号&#xff09; 下面…...

gradle和maven的区别以及怎么选择使用它们

目录 区别 1. 配置方式 2. 依赖管理 3. 构建性能 4. 灵活性和扩展性 5. 多项目构建 如何选择使用 选择 Maven 的场景 选择 Gradle 的场景 区别 1. 配置方式 Maven&#xff1a; 使用基于 XML 的 pom.xml 文件进行配置。所有的项目信息、依赖管理、构建插件等都在这个文…...

【面试】【前端】前端网络面试题总结

一、前端网络面试题总结 网络相关知识是前端开发的核心内容之一&#xff0c;面试中通常会考察协议、网络模型、性能优化及常见网络问题的处理。以下是针对前端网络面试题的总结&#xff1a; &#xff08;一&#xff09;协议森林&#xff08;大话网络协议&#xff09; 网络协议…...

Qt 5.14.2 学习记录 —— 이십일 Qt网络和音频

文章目录 1、UDP带有界面的Udp服务器&#xff08;回显服务器&#xff09; 2、TCP回显服务器 3、HTTP客户端4、音频 和Linux的网络一样&#xff0c;Qt封装了Linux的网络API&#xff0c;即Socket API。网络编程是在应用层写&#xff0c;需要传输层支持&#xff0c;传输层有UDP和T…...

C++小病毒-1.0勒索(更新次数:2)

内容供学习使用,不得转卖,代码复制后请1小时内删除,此代码会危害计算机安全,谨慎操作 在C20环境下,并在虚拟机里运行此代码!&#xff0c;病毒带来后果自负! 使用时请删除在main()里的注释,并修改位置至C:\\(看我代码注释)//可以改成WIN Main() #include <iostream> #i…...

labelimg闪退的解决办法

其实就是你的python版本太高不稳定不支持labelimg 标记时出现闪退 问题原因&#xff1a;python版本过高 解决方案 第一步&#xff1a; 在python3.9以上的版本运行软件会闪退&#xff0c;这个时候我们需要创建一个3.9或者及以下的虚拟环境 conda cr…...

使用脚本执行地理处理工具

确定工具箱的别名,查看当前使用的arcgis的许可级别,确保工具可访问后,即可使用脚本执行工具. 操作方法 1.在arcmap中打开目标地图 2.打开python窗口 3.创建一个变量,引用要裁剪的输入要素类 in_features "<路径>" 4.创建一个变量,引用用于裁剪的图层 cl…...

【数据分享】2014-2025年我国道路数据(免费获取/全国/分省)

道路数据是我们在各项研究中经常使用的数据&#xff01;道路数据虽然很常用&#xff0c;但是却基本没有能下载最近年份道路数据的网站&#xff0c;所以很多人不知道如何获到道路数据。 本次我们给大家分享的是2014-2025年的全国范围的道路数据&#xff01;数据格式为shp矢量格…...

Mybatis-plus 更新 Null 的策略踩坑记

一个bug 在一个管理页面&#xff0c;有一个非必填字段被设置成空了并提交更新&#xff0c;再次打开的时候&#xff0c;发现字段还在&#xff0c;并没有被更新成功。 使用的数据库映射框架是 Mybatis-plus &#xff0c;对于Mybatis 在更新字段的时候会对空进行校验&#xff0c;…...

图解 script 标签中的 async 和 defer 属性

<script> 当浏览器解析到这个标签时&#xff0c;它会立即停止解析HTML文档&#xff0c;转而去加载并执行这个脚本。这意味着如果将<script>放在页面顶部&#xff0c;它可能会延迟整个页面的加载速度&#xff0c;因为浏览器必须等待脚本执行完毕才能继续解析HTML。…...

C++ Lambda 表达式的本质及原理分析

目录 1.引言 2.Lambda 的本质 3.Lambda 的捕获机制的本质 4.捕获方式的实现与底层原理 5.默认捕获的实现原理 6.捕获 this 的机制 7.捕获的限制与注意事项 8.总结 1.引言 C 中的 Lambda 表达式是一种匿名函数&#xff0c;最早在 C11 引入&#xff0c;用于简化函数对象的…...

08.OSPF 特殊区域及其他特性

OSPF 特殊区域及其他特性 一. 前言OSPF的四个特殊区域Stub末梢区域Totally Stub完全末梢区域NSSATotally NSSA完全的NSSA二.Stub 区域和 Totally Stub 区域(1)网络规模变大引发的问题(2)传输区域和末端区域(3)Stub 区域(4)Totally Stub 区域三.NSSA 区域和 Totally NSS…...

计网week1+2

计网 一.概念 1.什么是Internet 节点&#xff1a;主机及其运行的应用程序、路由器、交换机 边&#xff1a;通信链路&#xff0c;接入网链路主机连接到互联网的链路&#xff0c;光纤、网输电缆 协议&#xff1a;对等层的实体之间通信要遵守的标准&#xff0c;规定了语法、语义…...

日志收集Day008

1.zk集群优化 修改zookeeper的堆内存大小&#xff0c;一般情况下&#xff0c;生产环境给到2G足以&#xff0c;如果规模较大可以适当调大到4G。 (1)配置ZK的堆内存 vim /app/softwares/zk/conf/java.env export JAVA_HOME/sortwares/jdk1.8.0_291 export JVMFLAGS"-Xms2…...

使用PC版本剪映制作照片MV

目录 制作MV模板时长调整拖动边缘缩短法分割删除法变速法整体调整法 制作MV 导入音乐 导入歌词 点击歌词 和片头可以修改字体&#xff1a; 还可以给字幕添加动画效果&#xff1a; 导入照片&#xff0c;自动创建照片轨&#xff1a; 修改片头字幕&#xff1a;增加两条字幕轨&…...

性能测试全链路监控模式有哪些?

目录 性能测试全链路监控的模式有哪些呢&#xff1f; 1. 调用链追踪&#xff08;Trace&#xff09; 2. 分布式追踪与日志聚合 3. 实时性能指标采集 4. 资源利用率监控 5. 自动化测试与回滚机制 6. 用户体验质量&#xff08;QoE&#xff09;评估 性能测试中的全链路监控模…...

【吉林乡镇界】面图层shp格式arcgis数据乡镇名称和编码wgs84无偏移内容测评

标题中的“吉林省乡镇界面图层shp格式arcgis数据乡镇名称和编码wgs84无偏移”揭示了这是一个地理信息系统&#xff08;GIS&#xff09;相关的数据集&#xff0c;主要用于描绘吉林省的乡镇边界。这个数据集包含了一系列的文件&#xff0c;它们是ArcGIS软件能够识别和处理的Shape…...

SpringAI 搭建智能体(二):搭建客服系统智能体

在现代人工智能应用中&#xff0c;智能体&#xff08;Agent&#xff09; 是一个重要的概念&#xff0c;它的核心能力是自主性与灵活性。一个智能体不仅能够理解用户的需求&#xff0c;还能拆解任务、调用工具完成具体操作&#xff0c;并在复杂场景中高效运行。在本篇博客中&…...

JAVA设计模式:依赖倒转原则(DIP)在Spring框架中的实践体现

文章目录 一、DIP原则深度解析1.1 核心定义1.2 现实比喻 二、Spring中的DIP实现机制2.1 传统实现 vs Spring实现对比 三、Spring中DIP的完整示例3.1 领域模型定义3.2 具体实现3.3 高层业务类3.4 配置类 四、Spring实现DIP的关键技术4.1 依赖注入方式对比4.2 自动装配注解 五、D…...

LeetCode题练习与总结:N 叉树的前序遍历--589

一、题目描述 给定一个 n 叉树的根节点 root &#xff0c;返回 其节点值的 前序遍历 。 n 叉树 在输入中按层序遍历进行序列化表示&#xff0c;每组子节点由空值 null 分隔&#xff08;请参见示例&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [1,null,3,2,4,nu…...

WebSocket 详解:全双工通信的实现与应用

目录 一、什么是 WebSocket&#xff1f;&#xff08;简介&#xff09; 二、为什么需要 WebSocket&#xff1f; 三、HTTP 与 WebSocket 的区别 WebSocket 的劣势 WebSocket 的常见应用场景 WebSocket 握手过程 WebSocket 事件处理和生命周期 一、什么是 WebSocket&#xf…...

【漫话机器学习系列】064.梯度下降小口诀(Gradient Descent rule of thume)

梯度下降小口诀 为了帮助记忆梯度下降的核心原理和关键注意事项&#xff0c;可以用以下简单口诀来总结&#xff1a; 1. 基本原理 损失递减&#xff0c;梯度为引&#xff1a;目标是让损失函数减少&#xff0c;依靠梯度指引方向。负梯度&#xff0c;反向最短&#xff1a;沿着负…...

Vue 3 中的 TypeScript:接口、自定义类型与泛型

在 Vue 3 中&#xff0c;TypeScript 提供了强大的类型系统&#xff0c;帮助我们更好地管理代码的类型安全。通过使用 接口&#xff08;Interface&#xff09;、自定义类型&#xff08;Type Aliases&#xff09; 和 泛型&#xff08;Generics&#xff09;&#xff0c;我们可以编…...

[SaaS] 内容创意生产平台

1.即梦 2.讯飞绘镜 typemovie 3.Krea.ai 4.Pika 5.runway 6.pixVerse 7....

低代码系统-产品架构案例介绍、明道云(十一)

明道云HAP-超级应用平台(Hyper Application Platform)&#xff0c;其实就是企业级应用平台&#xff0c;跟微搭类似。 通过自设计底层架构&#xff0c;兼容各种平台&#xff0c;使用低代码做到应用搭建、应用运维。 企业级应用平台最大的特点就是隐藏在冰山下的功能很深&#xf…...

2025年1月26日(超声波模块:上拉或下拉电阻)

添加上拉或下拉电阻是在电子电路设计和嵌入式系统编程中常用的一种技术手段&#xff0c;下面为你详细解释其含义、作用和应用场景。 基本概念 在数字电路里&#xff0c;引脚的电平状态通常有高电平&#xff08;逻辑 1&#xff09;和低电平&#xff08;逻辑 0&#xff09;两种…...

【自然语言处理(NLP)】深度循环神经网络(Deep Recurrent Neural Network,DRNN)原理和实现

文章目录 介绍深度循环神经网络&#xff08;DRNN&#xff09;原理和实现结构特点工作原理符号含义公式含义 应用领域优势与挑战DRNN 代码实现 个人主页&#xff1a;道友老李 欢迎加入社区&#xff1a;道友老李的学习社区 介绍 **自然语言处理&#xff08;Natural Language Pr…...

C语言学习阶段性总结(五)---函数

函数构成五要素&#xff1a; 1、返回值类型 2、函数名 3、参数列表&#xff08;输入&#xff09; 4、函数体 &#xff08;算法&#xff09; 5、返回值 &#xff08;输出&#xff09; 返回值类型 函数名 (参数列表) { 函数体&#xff1b; return 返回值&#xff1b; } void 类型…...

C++初阶—string类

第一章&#xff1a;为什么要学习string类 1.1 C语言中的字符串 C语言中&#xff0c;字符串是以\0结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C标准库中提供了一些str系列的库函数&#xff0c;但是这些库函数与字符串是分离开的&#xff0c;不太符合OOP的思想&…...

Solon Cloud Gateway 开发:Route 的过滤器与定制

RouteFilterFactory 是专为路由过滤拦截处理设计的接口。对应路由配置 filters 1、内置的路由过滤器 过滤器工厂本置前缀说明与示例AddRequestHeaderFilterFactoryAddRequestHeader添加请求头 (AddRequestHeaderDemo-Ver,1.0)AddResponseHeaderFilterFactoryAddResponseHeade…...

【MySQL】 数据类型

欢迎拜访&#xff1a;雾里看山-CSDN博客 本篇主题&#xff1a;【MySQL】 数据类型 发布时间&#xff1a;2025.1.27 隶属专栏&#xff1a;MySQL 目录 数据类型分类数值类型tinyint类型数值越界测试结果说明 bit类型基本语法使用注意事项 小数类型float语法使用注意事项 decimal语…...

基于vue和elementui的简易课表

本文参考基于vue和elementui的课程表_vue实现类似课程表的周会议列表-CSDN博客&#xff0c;原程序在vue3.5.13版本下不能运行&#xff0c;修改两处&#xff1a; 1&#xff09;slot-cope改为v-slot 2&#xff09;return background-color:rgb(24 144 255 / 80%);color: #fff; …...

vim的多文件操作

[rootxxx ~]# vim aa.txt bb.txt cc.txt #多文件操作 next #下一个文件 prev #上一个文件 first #第一个文件 last #最后一个文件 快捷键: ctrlshift^ #当前和上个之间切换 说明&#xff1a;快捷键ctrlshift^&#xff0c…...

spring spring-boot spring-cloud发布以及适配

https://spring.io/blog/2024/10/01/from-spring-framework-6-2-to-7-0 看了 spring 的官网&#xff0c;提到 2025 年 spring 会跟随 jdk 25 LTS发布后&#xff0c;接着发布 Spring Framework 7.0 GA&#xff0c;与之对应 spring 系列的组件版本情况如下。 Spring Framework版…...

【快速上手】阿里云百炼大模型

为了创建自己的知识库&#xff0c;本文介绍一下阿里云的百炼大模型&#xff0c;方便大家快速上手&#xff01;快速查询自己想要的内容。 一、入口页 阿里云登录 - 欢迎登录阿里云&#xff0c;安全稳定的云计算服务平台 二、大模型的选择 首先前提条件是 1、账号不能欠费 2、需…...

Linux:多线程[2] 线程控制

了解&#xff1a; Linux底层提供创建轻量级进程/进程的接口clone&#xff0c;通过选择是否共享资源创建。 vfork和fork都调用的clone进行实现&#xff0c;vfork和父进程共享地址空间-轻量级进程。 库函数pthread_create调用的也是底层的clone。 POSIX线程库 与线程有关的函数构…...