Swift中的内存泄漏

iPhone开发Swift基础07 内存泄漏检测 Instruments Instruments工具是用于动态跟踪和分析代码的工具,可以跟踪一个或者多个进程,并检测收集实时数据,使开发者更好地理解应用程序和操作系统的行为和性能。 追查代码中难以重现的问题 对应用程序进行性能分析 自动化测试应用程序中的代码 对应用程序进行压力测试 进行一般的系统级故障诊断 对应用程序的代码如何工作有更加深入的了解 Allocations Instruments中的Allocations工具可以显示内存中的对象占用曲线,内存当前都分配给那些对象、被分配内存对象数量以及由哪 阅读详情

原文地址

在这篇文章中,我会解释什么是内存泄漏(memory leaks),讨论循环引用(retain cycles)和其他事物。

内存泄露

这确实是我们开发者经常面对的问题之一,我们的代码越来越复杂,随着app的增长,我们也带来了泄漏。

内存泄漏会永久占用一部分内存,让它无法再使用。它是占据空间带来问题的垃圾。

有时候我们分配内存,却再也没有释放,并且也没有app引用去。因为没有对它的引用,也就没有办法释放它,这段内存就无法再使用了。

我们时不时在不断制造内存泄漏,无论是新手开发者还是业界老鸟。它和我们的经验无关。最重要的是要清除它们,让我们的app更干净,避免崩溃。为什么?因为它们很危险。

内存泄漏很危险

不仅是因为它们增加了app的内存占用(memory footprint),它们还带来了有害的副作用和崩溃。

为什么内存占用会增加呢?它直接来源于一些对象没有被释放。这些对象毫无益处。如果我们重复创建这些对象,占用的内存就会增加。这就太可怕了!这会导致内存警告,最终让app崩溃。

解释有害的副作用需要说明一些细节

设想一个对象在init中被创建时就开始听从一个通知。它响应通知,为一个数据库存储数据,播放视频,或者为分析仪器记录事件。因为对象需要被平衡,我们让它在deinit中被释放时不再听从这个通知。

如果这样的对象发生泄漏的话会发生什么呢?

它永远不会消失,会一直听从通知。每次通知传达,对象就会响应它。如果用户不断重复创建这样的对象,就会有多重实例存在。这些实例都会响应通知,并互相干涉。

这种情况下,最可能发生的就是崩溃了

多重泄漏的对象响应一个app通知,变更数据库,UI,使整个app崩溃。要想了解这类问题的重要性,你可以读一读发表在The Pragmatic Programmer.上的Dead programs tell no lies

泄漏必定会导致差劲的用户体验和App Store的低排名

泄漏是从哪来的?

泄漏可能来源于第三方SDK或者一些框架。甚至是来源于苹果中的类,比如CALayer和UILabel。这种情况下我们也做不了什么,只能等待SDK的更新或者不用它。

但是泄漏更多的是我们的代码带来的,泄漏的第一原因就是循环引用(retain cycles)。

为了避免泄漏,我们必须理解内存管理与循环引用

循环引用

引用这个词来源于Objective-C中的手动引用计数(Manual Reference Counting)。在ARC、Swift和其他对值类型的操作之前,我们都用Objective-C和MRC。可以在这篇文章中了解MRC和ARC

在那段时间,我们需要对内存管理稍加了解。需要理解关于分配,复制,保持和如何用反向操作平衡这些操作,比如释放。基本原则是:只要你创建了一个对象,你就拥有了它,并且要负责释放它。

如今事情就简单多了,但是依旧需要了解一些概念。

在Swift中,当一个对象对另一个对象有强关联,它就在引用(retain)它。这里我说的对象基本是指引用类型或者类。

结构(Struct)和枚举(Enums)是值类型。如果只有值类型,是不可能创建循环引用的。当获取并存储了值类型(结构和枚举),并不存在一个引用。值是被复制,而不是引用,尽管这个值可以含有对对象的引用。

当一个对象引用了另一个,它就拥有了它。第二个对象会始终保持存活,直到被释放。这就是强引用。只有你把属性设为空(nil),第二个对象才会被销毁。

class Server {
}

class Client {
    var server : Server //Strong association to a Server instance

    init (server : Server) {
        self.server = server
    }
}

强引用

如果A引用B,B引用A,就形成了循环引用

A || B + A || B =||

class Server {
    var clients : [Client] //Because this reference is strong

    func add(client:Client){
        self.clients.append(client)
    }
}

class Client {
    var server : Server //And this one is also strong

    init (server : Server) {
        self.server = server

        self.server.add(client:self) //This line creates a Retain Cycle -> Leak!
    }
}

一个循环引用

在本例中,释放client或者server都是不可能的

要想从内存中释放,一个对象必须先解除其所有关联性。因为如果对象本身是一个从属,它无法被释放。同样如果一个对象又一个循环引用,它就不会消失。

当循环中的一个引用是弱引用或无主引用时,循环引用会被破坏。循环必须存在,因为它是我们编码中关联性本质所必须的。问题就在于,不能所有的关联都是强的,其中一个必须是弱关联。

class Server {
    var clients : [Client] 

    func add(client:Client){
        self.clients.append(client)
    }
}

class Client {
    weak var server : Server! //This one is weak

    init (server : Server) {
        self.server = server

        self.server.add(client:self) //Now there is no retain cycle
    }
}

一个弱引用破坏循环引用

怎样破坏循环引用

当你处理类类型的属性时,Swift提供了两种方法解决强引用循环:弱引用与无主引用
弱引用与无主引用允许一个引用循环中的实例引用其他实例,而不需要保持很强的关系。这样实例就可以在不创建强引用循环的情况下互相引用。
Apple’s Swift Programming Language

弱引用:一个变量可以选择不持有对其引用对象的拥有权。弱引用可以是空(nil)

无主引用:像弱引用,无主引用对引用对象不保持很强的关系。和弱引用不同的是,无主引用总是被设定为一个值。因此,无主引用总是被设定为不可选择的类型。无主引用不可以是空

怎样使用它们

当一个闭包与其占有的实例总是互相引用,并且总是同时被释放时,我们定义闭包中的占有(capture)为循环引用。
相反地,如果被占有的引用在将来可能变为空时,我们定义这个占有为弱引用。弱引用总是选择类型,并且其引用的实例被释放时会自动变为空。
Apple’s Swift Programming Language

class Parent {
    var child : Child
    var friend : Friend

    init (friend: Friend) {
        self.child = Child()
        self.friend = friend
    }

    func doSomething() {
        self.child.doSomething( onComplete: { [unowned self] in  
              //The child dies with the parent, so, when the child calls onComplete, the Parent will be alive
              self.mustBeAlive() 
        })

        self.friend.doSomething( onComplete: { [weak self] in
            // The friend might outlive the Parent. The Parent might die and later the friend calls onComplete.
              self?.mightNotBeAlive()
        })
    }
}

弱引用VS无主引用

当我们编码时,忘记weak self这种事并不少见。我们经常在编写区块闭包(blockclosures),比如flatMap或者包含互动代码的map,或者当我们编码观察者(observers),委托(delegates)时带来内存泄漏。在这篇文章中你可以读到关于闭包中的泄漏。

怎样清除内存泄漏?

1、不要创建它们。深入理解内存管理,为你的项目制定好编码方式并遵照它。如果你很有条理,遵照编码方式,weak self的缺少就会显而易见。核心回顾(Core reviews)也是很有用的

2、使用Swift Lint。它会强制要求你遵照一个编码方式从而遵守规则1。它帮你在编译时检查早期问题。比如并非弱的,可能变为循环引用的委托变量声明。

3、在运行时检查泄漏,并让它们可见。如果你清楚某一个对象在某一时刻必须存在有多少实例,,你可以使用LifetimeTracker。它是开发模式下一个有用的工具。

4、频繁配置(Profile)app,XCode带来的内存分析工具能做的很好。看这篇文章。

5、用SpecLeaks做内存泄漏的单元测试,它又快又灵敏,让你可以便捷地建立泄漏测试。

内存泄漏的单元测试

一旦我们知道循环和弱引用是怎么工作的,我们就可以为循环引用写测试代码,就是用弱引用探测循环。通过一个对对象的弱引用,我们可以测试对象是否泄漏。

因为弱引用和其引用实例并没有强的关联,释放一个弱引用引用的实例是可能的。因为ARC会自动把引用实例被释放的弱引用设为空。

让我们看看如果对象x泄漏了会怎样。我们可以为它创建一个弱引用,并称为leakReferece。如果x从内存中释放,ARC会设leakReference为空。所以如果x泄漏,leakReference就一定不是空。

func isLeaking() -> Bool {

    var x : SomeObject? = SomeObject()

    weak var leakReference = x

    x = nil

    if leakReference == nil {
        return false //Not leaking
    }
    else{
        return true //Leaking
    }
}

测试对象是否泄漏

如果x确实发生泄漏,弱变量leakReference会指向泄漏的实例。另一方面,如果对象没有发生泄漏,在设定它为空后,它将不再存在。这种情况下,leakReference会是空。

“Swift by Sundell”在文章中解释了几种内存泄漏。这篇文章对我的写作起到很大帮助。

Swift 异步序列 AsyncStream 新“玩法”以及内存泄漏、死循环那些事儿(下) 在本篇博文中,我们通过实际代码中出现的例子介绍了 Swift 并发模型里使用异步序列可能出现的陷阱,并成功的让它们“全面瓦解”。 阅读详情

相关推荐

Swift利用URLSession下载时一个“隐藏“内存泄漏的发现与解决

一个利用URLSession实现下载功能的App中,出现了“奇怪”的内存泄漏现象,但从Xcode正常的内存调试图(Debug Memory Graph)中无法显示该泄漏。 看完本篇博文,您将了解到: - 如何使用URLSession实现下载数据 - 一般对象测试是否泄漏的简单方法 - 如何向Swift明确表达对象内存释放的意图? - 使用URLSession下载,造成内存泄漏的原因及解决方法

享受开发,颠倒银河 634

SWIFT中最常见的内存泄漏陷阱

它必须保留指向该对象的强大指针,并且即使整个viewController可能已被释放,也永远不会释放它。之所以会发生内存泄漏,是因为Swift中的闭包必须捕获作用域(即{}之间的所有东西)以及您引用的任何。您可以处理某些情况下它在闭包内部可能为零的情况。如果您尝试访问一个恰好为nil的。变量,它将使整个程序崩溃。因此,只有当您肯定变量在闭包周围时将始终存在时,才使用。如果确实有一个(或通常是一堆),则表示您有一个泄漏的物体。更新:正如中型读者所指出的那样,也可以使用。是您在Swift闭包中的朋友!

weixin_42610770的博客 801

Swift-MLeaksFinder腾讯开源的iOS平台的自动内存泄漏检测工具

MLeaksFinder腾讯开源的 iOS 平台的自动内存泄漏检测工具

XCTAssertNoLeak - 精准检测Swift内存泄漏的测试工具

XCTAssertNoLeak - 精准检测Swift内存泄漏的测试工具 1、项目介绍 XCTAssertNoLeak 是一个专门为Swift开发者设计的开源测试框架,用于帮助你在开发过程中有效地检测和预防内存泄漏问题。这个框架利用了Swift的反射(Mirror)功能,遍历对象树来查找可能存在的泄漏对象。通过直观的可视化界面,你可以清晰地看到哪个类或实例在测试后仍然存在,从而及时定位并修复潜在的...

gitblog_00098的博客 617

XCTAssertNoLeak:提供断言功能以检查Swift中的内存泄漏

XCTAssertNoLeak 提供Swift的内存泄漏测试用例。 使用镜像从遍历对象树中找到内存泄漏对象。 支持版本 Swift 支持 笔记 5.0 ○ -- 4.2(Xcode 10.2) ○ -- 4.2(Xcode 10.1) × 4.1 ○ -- 指令说明 迦太基 github "tarunon/XCTAssertNoLeak" 可可豆 将其写在Pods下方进行测试。 pod 'XCTAssertNoLeak'

Swift-XCTAssertNoLeak-提供断言函数检查Swift中的内存泄漏

XCTAssertNoLeak - 提供断言函数,检查Swift中的内存泄漏

探秘Swift世界中的内存泄露克星:Swift Leak Checker

探秘Swift世界中的内存泄露克星:Swift Leak Checker 在开发iOS或macOS应用时,内存泄露是一个让人头疼的问题,它可能导致应用性能下降甚至崩溃。今天,我们向您隆重推荐一款名为Swift Leak Checker的开源神器,这是一套旨在帮助开发者发现和解决因强引用闭包引起的内存泄漏问题的框架与命令行工具。 项目介绍 Swift Leak Checker利用强大的源码分析技术,...

gitblog_00008的博客 889

STM32 超声波模块输入捕获测距(标准库)

通道2设置为下降沿触发,触发瞬间会将CNT的记录到CCR2中,由于上升沿触发时已经将CNT置零所以此时的CNT的值就是高电平的持续时间。通道1设置为上升沿触发,触发的瞬间会将CNT的值记录到CCR1里面,同时设置通道1触发带有从模式,并把从模式设置为置零CNT。超声波传感器模块上面通常有两个超声波元器件,一个用于发射,一个用于接收。电路板上有四个引脚:VCC、GND、Trig(触发)、Echo(回应)。输入捕获原理可以看看这位大佬,感觉写的很清楚,工作电压与电流:5V,15mA。感测角度:不小于15度。

qq_62425900的博客 2082

Swift界面跳转性能瓶颈突破(4个被忽视的内存泄漏陷阱)

突破Swift界面跳转性能瓶颈,揭秘4个常被忽视的内存泄漏陷阱。涵盖UIStoryboardSegue、闭包强引用、代理未置空及通知未移除等场景,提供高效解决方案。优化应用响应速度与稳定性,提升用户体验,值得收藏。

SimSolve的博客 966

手把手教你用二茂铁催化剂搞定醇的动力学拆分(保姆级实验避坑指南)

本文详细介绍了使用平面手性二茂铁催化剂进行醇的动力学拆分的全流程,包括催化剂选择与制备、反应体系搭建与条件优化、实时监测与反应淬灭、产物分离与纯化等关键步骤。通过实战案例和避坑指南,帮助实验人员实现>98% ee的拆分效果,特别适合需要高光学纯度二级芳基醇或烯丙醇的研究者。

weixin_30500105的博客 435

手把手教你用Python实现ITRS与GCRS坐标转换(附完整代码)

本文详细介绍了如何使用Python的Astropy库实现ITRS与GCRS坐标转换。文章从环境配置、坐标对象构建入手,深入解析了transform_to方法背后的转换矩阵与地球定向参数管理,并通过卫星地面轨迹计算实战案例,帮助开发者掌握高精度空间坐标转换的核心技术与工程实践。

weixin_28335283的博客 79

Swift闭包中的内存泄漏

内存泄漏在 iOS 中是永恒的话题,如果你在开发过程中不小心对待的话,那么总有一天他会以 Crash 的形式提醒你它的存在。内存泄漏不仅破坏用户体验,而且会影响性能甚至应用的安全。既然内存泄漏如此的重要,所以这篇文章在这篇文章将说一说 Swift 闭包中的内存泄漏问题。 Apple 在文章中详细介绍了循环强引用的概念、何为内存泄漏、如何...

weixin_34220834的博客 367

f_fs.c 初始化流程(进行中)

前言 之所以选择f_fs.c作为这系列的开始,因为usb在/driver/usb下面的内容实在太多太多,如果讲各种各样点会太分散了。所以决定切入点以实际使用为主,就是f_fs了,目前我了解这是做bulk裸传输使用的,具体的通信协议自己确定。 初始化部分 开始点从这个路径下说起, /drivers/usb/gadget/function/f_fs.c 然后f_fs.c第一个使用的是include/composite.h中的宏定义function #define DECLARE_USB_FUNCTION(_n

weixin_36406973的博客 1967

[译] Swift 中的内存泄漏

原文地址:Memory Leaks in Swift: Unit Testing and other tools to avoid them. 原文作者:Leandro Pérez 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m… 译者:RickeyBoy 校对者:swants, talisk Swift 中的内存泄漏 通过单元测试等方式避免 本篇文章...

weixin_34186950的博客 726

anaconda的conda国内加速镜像源(pytorch 快速安装)

最近清华源不太好使了,所以推荐中科大镜像源,如下 1.还原原始通道 conda config --remove-key channels 2.

qq_41719643的博客 2万+

CubeMX实战:ETH+LWIP+FreeRTOS网络工程从零搭建与AC6编译优化

本文手把手教你使用STM32CubeMX从零搭建集成以太网(ETH)、LWIP协议栈和FreeRTOS的嵌入式网络工程。文章详细解析了外设配置、协议栈调优、任务规划等关键步骤,并重点分享了如何解决ARM Compiler 6(AC6)的兼容性问题,实现编译速度与稳定性的双重提升,助你快速构建稳定的网络应用原型。

n2o3p4的博客 409

SpecLeaks - Swift内存泄漏检测工具

SpecLeaks - Swift内存泄漏检测工具 基础介绍 SpecLeaks 是一个用于在Swift语言编写的项目中检测内存泄漏的开源工具。这个项目通过提供对Quick和Nimble测试框架的扩展,使得开发者能够轻松地编写针对内存泄漏的单元测试。 编程语言 Swift Ruby(用于项目配置和脚本) Objective-C(可能用于桥接或兼容) 核心功能 SpecLeaks 的核心功能是允...

gitblog_00760的博客 440

第244篇 视觉SLAM后端之BA优化——Bundle Adjustment的原理

本文介绍了SLAM后端优化中的Bundle Adjustment(BA)技术。BA通过同时优化相机位姿和3D路标点位置,最小化重投影误差,解决前端估计的累积误差问题。文章详细讲解了BA的数学原理,包括目标函数构建、Gauss-Newton法和Levenberg-Marquardt法求解过程,以及利用稀疏性和Schur补加速计算的关键技术。对比了全量BA和增量BA的优缺点,并介绍了Ceres、g2o等工程实现工具。BA作为SLAM后端的核心算法,其理解深度直接影响系统性能优化能力。

郑伟强dev的专栏 345

揭秘Swift项目中的内存泄漏陷阱:3个你必须掌握的排查与优化方案

掌握Swift项目实战中常见的内存泄漏问题,本文详解3种高效排查与优化方案,涵盖闭包循环引用、代理设置错误等典型场景。通过弱引用、调试工具和代码规范实现性能提升,实用技巧值得收藏。

ByteVein的博客 910

GB28181 —— 3、C++编写GB28181设备端,完成注册、注销、保活心跳、响应查询目录等功能(附源码)

GB28181 —— Win10下编写GB28181设备端,完成注册、注销、保活心跳、响应查询目录等功能(附源码)

1308

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Swift代理造成内存泄漏的解决办法

在swift中,使用代理 ,可能很多人会这样实现: 1.首先定义一份协议。 protocol ToolProrocol{ //代理方法 func didRecieveResults(result:Int) } 2.定义一个代理属性 //声明代理属性 var delegate : ToolProrocol? self.delegate?.didRecieve...

weixin_30294021的博客 125

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

彻底解决大模型训练卡顿:PyTorch Profiler揪出SWIFT内存泄漏元凶

你是否在使用SWIFT训练大模型时遇到过这些问题:训练过程中GPU内存占用持续攀升、训练到深夜突然OOM(内存溢出)、相同配置下模型训练速度越来越慢?这些现象背后往往隐藏着内存泄漏问题。本文将以SWIFT框架(魔搭大模型训练推理工具箱)为基础,手把手教你使用PyTorch Profiler定位并修复训练过程中的内存泄漏,让你的模型训练效率提升30%以上。 读完本文你将掌握: - 内存泄漏的3个典...

gitblog_01185的博客 1238

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Swift基础 循环引用导致内存泄漏

实例对象之间循环强引用 ARC并不是绝对安全的,当两个实例对象间相互强引用,就会造成引用循环,导致所占用的内存无法释放。 class Person{ var name : String init(personName: String) { name = personName } } class Student: Person { var classmate : Student? override init(personName: String) {

乐事派的专栏 608

iOS内存泄漏终极检测指南:MLeaksFinder快速定位Swift项目中的内存问题

MLeaksFinder是iOS开发中一款强大的内存泄漏检测工具,能够在开发阶段快速发现UIView和UIViewController对象的内存泄漏问题。这款工具由腾讯团队开源,为开发者提供了简单高效的iOS内存泄漏解决方案。🚀 ## 🔍 为什么需要MLeaksFinder? 在iOS开发中,内存泄漏是常见的问题之一,特别是在复杂的Swift项目中。传统的内存泄漏检测方法往往需要手动调试或

gitblog_00278的博客 990
上一篇: ARKit中文文档
下一篇: MultipeerConnectivity中文文档
Philm_iOS
博客等级 码龄8年 74粉丝 53原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值