6个实战技巧:SDWebImage图片加载性能优化全攻略
SDWebImage作为iOS开发中最受欢迎的图片加载库,凭借其强大的缓存机制和高效的异步处理能力,被广泛应用于各类移动应用。本文将从问题诊断、方案实施到进阶拓展三个维度,通过6个实战技巧帮助开发者彻底掌握SDWebImage的优化方法,解决实际项目中的性能瓶颈,提升应用用户体验。无论你是刚接触图片加载的新手,还是希望优化现有项目的资深开发者,都能从本文获得实用的技术指导。
问题诊断篇:揭开图片加载的三大隐藏痛点
在移动应用开发中,图片加载看似简单,实则隐藏着诸多性能陷阱。即使使用了SDWebImage这样成熟的库,如果不能正确识别和解决这些问题,应用依然会出现各种性能问题,影响用户体验。
1.1 缓存一致性问题:看似缓存却重复下载
现象描述:应用中明明已经加载过的图片,在切换页面或重新打开时,依然会从网络下载,导致流量浪费和加载延迟。这种问题在用户网络环境较差时尤为明显,直接影响用户体验。
技术根源:SDWebImage的缓存机制依赖于URL作为缓存键,但在实际开发中,很多开发者忽略了URL参数变化对缓存的影响。例如,相同图片可能因为URL中包含不同的查询参数(如时间戳、版本号)而被视为不同的资源,导致缓存失效。
诊断方法:通过启用SDWebImage的调试日志,可以清晰地看到图片的加载来源。在开发环境中,添加以下代码开启调试日志:
[SDWebImageManager sharedManager].logLevel = SDWebImageLogLevelDebug;
观察控制台输出,如果频繁出现"Downloading image from network"而不是"Using cache",则可能存在缓存一致性问题。
1.2 线程阻塞危机:图片处理拖慢UI响应
现象描述:在快速滑动列表加载大量图片时,界面出现明显卡顿,甚至出现掉帧现象。这种问题在加载高清图片或需要进行复杂处理的图片时尤为突出。
技术根源:图片解码和处理操作默认在主线程执行,当同时加载多张图片时,大量的CPU密集型操作会阻塞主线程,导致UI响应延迟。SDWebImage虽然提供了后台解码功能,但很多开发者并未正确配置和使用。
诊断方法:使用Xcode的Instruments工具中的Time Profiler,记录应用在滑动列表时的性能表现。如果主线程中SDWebImage相关方法(如sd_setImageWithURL:)的耗时过长,超过16ms(60fps的单帧时间),则说明存在线程阻塞问题。
1.3 格式兼容性陷阱:高级格式加载失败或性能低下
现象描述:应用在加载WebP、HEIC等高级图片格式时,出现加载失败、显示异常或内存占用过高的问题。随着移动网络的发展,越来越多的服务端开始采用这些高效图片格式,但客户端处理能力往往未能跟上。
技术根源:SDWebImage虽然支持多种图片格式,但部分高级格式需要额外的编解码器支持。例如,WebP格式需要引入SDWebImageWebPCoder插件,而HEIC格式在不同iOS版本上的支持程度也有所不同。此外,不同格式的图片解码性能和内存占用差异很大,如果使用不当,反而会影响应用性能。
诊断方法:通过查看应用的崩溃日志或错误报告,关注与图片解码相关的异常信息。同时,可以使用SDWebImage的完成回调函数,检查返回的错误信息:
[imageView sd_setImageWithURL:imageURL completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
if (error) {
NSLog(@"Image loading error: %@", error.localizedDescription);
}
}];
方案实施篇:四大功能模块的优化实践
针对上述诊断出的问题,本节将从基础加载、高级缓存、格式处理和性能调优四个核心功能模块,提供具体的优化方案和实施步骤,帮助开发者彻底解决图片加载中的性能瓶颈。
2.1 基础加载模块:构建高效图片加载流程
基础加载模块是SDWebImage的核心功能,正确使用这一模块可以解决大部分常见的图片加载问题。以下是两个关键的优化实践:
2.1.1 智能取消请求:解决列表滑动图片错乱
问题场景:在UITableView或UICollectionView中快速滑动时,由于图片加载异步性,经常出现图片与单元格不匹配的情况,即"图片错乱"问题。
解决方案:利用SDWebImage提供的取消请求机制,在单元格复用前取消未完成的图片加载请求。
- (void)prepareForReuse {
[super prepareForReuse];
// 取消当前单元格的所有图片请求
[self.imageView sd_cancelCurrentImageLoad];
// 重置图片,避免复用前的图片短暂显示
self.imageView.image = [UIImage imageNamed:@"placeholder"];
}
核心原理:通过sd_cancelCurrentImageLoad方法取消当前视图的图片加载请求,确保单元格复用时不会显示错误的图片。这一机制利用了SDWebImage内部的操作管理系统,能够安全地终止正在进行的网络请求和图片处理过程。
✅ 最佳实践:始终在prepareForReuse方法中取消图片请求,这是解决图片错乱问题的关键步骤。同时,设置合适的占位图可以提升用户体验。
2.1.2 预加载策略:提升列表滑动流畅度
问题场景:在长列表中,用户快速滑动时,图片加载往往跟不上滑动速度,导致大量空白区域,影响用户体验。
解决方案:使用SDWebImage的预加载功能,提前加载用户可能浏览到的图片。
// 获取当前可见单元格的索引路径
NSArray *visibleIndexPaths = [self.tableView indexPathsForVisibleRows];
NSMutableArray *urlsToPrefetch = [NSMutableArray array];
// 预加载当前可见单元格前后各3个单元格的图片
for (NSIndexPath *indexPath in visibleIndexPaths) {
for (NSInteger i = 1; i <= 3; i++) {
NSInteger nextRow = indexPath.row + i;
if (nextRow < self.images.count) {
NSURL *url = [NSURL URLWithString:self.images[nextRow]];
[urlsToPrefetch addObject:url];
}
NSInteger prevRow = indexPath.row - i;
if (prevRow >= 0) {
NSURL *url = [NSURL URLWithString:self.images[prevRow]];
[urlsToPrefetch addObject:url];
}
}
}
// 使用SDWebImagePrefetcher进行预加载
[[SDWebImagePrefetcher sharedImagePrefetcher] prefetchURLs:urlsToPrefetch
progress:nil
completed:^(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls) {
NSLog(@"Preloading completed: %lu finished, %lu skipped",
(unsigned long)noOfFinishedUrls, (unsigned long)noOfSkippedUrls);
}];
核心原理:SDWebImagePrefetcher能够根据设置的并发数和优先级,在后台预加载指定的图片URL。预加载的图片会被缓存起来,当用户滑动到对应位置时,可以直接从缓存中获取,大大提升加载速度。
💡 小贴士:预加载的数量和范围需要根据实际情况调整。过多的预加载会浪费带宽和内存,过少则无法达到提升体验的效果。一般建议预加载当前可见区域前后各3-5个单元格的图片。
2.2 高级缓存模块:打造智能缓存系统
SDWebImage的缓存系统是其核心优势之一,但很多开发者只使用了默认配置,未能充分发挥其潜力。通过合理配置和使用缓存功能,可以显著提升应用性能和用户体验。
2.2.1 多级缓存策略:平衡性能与存储
SDWebImage采用内存缓存和磁盘缓存的二级缓存策略,理解并合理配置这两级缓存是优化的关键。
缓存工作流程:
- 当请求加载图片时,首先检查内存缓存,如果命中则直接返回
- 内存缓存未命中时,检查磁盘缓存,如果命中则加载并更新到内存缓存
- 磁盘缓存未命中时,从网络下载,下载完成后同时更新到内存和磁盘缓存
优化配置示例:
// 获取默认缓存实例
SDImageCache *cache = [SDImageCache sharedImageCache];
// 配置内存缓存
cache.config.maxMemoryCost = 1024 * 1024 * 50; // 50MB内存缓存上限
cache.config.shouldUseWeakMemoryCache = YES; // 启用弱引用内存缓存
// 配置磁盘缓存
cache.config.maxCacheAge = 60 * 60 * 24 * 7; // 7天缓存有效期
cache.config.maxCacheSize = 1024 * 1024 * 500; // 500MB磁盘缓存上限
// 启用磁盘缓存压缩
cache.config.shouldCompressImagesInDisk = YES;
cache.config.imageCompressionQuality = 0.8; // 压缩质量
核心原理:通过调整内存缓存大小,可以在应用性能和内存占用之间取得平衡。启用弱引用缓存可以在内存紧张时自动释放缓存,避免应用崩溃。磁盘缓存配置则可以控制存储空间占用,避免应用被系统清理。
⚠️ 风险提示:内存缓存设置过大可能导致应用内存占用过高,触发系统内存警告;设置过小则会导致频繁的磁盘缓存访问,影响性能。需要根据应用的实际情况进行测试和调整。
2.2.2 缓存键自定义:解决URL参数变化问题
问题场景:同一图片资源可能因为URL参数不同(如版本号、时间戳)而被SDWebImage视为不同的资源,导致重复下载和缓存,浪费带宽和存储空间。
解决方案:自定义缓存键生成策略,忽略URL中的无关参数。
// 创建自定义缓存键过滤器
SDWebImageCacheKeyFilter *customCacheKeyFilter = [SDWebImageCacheKeyFilter cacheKeyFilterWithBlock:^NSString * _Nullable(NSURL * _Nonnull url) {
// 解析URL,移除无关参数
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
NSMutableArray *queryItems = [components.queryItems mutableCopy];
// 过滤掉不需要的查询参数
queryItems = [queryItems filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSURLQueryItem * _Nullable item, NSDictionary<NSString *,id> * _Nullable bindings) {
// 保留必要的参数,移除如时间戳、版本号等可变参数
return ![item.name isEqualToString:@"timestamp"] && ![item.name isEqualToString:@"version"];
}]];
components.queryItems = queryItems;
return components.URL.absoluteString;
}];
// 应用自定义缓存键过滤器
[SDWebImageManager sharedManager].cacheKeyFilter = customCacheKeyFilter;
核心原理:SDWebImage允许通过cacheKeyFilter自定义缓存键的生成方式。通过移除URL中的可变参数,可以确保同一资源即使URL参数变化也能命中缓存,提高缓存命中率。
💡 小贴士:除了过滤URL参数,还可以根据实际需求实现更复杂的缓存键生成逻辑,如对URL进行MD5哈希,避免长URL带来的性能问题。
2.3 格式处理模块:优化图片解码与显示
随着移动网络的发展,越来越多的应用开始使用WebP、HEIC等高效图片格式。SDWebImage提供了灵活的编解码器架构,支持多种图片格式的处理。
2.3.1 高级格式支持:集成WebP/HEIC编解码器
问题场景:服务端已经采用WebP等高效图片格式,但客户端无法正确加载或解码,导致图片显示异常。
解决方案:集成相应的编解码器插件,扩展SDWebImage的格式支持能力。
集成方法:通过CocoaPods集成WebP编解码器:
pod 'SDWebImage/WebP'
使用方式:
// WebP图片加载,使用方式与普通图片一致
[imageView sd_setImageWithURL:[NSURL URLWithString:@"https://example.com/image.webp"]
placeholderImage:[UIImage imageNamed:@"placeholder"]];
核心原理:SDWebImage采用插件化架构,通过注册不同的编解码器来支持多种图片格式。WebP编解码器会自动处理WebP格式的解码,开发者无需修改现有代码即可享受高效图片格式带来的好处。
✅ 最佳实践:在项目初期就规划好图片格式支持策略,优先采用WebP等高效格式,可以显著减少网络传输量和存储空间占用。根据统计,WebP格式相比JPEG可以节省30%左右的存储空间。
2.3.2 动画图片优化:解决GIF播放性能问题
问题场景:加载GIF动画时出现内存占用过高、播放卡顿或应用崩溃等问题。原生UIImageView对GIF的支持有限,直接使用会导致严重的性能问题。
解决方案:使用SDWebImage提供的SDAnimatedImageView控件,专门优化动画图片的加载和播放。
#import <SDWebImage/SDAnimatedImageView.h>
// 创建动画图片视图
SDAnimatedImageView *animatedImageView = [[SDAnimatedImageView alloc] init];
animatedImageView.frame = CGRectMake(0, 0, 200, 200);
[self.view addSubview:animatedImageView];
// 加载GIF动画
[animatedImageView sd_setImageWithURL:[NSURL URLWithString:@"https://example.com/animation.gif"]
placeholderImage:[UIImage imageNamed:@"loading"]];
// 高级配置
animatedImageView.autoPlayAnimatedImage = YES; // 自动播放
animatedImageView.animatedImageLoopCount = 0; // 无限循环(0表示无限)
animatedImageView.maxBufferSize = 10 * 1024 * 1024; // 10MB缓存上限
核心原理:SDAnimatedImageView内部采用帧缓存池机制,避免重复解码相同帧,同时根据设备性能动态调整帧率。相比原生UIImageView,SDAnimatedImageView可以将GIF播放的内存占用降低50%以上。
⚠️ 风险提示:即使使用SDAnimatedImageView,过大的GIF动画依然可能导致性能问题。建议服务端提供不同分辨率的GIF资源,根据设备性能和网络状况动态选择合适的资源。
2.4 性能调优模块:深入优化加载性能
除了基础功能的正确使用,SDWebImage还提供了多种高级优化选项,可以根据应用的具体需求进行深度优化。
2.4.1 图片解码策略:后台解码与缩略图生成
问题场景:加载大尺寸图片时,解码过程占用大量CPU资源,导致主线程阻塞,界面卡顿。
解决方案:启用后台解码和缩略图生成功能,降低主线程负担。
// 全局配置后台解码
[SDImageCache sharedImageCache].config.shouldDecodeImages = YES;
[SDImageCache sharedImageCache].config.shouldDecodeAnimatedImages = YES;
// 加载图片时指定缩略图选项
[imageView sd_setImageWithURL:imageURL
placeholderImage:placeholder
options:SDWebImageDecodeFirstFrameOnly | SDWebImageScaleDownLargeImages];
核心原理:SDWebImage的后台解码功能将图片解码操作移至后台线程执行,避免阻塞主线程。缩略图生成则会根据目标视图大小自动调整图片尺寸,减少内存占用和绘制开销。
💡 小贴士:对于列表中的图片,建议始终启用SDWebImageScaleDownLargeImages选项,该选项会自动将图片缩小到目标视图的大小,显著降低内存占用。
2.4.2 请求优先级控制:优化用户体验
问题场景:在同时加载多张图片时,重要图片(如首屏Banner)和普通图片竞争网络资源,导致重要图片加载延迟。
解决方案:使用SDWebImage的请求优先级控制功能,为不同图片设置不同的加载优先级。
// 高优先级加载(如首屏Banner)
[bannerImageView sd_setImageWithURL:bannerURL
placeholderImage:placeholder
options:0
progress:nil
completed:nil];
[[SDWebImageManager sharedManager] setImageLoadPriority:SDWebImageLoadPriorityHigh forURL:bannerURL];
// 低优先级加载(如列表图片)
[cell.imageView sd_setImageWithURL:imageURL
placeholderImage:placeholder
options:0
progress:nil
completed:nil];
[[SDWebImageManager sharedManager] setImageLoadPriority:SDWebImageLoadPriorityLow forURL:imageURL];
核心原理:SDWebImage允许为每个图片请求设置优先级,内部会根据优先级调整网络请求的执行顺序。高优先级的请求会被优先处理,确保重要图片先加载完成,提升用户体验。
进阶拓展篇:深度应用场景解决方案
除了基础功能和性能优化,SDWebImage还支持多种高级应用场景,如跨平台适配和企业级定制。本节将介绍这些深度应用场景的解决方案。
3.1 跨平台适配:iOS/OSX/Vision Pro全平台支持
SDWebImage不仅支持iOS平台,还提供了对macOS、tvOS和Vision Pro等平台的支持。通过统一的API接口,可以实现跨平台的图片加载功能。
跨平台代码示例:
#if TARGET_OS_IPHONE || TARGET_OS_TV
#import <UIKit/UIKit.h>
#define SDImageView UIImageView
#elif TARGET_OS_MAC
#import <AppKit/AppKit.h>
#define SDImageView NSImageView
#endif
// 跨平台图片加载代码
SDImageView *imageView = [[SDImageView alloc] init];
[imageView sd_setImageWithURL:[NSURL URLWithString:@"https://example.com/image.jpg"]
placeholderImage:nil];
Vision Pro适配:
import SwiftUI
import SDWebImageSwiftUI
struct ContentView: View {
var body: some View {
WebImage(url: URL(string: "https://example.com/image.jpg"))
.resizable()
.placeholder {
ProgressView()
}
.aspectRatio(contentMode: .fit)
.frame(width: 300, height: 200)
}
}
核心原理:SDWebImage通过条件编译和抽象接口,为不同平台提供了统一的API。在iOS和tvOS上使用UIKit组件,在macOS上使用AppKit组件,在Vision Pro上则提供了SwiftUI组件,实现了真正的跨平台支持。
✅ 最佳实践:对于跨平台项目,建议将图片加载逻辑封装在独立的工具类中,利用条件编译处理平台差异,保持业务逻辑的一致性。
3.2 企业级定制:构建专属图片加载系统
对于大型应用或有特殊需求的企业级项目,SDWebImage提供了丰富的定制接口,可以构建符合特定业务需求的图片加载系统。
3.2.1 自定义缓存管理器:多缓存策略实现
应用场景:需要区分不同类型图片(如用户头像、商品图片、广告图片)的缓存策略,例如用户头像需要长期缓存,而广告图片则需要频繁更新。
解决方案:使用SDImageCachesManager管理多个缓存实例,为不同类型的图片设置不同的缓存策略。
// 创建多个缓存实例
SDImageCache *avatarCache = [[SDImageCache alloc] initWithNamespace:@"avatars"];
avatarCache.config.maxCacheAge = 60 * 60 * 24 * 30; // 30天缓存
SDImageCache *adCache = [[SDImageCache alloc] initWithNamespace:@"ads"];
adCache.config.maxCacheAge = 60 * 60 * 2; // 2小时缓存
// 创建缓存管理器
SDImageCachesManager *cachesManager = [[SDImageCachesManager alloc] init];
[cachesManager addCache:avatarCache];
[cachesManager addCache:adCache];
// 创建自定义图片管理器
SDWebImageManager *avatarManager = [[SDWebImageManager alloc] initWithCache:avatarCache loader:[SDWebImageDownloader sharedDownloader]];
SDWebImageManager *adManager = [[SDWebImageManager alloc] initWithCache:adCache loader:[SDWebImageDownloader sharedDownloader]];
// 使用自定义管理器加载图片
[avatarImageView sd_setImageWithURL:avatarURL
placeholderImage:placeholder
options:0
manager:avatarManager];
核心原理:SDImageCachesManager允许管理多个SDImageCache实例,每个实例可以有独立的缓存策略。通过自定义SDWebImageManager,可以为不同类型的图片指定不同的缓存和加载策略。
3.2.2 自定义加载器:对接企业内部图片服务
应用场景:企业内部图片服务可能有特殊的认证机制、加密方式或协议要求,需要定制图片加载过程。
解决方案:实现SDImageLoader协议,创建自定义加载器。
// 自定义加载器实现
@interface CustomImageLoader : NSObject <SDImageLoader>
@end
@implementation CustomImageLoader
- (BOOL)canRequestImageForURL:(nullable NSURL *)url {
// 只处理企业内部域名的URL
return [url.host containsString:@"internal.example.com"];
}
- (nullable id<SDWebImageOperation>)requestImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
context:(nullable SDWebImageContext *)context
progress:(nullable SDImageLoaderProgressBlock)progressBlock
completed:(nullable SDImageLoaderCompletedBlock)completedBlock {
// 创建自定义请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 添加企业认证头
[request setValue:@"CustomToken" forHTTPHeaderField:@"Authorization"];
// 使用自定义网络库发送请求
CustomURLSessionTask *task = [[CustomURLSessionTask alloc] initWithRequest:request];
[task setCompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
completedBlock(nil, nil, error, YES);
} else {
completedBlock(data, response, nil, YES);
}
}];
[task setProgressHandler:^(NSProgress *progress) {
if (progressBlock) {
progressBlock(progress.completedUnitCount, progress.totalUnitCount, url);
}
}];
[task resume];
return task;
}
@end
// 注册自定义加载器
SDImageLoadersManager *loadersManager = [SDImageLoadersManager sharedManager];
[loadersManager addLoader:[[CustomImageLoader alloc] init]];
核心原理:SDWebImage的加载器系统采用责任链模式,允许注册多个加载器。当需要加载图片时,会依次询问每个加载器是否能够处理该URL,第一个能够处理的加载器将负责加载过程。通过实现自定义加载器,可以无缝对接企业内部服务。
学习资源地图
为了帮助开发者进一步深入学习和应用SDWebImage,以下提供了全面的学习资源地图:
官方文档
- 快速入门:README.md
- 高级用法:Docs/HowToUse.md
- 迁移指南:Docs/SDWebImage-5.0-Migration-guide.md
代码示例
- iOS示例:Examples/SDWebImage Demo
- macOS示例:Examples/SDWebImage OSX Demo
- Vision Pro示例:Examples/SDWebImage Vision Demo
性能测试工具
- Xcode Instruments:使用Time Profiler分析加载性能,Memory Graph检测内存泄漏
- 内置性能测试:Tests目录下包含多种性能测试用例,可直接运行
扩展插件
- WebP支持:通过CocoaPods集成SDWebImage/WebP
- HEIC支持:通过CocoaPods集成SDWebImage/HEIC
- 其他格式支持:参考官方文档中的插件列表
调试工具
- 启用详细日志:
[SDWebImageManager sharedManager].logLevel = SDWebImageLogLevelDebug - 缓存统计信息:
[[SDImageCache sharedImageCache] calculateSizeWithCompletionBlock:^(NSUInteger fileCount, NSUInteger totalSize) { ... }]
通过这些资源,开发者可以全面掌握SDWebImage的使用和优化技巧,构建高性能的图片加载系统。无论是解决实际项目中的性能问题,还是进行深度定制开发,SDWebImage都提供了强大而灵活的功能支持。
总结
SDWebImage作为iOS开发中最受欢迎的图片加载库,提供了丰富的功能和灵活的定制选项。本文通过"问题-方案-进阶"三阶结构,系统介绍了SDWebImage的优化技巧和深度应用场景。从基础的图片加载流程优化,到高级的缓存策略配置,再到跨平台适配和企业级定制,全面覆盖了SDWebImage的核心功能和最佳实践。
通过本文介绍的6个实战技巧,开发者可以解决图片加载中的缓存一致性、线程阻塞和格式兼容性等关键问题,显著提升应用性能和用户体验。同时,本文提供的学习资源地图可以帮助开发者持续深入学习和探索SDWebImage的更多高级特性。
图片加载看似简单,实则涉及网络、缓存、解码、渲染等多个环节,任何一个环节的优化都可能带来显著的性能提升。希望本文能够帮助开发者更好地理解和应用SDWebImage,构建高效、稳定的图片加载系统,为用户提供流畅的视觉体验。
GLM-5智谱 AI 正式发布 GLM-5,旨在应对复杂系统工程和长时域智能体任务。Jinja00
GLM-5.1GLM-5.1是智谱迄今最智能的旗舰模型,也是目前全球最强的开源模型。GLM-5.1大大提高了代码能力,在完成长程任务方面提升尤为显著。和此前分钟级交互的模型不同,它能够在一次任务中独立、持续工作超过8小时,期间自主规划、执行、自我进化,最终交付完整的工程级成果。Jinja00
LongCat-AudioDiT-1BLongCat-AudioDiT 是一款基于扩散模型的文本转语音(TTS)模型,代表了当前该领域的最高水平(SOTA),它直接在波形潜空间中进行操作。00- QQwen3.5-397B-A17BQwen3.5 实现了重大飞跃,整合了多模态学习、架构效率、强化学习规模以及全球可访问性等方面的突破性进展,旨在为开发者和企业赋予前所未有的能力与效率。Jinja00
HY-Embodied-0.5这是一套专为现实世界具身智能打造的基础模型。该系列模型采用创新的混合Transformer(Mixture-of-Transformers, MoT) 架构,通过潜在令牌实现模态特异性计算,显著提升了细粒度感知能力。Jinja00
FreeSql功能强大的对象关系映射(O/RM)组件,支持 .NET Core 2.1+、.NET Framework 4.0+、Xamarin 以及 AOT。C#00

