当前位置:首页>iOSAPP>iOS 内存管理深度剖析(中):图片解码 / Autorelease 池 / 循环引用 / 大对象生命周期

iOS 内存管理深度剖析(中):图片解码 / Autorelease 池 / 循环引用 / 大对象生命周期

  • 2026-06-20 07:44:09
iOS 内存管理深度剖析(中):图片解码 / Autorelease 池 / 循环引用 / 大对象生命周期

这是 iOS 内存深度剖析系列的第二篇——场景与优化篇

  • 上篇 · 原理篇:从虚拟内存到 ARC,jetsam 杀进程的真实逻辑(已发,建议先看)。
  • 中篇 · 场景与优化篇(本文):图片解码 / Autorelease 池 / 循环引用 / 大对象生命周期 / Swift 内存陷阱。
  • 下篇 · 实战篇:5 类 OOM 形态 / Memory Graph / MetricKit / 线上止损 Playbook。

上篇我们把 iOS 内存的"原理面"扒透了——5 类内存页、phys_footprint才是 jetsam 真正看的指标。但读到这里你应该有个疑问:那我代码里到底是哪些操作在贡献 dirty page?

这一篇就是答案。我们围绕 5 类真实占满你 App 内存的元凶展开:图片、Autorelease Pool、循环引用、大对象缓存、Swift 特有陷阱。每一类都给出可以直接用到代码里的优化手段量化收益

全文阅读时间约 15 分钟。


一、图片:iOS 内存第一杀手

iOS 内存优化做到极致,80% 的精力都花在图片上——这不是夸张。一张 4K 图(4032 × 3024)在内存里展开后是:

4032 × 3024 × 4 bytes(RGBA)= 46.6 MB

就这一张图——已经远超 Notification Service Extension 24MB 的内存上限(上篇钩子里 28MB OOM 的同款限额)。10 张这样的图在列表里同时滚动,前台 App 也能瞬间被 jetsam 杀掉。

1.1 UIImage(named:) 的隐性 cache

很多人以为 UIImage(named:)就是"读个图"——错。它背后是 Apple 维护的一份 named image cache

let img1 = UIImage(named: "big_photo")   // 读元数据 + 加入 named cachelet img2 = UIImage(named: "big_photo")   // 直接复用同一个 UIImage 实例

三个特征

  1. 缓存的是 UIImage实例——同一个 name 多次调用,复用同一个对象
  2. 位图常驻——首次渲染时解码出位图(CGImage backing store),之后默认不主动释放,再次显示直接复用
  3. 进程级共享——你整个 App 共用一份 cache,只要 cache 还持有这个 UIImage,位图就在

这就是为什么"我的 ImageView 释放了,内存却没下降"——你释放的是 ImageView 对 UIImage 的一个引用,但 UIImage实例还在 named cache 里,背后的位图也还在。系统会在 memory warning 时清理 named cache,但平时不会。

**对比 UIImage(contentsOfFile:)**:

行为
UIImage(named:)UIImage(contentsOfFile:)
named cache
✅ 有
❌ 无
重复创建
复用 cache
每次都重新解码
释放时机
系统决定
ARC 决定
适合场景
小图标、反复使用的 UI 资源
大图、一次性使用的内容图

实战结论

  • App 自带的小图标 / 按钮图:用 UIImage(named:),受益于 cache
  • 从网络 / 相册 / 沙盒加载的大图:用 UIImage(contentsOfFile:),避免大图被 named cache 长时间持有

1.2 解码时机:什么时候 PNG/JPG 变成位图

PNG / JPG 文件本身不是位图——它是压缩格式。一张 10MB 的 JPG 文件,在磁盘上 10MB,解码后变成位图可能是 40-60 MB

解码发生的时机大多数人不知道:

let img = UIImage(named: "photo")     // ① 读元数据 + 加入 named cache,不解码imageView.image = img                  // ② 仍未解码// ↓// 当 imageView 第一次 layout / display 时// CoreAnimation 在主线程触发解码 → 位图进 dirty memory

UIImage(contentsOfFile:)同样是延迟解码——首次渲染才解码是 UIKit 的统一行为,不是 named:独有的。

问题:解码发生在主线程,且发生在你看不到的时机——这是为什么列表滚动时偶尔卡顿的根因。

解决方案:提前在子线程强制解码

extensionUIImage{funcdecoded() -> UIImage {guardlet cgImage = self.cgImage else { returnself }let colorSpace = CGColorSpaceCreateDeviceRGB()let context = CGContext(            data: nil,            width: cgImage.width,            height: cgImage.height,            bitsPerComponent: 8,            bytesPerRow: cgImage.width * 4,            space: colorSpace,            bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue |CGBitmapInfo.byteOrder32Little.rawValue        )        context?.draw(cgImage, inCGRect(x: 0, y: 0,                                           width: cgImage.width,                                           height: cgImage.height))guardlet decoded = context?.makeImage() else { returnself }returnUIImage(cgImage: decoded)    }}// 用法(子线程)DispatchQueue.global().async {let decoded = UIImage(contentsOfFile: path)?.decoded()DispatchQueue.main.async {        imageView.image = decoded   // 此时位图已解码完,主线程不再阻塞    }}

SDWebImage / Kingfisher 默认就在子线程做了强制解码——这是它们比手写 UIImage(contentsOfFile:)快的根本原因之一。

1.3 Downsampling:内存占用降低一个数量级

最关键的优化技巧——大多数列表场景根本不需要原图分辨率

举例:你列表里一个 cell 的缩略图区域是 200 × 200 pt(即 600 × 600 px @3x),但用户上传的是 4032 × 3024的原图。如果直接 UIImage(contentsOfFile:)

原图位图:4032 × 3024 × 4 = 46.6 MB显示区域只有 600 × 600 px,差距 28 倍

正确做法——用 ImageIO的 thumbnail API 在解码时直接缩小:

import ImageIOfuncdownsample(imageAt url: URL, to pointSize: CGSize, scale: CGFloat) -> UIImage? {let imageSourceOptions = [kCGImageSourceShouldCache: falseasCFDictionaryguardlet imageSource = CGImageSourceCreateWithURL(url asCFURL,                                                        imageSourceOptions) else {returnnil    }let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scalelet downsampleOptions: [CFStringAny] = [        kCGImageSourceCreateThumbnailFromImageAlways: true,        kCGImageSourceShouldCacheImmediately: true,        kCGImageSourceCreateThumbnailWithTransform: true,        kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels    ]guardlet downsampledImage = CGImageSourceCreateThumbnailAtIndex(        imageSource, 0, downsampleOptions asCFDictionaryelse {returnnil    }returnUIImage(cgImage: downsampledImage)}// 用法let thumbnail = downsample(imageAt: photoURL,                            to: CGSize(width: 200, height: 200),                            scale: UIScreen.main.scale)

收益(按列表 prefetch 20 张同时存活计算):

方案
单张内存占用
20 张 prefetch
UIImage(contentsOfFile:)
原图
~47 MB
~940 MB(前台必 OOM)
downsample
到 600x600
~1.4 MB
~28 MB ✅

降低 30+ 倍——这是抖音、微信等社交 App 在 feed 流里的标配做法。

1.4 列表加载图片的正确姿势

总结上面三点,正确的列表图片加载流程:

① 主线程:cell 出现,触发图片加载   ↓② 子线程:CGImageSourceCreateThumbnailAtIndex 一步搞定         (读文件 + 解码 + 缩放,全在子线程)   ↓③ 主线程:拿到缩略图 UIImage,赋值给 imageView   ↓④ cell 滑出屏幕:取消该 cell 的图片加载任务(避免重复加载)

反模式(性能极差):

// ❌ 错误做法cell.imageView.image = UIImage(contentsOfFile: largeImagePath)// 问题:1) 主线程读磁盘 2) 主线程解码 3) 用原图分辨率

正确做法:用 SDWebImage / Kingfisher 这类成熟库——它们默认做了子线程解码 + 磁盘 cache + 内存 cache + cell 取消加载。但 downsample 需要显式配置(Kingfisher 用 DownsamplingImageProcessor、SDWebImage 通过 context: [.imageThumbnailPixelSize: ...])——别假设它默认会缩放。如果非要手写,至少做到 downsample + 子线程解码这两点。

记住一句话:图片优化的核心是 **"渲染需要多少像素,就只解码到那么多像素"**——而不是把原图整个塞进内存。


二、Autorelease Pool:被低估的内存控制阀

@autoreleasepool是 ObjC/Swift 里最常被误解的东西。很多人觉得"自从 ARC 之后就不用管它了"——完全错。它是控制临时对象生命周期的关键开关,特别是在大循环里。

2.1 主线程的 autorelease pool 是 RunLoop 自动管的

主线程之所以"不用管 autorelease pool",是因为 RunLoop 在固定时机自动 push/pop

主线程 RunLoop 的 pool 行为:进入 RunLoop(kCFRunLoopEntry)           → push pool   ├─ 处理事件 → 临时对象进入当前 pool   ├─ 即将休眠(kCFRunLoopBeforeWaiting)→ pop 旧 pool + push 新 pool   ├─ 醒来继续处理事件 → 进入新 pool   └─ 退出 RunLoop(kCFRunLoopExit)      → pop pool

也就是说,每次主线程从"忙"转"闲"(即将休眠等下一个事件)时,本轮产生的所有 autorelease 对象就会被释放一次。这就是为什么平时主线程内存看着稳定——临时对象不会跨事件循环堆积。

2.2 子线程必须自己包,否则内存会爆

子线程不一样——dispatch_queue / NSThread / pthread 创建的子线程没有 RunLoop

GCD 有个补救:每次 block 执行时自动包一层 autorelease pool,block 结束 drain。但这只在 block 边界生效——block 内部的大循环里产生的 autorelease 对象,仍然要等整个 block 结束才统一释放

这意味着:

// ❌ 在子线程的大循环里产生 autorelease 对象DispatchQueue.global().async {for i in0..<10000 {let str = "iter \(i)"// 纯 Swift String,不走 autoreleaselet nsstr = NSString(format: "%d", i)   // ⚠️ NSObject 子类,会被 autorelease// 处理 nsstr ...    }// 循环结束才统一释放,期间内存峰值 = 10000 个 NSString}

正确写法

DispatchQueue.global().async {for i in0..<10000 {        autoreleasepool {let nsstr = NSString(format: "%d", i)// 处理 nsstr ...        }   // 每次循环结束,立刻释放    }}

2.3 一个 for 循环把 200MB 压到 20MB

最经典的场景:批量处理图片 / 数据

// ❌ 反模式:一次循环跑完才释放,内存峰值爆炸DispatchQueue.global().async {var thumbnails: [UIImage] = []for url in100张图的URL列表 {let img = UIImage(contentsOfFile: url.path)!let thumb = img.resized(to: CGSize(width: 200, height: 200))        thumbnails.append(thumb)// 期间 img 被 autorelease,要等循环结束才能释放// 假设每张图解码后 ~30 MB(中等尺寸),100 张峰值 ≈ 3 GB → OOM    }}// ✅ 正确做法DispatchQueue.global().async {var thumbnails: [UIImage] = []for url in100张图的URL列表 {        autoreleasepool {let img = UIImage(contentsOfFile: url.path)!let thumb = img.resized(to: CGSize(width: 200, height: 200))            thumbnails.append(thumb)        }   // 每次结束,原图 img 立即释放    }// 内存峰值 ≈ 单张图 30MB + thumbnails 数组(100 × 0.5MB)≈ 80 MB}

真实案例:很多团队在做"导出相册到文件"功能时遇到 OOM,根因就是没在循环里加 autoreleasepool。加了之后内存峰值从几 GB 直接降到几十 MB。

2.4 Swift 里 autoreleasepool 的注意事项

Swift 里写法和 ObjC 略不同——是个全局函数

autoreleasepool {// 临时对象的代码}

注意点

  • 纯 Swift 类型(StringArraystruct)不需要 pool——它们是 value type(内部 buffer 走 COW,不走 ObjC autorelease 机制)
  • 但只要有 NSObject子类的临时对象(包括 UIImage / NSData / Foundation 类型),仍然受 pool 影响
  • 跨语言桥接对象(Foundation 桥接:URLDateData)也可能产生 autorelease 对象

实操建议:在子线程跑大循环 + 涉及任何 Foundation / UIKit 对象时,**无脑加 autoreleasepool**。代价是几乎可以忽略的,收益可能是从 OOM 到不 OOM 的差距。

记住一句话:autorelease pool 不是"ARC 之前的遗物"——它是子线程大循环里唯一能控制临时对象生命周期的工具。RunLoop 帮你管的只是主线程,子线程要自己管。


三、循环引用:经典且永不过时

ARC 自动管理引用计数,但有一个它解决不了的问题:两个对象互相 strong 持有,引用计数永远不会归零,永远不释放。这就是循环引用,是 iOS 内存泄漏的头号原因。

3.1 ObjC 三种典型循环引用

类型 1:delegate 错用 strong

// ❌@property (nonatomicstrongid<MyDelegate> delegate;// 当 delegate 反过来又持有 self(典型场景:VC 自己当 cell/view 的 delegate),就形成循环// ✅@property (nonatomicweakid<MyDelegate> delegate;

类型 2:block 捕获 self

// ❌self.completion = ^{    [self doSomething];   // block 捕获 self → self 持有 block → block 持有 self → 循环};// ✅__weaktypeof(self) wself = self;self.completion = ^{    __strongtypeof(wself) sself = wself;   // 在 block 内"提升"为 strong,避免中途释放    [sself doSomething];};

类型 3:NSTimer 持有 target

// ❌self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0                                              target:self                                            selector:@selector(tick)                                            userInfo:nil                                             repeats:YES];// timer 持有 self(target),self 持有 timer → 循环// ✅(iOS 10+)__weaktypeof(self) wself = self;self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0                                              repeats:YES                                                block:^(NSTimer *t) {    [wself tick];}];

NSTimer 的坑特别深:即使你 [timer invalidate]只要 RunLoop 还活着,target 仍然不释放——因为 RunLoop 持有 timer,timer 持有 target。所以 ViewController 在 dealloc 里 invalidate 是走不到的——正因为循环引用让 self 一直活着,dealloc 永远不会被调到。唯一正确做法:在 viewWillDisappear或外部主动 invalidate。

3.2 Swift 闭包:[weak self] vs [unowned self]

Swift 里循环引用最常见的就是闭包捕获 self:

// ❌ 闭包默认 strong 捕获 selfviewModel.onUpdate = {self.updateUI()   // 循环:self 持有 viewModel,viewModel 持有 onUpdate,onUpdate 持有 self}// ✅ 用 weak selfviewModel.onUpdate = { [weakselfinself?.updateUI()}// ⚠️ 用 unowned self(更快但有风险)viewModel.onUpdate = { [unownedselfinself.updateUI()   // 如果 self 已被释放,崩溃}

[weak self]vs [unowned self]怎么选

情况
推荐
不确定 self 和闭包谁先死
[weak self]
闭包是 self 的实例属性,且 self 一定先于闭包销毁
[unowned self]

实操原则:**默认 [weak self]**——Apple Swift 社区指南也是这个建议。unowned和 weak的性能差距在现代 Swift 已经很小,不值得为几纳秒承担 crash 风险。只在你能 100% 证明生命周期顺序时才用 [unowned self]

3.3 Combine / Swift Concurrency 时代的新坑

iOS 13+ 引入 Combine 和 Swift Concurrency 后,循环引用又冒出几种新形态。

Combine 订阅没保存就漏

// ❌ subscription 没保存publisher.sink { [weakself] value inself?.handle(value)}// 这个 sink 返回的 `AnyCancellable` 离开当前作用域立即 deinit// → cancel() → 后续异步发出的值都收不到(看起来代码能跑,实际信号丢失)
// ✅ 保存到属性privatevar cancellables = Set<AnyCancellable>()publisher.sink { [weakself] value inself?.handle(value)}.store(in: &cancellables)

Combine 闭包的隐性循环

// ❌ self 隐式持有publisher.sink { value inself.handle(value)   // 闭包持有 self}.store(in: &self.cancellables)   // self 持有 cancellable,cancellable 持有闭包,闭包持有 self

修法和闭包一样:用 [weak self]

Swift Concurrency Task 的循环

classViewControllerUIViewController{var task: Task<VoidNever>?funcstartWork() {        task = Task {            await self.fetchData()   // ⚠️ Task 闭包 strong 捕获 self        }    }// 只要 task 没结束,self 永远不释放——ViewController 即使被 pop 也会一直活着}

修法:

task = Task { [weakselfin    await self?.fetchData()}

3.4 工具:Memory Graph 5 分钟找循环引用

Xcode 自带的 Memory Graph 是找循环引用的最简单工具

  1. Xcode 跑起来 App(Debug build)
  2. 点击底部 Debug 工具栏里的 Debug Memory Graph 按钮(三个圆圈相连的图标)
  3. App 暂停,左侧 Navigator 列出所有存活对象
  4. 点击右下角的紫色 ⚠️ 图标——只显示 Xcode 检测到的潜在泄漏对象
  5. 点开任一对象,中间画布显示完整的引用链——直接看到谁强引用了谁

Xcode 这套自动检测能发现 80% 的常见循环(直接strong 闭环),少数间接路径仍需配合 Instruments Allocations / Leaks——但比 Instruments Leaks 直观 10 倍,作为日常自查首选。

记住一句话:循环引用是 iOS 内存泄漏最常见的形态。用 Memory Graph 5 分钟扫一遍,能查出绝大多数泄漏。


四、大对象生命周期:什么该缓存、什么不该

App 里到处都是"缓存"——图片、网络结果、计算中间值、数据库查询结果。但缓存用不好就是内存炸弹。这一节讲怎么科学地管理大对象。

4.1 NSCache vs 自实现 LRU

iOS 里最常用的两种缓存方案:

方案
自动响应内存压力
容量上限
适用场景
NSCache
✅ 系统自动清理
可设置(按数量 / 总成本)
默认首选
——大多数场景
自实现 LRU
❌ 需自己监听 memory pressure
手动控制
需要精确控制淘汰策略

你可能听过 NSPurgeableData——它能"被丢弃后再重建",但 Apple 多年未更新、文档稀薄、社区基本不用,新项目不建议。

NSCache最常见的误用

// ❌ 没设置上限——cache 会一直长let cache = NSCache<NSStringUIImage>()cache.setObject(image, forKey: "key"asNSString)// ✅ 设上限let cache = NSCache<NSStringUIImage>()cache.countLimit = 100// 最多 100 个对象cache.totalCostLimit = 50_000_000// 最多 50MB(按 setObject:forKey:cost: 累计)

NSCache三个特性

  1. 线程安全——内置锁,不需要自己加
  2. 自动响应 memory warning——内存紧张时系统自动清理
  3. **key 必须实现 NSCopying**——cache 内部会复制 key(这点和 NSDictionary一致)

4.2 单例的代价:sharedInstance 永远不死

单例(sharedInstance)是 iOS 最常用的模式,但有个隐藏代价:全生命周期都活着,没人显式释放

classTrackingService{staticlet shared = TrackingService()var pendingEvents: [TrackEvent] = []   // 等待上报的事件functrack(_ event: TrackEvent) {        pendingEvents.append(event)    }// 网络好时一次上报;但用户长时间断网或上报失败重试逻辑没写好,// pendingEvents 只增不减 → 单例全程活着 → 内存只涨不降}

真实项目里 cache 不会粗暴写成 [String: UIImage],但单例 + 任意可增长属性都是同一类问题:埋点队列、撤销栈、未发送消息、日志缓冲……

优化方案

  • **单例内部用 NSCache**——让缓存能被系统自动清
  • 单例响应 memory pressure——主动释放可重建的数据
classMySingleton{staticlet shared = MySingleton()privatelet cache = NSCache<NSStringUIImage>()init() {        cache.totalCostLimit = 50_000_000// 监听内存警告NotificationCenter.default.addObserver(self,            selector: #selector(handleMemoryWarning),            name: UIApplication.didReceiveMemoryWarningNotification,            object: nil        )    }@objcfunchandleMemoryWarning() {        cache.removeAllObjects()    }}

4.3 ViewController 的隐性持有

UINavigationController的栈是强引用持有所有压栈的 VC:

NavigationController.viewControllers = [VC1, VC2, VC3, VC4, VC5, ...]                                        ↑ 全是 strong 引用

问题:用户从 VC1 推到 VC5,VC1-4 都在内存里没释放——它们的 ImageView、表格数据、网络请求结果都还活着

优化方向

  • VC 的 viewDidDisappear里释放重资源(比如清空大图缓存)
  • 真正退出某层逻辑时,主动 popToViewController,让中间 VC 们能被释放
  • 避免 VC 持有不该持有的东西——比如把数据库查询结果塞到 VC 属性里

4.4 第三方 SDK 的"全局状态"

第三方 SDK 通常都有自己的单例 + 全局缓存——你不知道它们缓存了多少。常见的"内存大户":

SDK 类型
典型问题
网络库
(Alamofire / 老项目 AFNetworking)
大文件响应未流式处理时整块入内存;URLCache 默认 4MB 内存 + 20MB 磁盘
图片库
(SDWebImage / Kingfisher)
内存 cache + 磁盘 cache,默认几百 MB
数据库
(Realm / FMDB)
缓存查询结果,长连接保持
Crash 监控
(Bugly / Sentry)
缓存日志、堆栈快照
统计 SDK
(GA / Firebase)
事件队列等待发送

实操建议

  • 集成时主动设置 SDK 的缓存上限(基本所有 SDK 都提供 API)
  • 监听 memory warning,主动清理 SDK 缓存(多数 SDK 提供 clearCachereset方法)
  • 上线后用 Memory Graph 抓快照看 SDK 实际占用

记住一句话:缓存是用空间换时间——没设置上限的缓存就是定时炸弹。所有缓存层(你自己的 + SDK 的)都要有明确的容量上限和清理策略。


五、Swift 特有的内存陷阱(4 个高频场景)

Swift 比 ObjC 多了几个值得专门讲的内存陷阱。

5.1 Array / Dictionary 的 COW 误用

上篇 §3.2 讲过 Swift Array 的 COW 机制——赋值不复制,写时才复制。但有个常被忽略的陷阱:

// ❌ 多线程共享 + 频繁修改classDataStore{var items: [Item] = []   // 多个线程都在改funcadd(_ item: Item) {        items.append(item)    }}// 问题:多线程同时 append 是**未定义行为**//       可能 buffer 重复释放 crash、可能数据丢失、可能内存破坏//       即使没崩,COW 频繁触发,性能也很差

修复方案

  • 加锁同步:用 DispatchQueue串行化所有写操作(最常见)
  • **用 actor**(Swift 5.5 / iOS 15+):原生并发安全的数据容器,最 Swifty
  • **用 os_unfair_lockNSLock**:性能最好的细粒度锁

注意:NSMutableArray本身也不是线程安全的——Apple 文档明确说明。换它解决不了 race condition。

classDataStore{privatelet queue = DispatchQueue(label: "store", attributes: .concurrent)privatevar items: [Item] = []funcadd(_ item: Item) {        queue.async(flags: .barrier) {self.items.append(item)        }    }}

5.2 Swift 闭包捕获的隐藏成本

Swift 闭包对值类型的捕获会"快照式"复制一份——大对象的复制成本被低估:

let bigArray = Array(0..<1_000_000)   // 8MBlet closure = {print(bigArray.count)   // 闭包捕获 bigArray}// 每次 closure 被持有/复制 → bigArray 也被持有// 如果 closure 被多个对象持有 → 多份 8MB 数组

优化

  • 把大对象抽到 reference type(class)里,闭包持有 class 引用而非值副本
  • 避免在闭包里直接捕获大值类型
// ✅ 把大对象抽到 class 里——class 是引用类型,闭包捕获的是引用,不复制内容classDataHolder{let bigArray = Array(0..<1_000_000)}let holder = DataHolder()let closure = { [weak holder] in// weak 是为了防止同一对象被多个闭包持有时形成循环print(holder?.bigArray.count ?? 0)}

5.3 actor / Task 的内存模型

Swift Concurrency 时代的新陷阱——Task 持有自己的栈帧 + 捕获的所有变量

classViewControllerUIViewController{var ongoingTasks: [Task<VoidNever>] = []   // 累积所有 taskfuncloadData() {let task = Task {let data = try? await fetchData()   // 假设 fetchData 要 30 秒self.handle(data)        }        ongoingTasks.append(task)   // ⚠️ 永远不清理    }}

问题

  • ongoingTasks永远在增长——每次调用 loadData多一个 task
  • 每个 task 持有 self(强引用)+ 任务执行期间的所有局部状态
  • 用户来回滑页面时,ongoingTasks 可能有几十个,所有相关内存都不释放

修复

  • 任务完成后从数组里移除
  • 或者用 [weak self]
  • 或者在 deinit / viewWillDisappear 时主动 cancel
deinit {    ongoingTasks.forEach { $0.cancel() }}

5.4 一个 Swift 特有反模式:@Published闭包陷阱

SwiftUI / Combine 项目里这个特别常见:

// ❌classViewModelObservableObject{    @Publishedvar text = ""init() {        $text            .debounce(for0.3, scheduler: RunLoop.main)            .sink { value inself.process(value)   // ⚠️ 闭包持有 self            }            .store(in: &cancellables)// ViewModel.cancellables 持有 sink 订阅// sink 订阅持有闭包// 闭包持有 self// → 循环    }}// ✅init() {    $text        .debounce(for0.3, scheduler: RunLoop.main)        .sink { [weakself] value inself?.process(value)        }        .store(in: &cancellables)}

记住一句话:Swift 编译期对引用关系的约束比 ObjC 严格——但它管不了你不让它管的(闭包捕获 self、Task 持有 self、Combine 订阅持有闭包)。这些场景仍然要你手动 weak。


中篇小结

到这里我们已经走完了 iOS 内存优化的"5 大场景":

  • 图片——80% 的内存来自这里。核心是 downsample(解码到显示尺寸)+ 子线程解码
  • Autorelease Pool——子线程大循环必须包,典型场景能把内存峰值压一个数量级(§2.3 的例子从几 GB → 80MB)
  • 循环引用——闭包 / delegate / Combine / Task 都是高发区,Memory Graph 5 分钟扫一遍
  • 大对象生命周期——用 NSCache自动响应内存压力,所有缓存必须设上限
  • Swift 特有陷阱——COW 多线程、闭包捕获大对象、Task 累积、Combine 闭包

记住一句话:iOS 内存优化的核心三件事——少创建(图片 downsample)、早释放(autoreleasepool / NSCache)、查泄漏(Memory Graph)。

下篇我们进入实战篇——5 类 OOM 形态怎么辨识、Memory Graph / MetricKit / Instruments 三件套实操、线上止损 Playbook、两个真实案例(WKWebView 泄漏 + Extension 24MB 限额求生),以及 8 道内存面试题与全景图。

下篇见。


参考资料:Apple WWDC 2018《iOS Memory Deep Dive》、WWDC 2021《Detect and diagnose memory issues》、SDWebImage / Kingfisher 源码、Mike Ash《Friday Q&A》系列、抖音 / 美团公开技术分享。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-15 07:51:27 HTTP/2.0 GET : https://c.mffb.com.cn/a/492035.html
  2. 运行时间 : 0.099136s [ 吞吐率:10.09req/s ] 内存消耗:4,461.79kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=6e1b4c1538dc8eecdec819a38ec3676c
  1. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/runtime/temp/cefbf809ba1a84190cb04b0cb7abcf79.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000621s ] mysql:host=127.0.0.1;port=3306;dbname=c_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000853s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000282s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000275s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000481s ]
  6. SELECT * FROM `set` [ RunTime:0.000194s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000517s ]
  8. SELECT * FROM `article` WHERE `id` = 492035 LIMIT 1 [ RunTime:0.000508s ]
  9. UPDATE `article` SET `lasttime` = 1784073087 WHERE `id` = 492035 [ RunTime:0.010786s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 65 LIMIT 1 [ RunTime:0.000342s ]
  11. SELECT * FROM `article` WHERE `id` < 492035 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000516s ]
  12. SELECT * FROM `article` WHERE `id` > 492035 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.004499s ]
  13. SELECT * FROM `article` WHERE `id` < 492035 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.007315s ]
  14. SELECT * FROM `article` WHERE `id` < 492035 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.001140s ]
  15. SELECT * FROM `article` WHERE `id` < 492035 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.003873s ]
0.100764s