Tag: 内存泄漏

没有人期望完成调查!

如何测试非调用完成处理程序方案 使用XCTestExpectation测试完成处理程序API幸福路径场景非常简单。 如果您的测试用例必须验证给定的处理程序是否未被调用,事情就会变得复杂。 如果在您模拟的其他事件之后同步调用它,则仍然很简单。 但是,如果延迟执行时间呢? 在本文中,我将向您演示一种解决方案,以100%确保不会调用给定的完成处理程序-快速可靠。 我们来谈谈提供完成处理程序的一些异步任务的单元测试,比如说从服务器下载JSON。 似乎很明显,我们必须验证在快乐路径方案中是否调用了完成。 XCTest提供了一个不错的API:只需创建一个期望,在完成时实现它,然后在测试结束时等待所有实现: 天真的解决方案 另一种方法是稍等片刻,并假设只要未完成完成(例如0.1s),它就永远不会执行。 它有两个明显的缺点: 您必须猜测要等待多长时间。 是0.1s还是0.01s? 如果设置不正确,很容易导致测试无效或不稳定。 它增加了单个测试的持续时间,默认情况下默认应尽可能快。 这两个缺点都可能导致危险的陷阱: 当测试花费的时间太长或不稳定时,您的队友(或将来的您)就会开始忽略它们,而迟早就放弃它们。 让我问一个问题:您何时能100%确定系统的某些外部部分永远不会调用完成? 答: 在没有人保留的情况下,不再有完成任务! 为了验证没有什么延迟执行块,我们必须确保所有其他实例都丢失对完成的引用。 好的,听起来很简单! 但是如何实施呢? 当然,我们不能定义对块的弱引用: ‘weak’ may only be applied to class and class-bound protocol types 。 因此,让我们定义简单的类ReferenceObserver ,它仅通知其deinit : 目前,我们对ReferenceObserver实例有两个引用: reference和capturedReference (已完成)。 注意:我们必须将虚拟分配添加到匿名变量 _ ,否则Swift根本不会捕获它。 如您所料,让我们摆脱助手参考(只是调用reference = nil )和voilà之一: 只有完成处理程序块会保留对ReferenceObserver并且一旦从内存中删除了完成处理程序, deinitCompletion自动满足deinitCompletion期望: 这是我们的最终测试: 我们最终得到了一个解决方案,该解决方案是: 不需要增加测试持续时间,因为在ReferenceObserver […]

使用XCode进行调试

手机上的内存是共享资源。 管理不当的应用程序会耗尽内存,崩溃并遭受性能急剧下降的困扰。 当我们将内存的给定部分分配给一组对象,而在使用完它们后忘记释放它,则会发生内存泄漏。 这意味着系统永远无法回收内存并将其用于其他用途,这最终意味着我们将耗尽可用内存。 尝试查找内存泄漏时,一个好的第一步是运行该应用程序,并查看Xcode中的内存使用情况图。 如果在使用应用程序时内存使用量趋于增加,则可能是某些内存没有适当释放。 内存图 Xcode内存图调试器有助于查找和修复保留周期和泄漏的内存。 激活后,它将暂停应用程序执行,并显示当前堆中的对象以及它们之间的关系以及哪些引用使它们保持活动状态。 可以通过选择“内存图”来启动“内存图”调试器 调试菜单栏中的调试器按钮。 在下面的文章中了解有关内存图调试的信息。 iOS-使用Xcode内存图调试器识别内存泄漏 来自petethedeveloper.com medium.com的交叉发布 在Xcode 9中使用内存图调试器 我从未真正花费时间来正确学习如何使用乐器。 当然,我知道如何使用Time Profiler和… medium.com 可能会有所帮助 主线程检查器 选择您的应用方案->产品>方案>编辑方案 方案1:如果主线程检查器为“未检查 ”。 在多线程环境中使用Logger。 线程清理程序告诉我们,由于多个线程正在访问我们的日志并间接访问logEventSource,因此存在竞争状态。 这将导致我们发生意外的行为,这可能很难跟踪或复制。 现在我们如何避免这种情况? 同步功能代码。 互斥锁 信号 GCD 让我们使用苹果推荐的方法: 生成的崩溃报告通常显示每个堆栈跟踪帧的内存地址,而不是源代码位置。 这使得很难确定错误在代码中发生的位置。这些调试符号包含所需的信息,以从内存地址转换为关联的源文件和行号。 象征性 符号化是将返回地址转换回人类可读的方法/文件名和行号的过程。 符号化使您更容易识别错误的根源并及时解决错误。 编译源代码时,编译器会将人类可读的源代码转换为机器可读的代码,并创建一个已编译的二进制文件。 它还会生成调试符号和映射表。 如果您看到映射表,则每个调试符号都将映射到已编译二进制文件中的确切机器指令,并映射到生成它的源代码中的行号。 有两种类型的构建,一种是调试构建,另一种是发行版本。 因此,根据构建设置,默认情况下,调试构建将调试符号存储在二进制文件中,另一方面,发布构建将调试符号存储在dSYM文件中以减小二进制文件的大小。 在构建过程中,XCode将从主可执行文件( 二进制 )中剥离所有调试信息,并将其放入名为dSYM的特殊文件中。 这有助于使我们的可执行文件更小,更易于分发给用户。 可能会有所帮助 谢谢!!!

我如何停止内存泄漏并回收150MB的内存— Swift

在iOS开发人员中,内存管理可能不是一个有趣的话题,但是如果您想将应用程序提升到一个新的水平,它需要引起注意。 内存泄漏的应用确实更有可能被iOS强制杀死。 为用户提供低于标准的体验。 好消息是,多年来,检测内存泄漏变得越来越容易。 XCode提供了一些非常好的工具,因此您可以修复并避免泄漏。 如果您在应用程序中遇到内存相关问题,或者只是想了解有关iOS中内存管理的更多信息,请在下面列出我的前3个资源。 实际上,我最近实际上已使用这些资源来修复应用程序中的主要内存泄漏。 Kaira Diagne撰写的Swift的Memory Management in Swift文章:该文章对该主题做了很好的介绍,并提供了一个内存泄漏修复程序的示例。 这个出色的Stackoverflow答案描述了如何使用XCode的Allocations Instrument,以及更新的“ Debug Memory Graph”选项。 后者绝对是救命稻草 。 这个惊人的Tweet基本上很有趣,可以捕获应用程序中的内存问题。 它解释说,您可以在XCode中放置一个断点,让您立即(仅通过播放声音)就知道UIViewController是从内存中释放的。 从第1天起将此断点添加到您的应用程序中,您肯定会在发生内存泄漏时及时发现它们。 在我的武器库中拥有这些工具后,我针对了应用程序中存在一段时间的内存泄漏。 该应用程序称为Buluttan,它是一款气象应用程序,具有自己的天气预报和警报。 在显示嵌入了Map的视图控制器时发生泄漏。 这是泄漏的发生方式: 看起来很恐怖,不是吗? 由于内存泄漏,在第二步中加载的MapKit永远不会从内存中删除。 该应用程序每次轻按都会变重,最终会从内存中消除力量。 这里发生的是第二个ViewController以某种方式创建了一个强大的参考周期。 导致无法将其从内存中删除 。 即使用户在上面的第3步中取消了地图,该地图仍在我们的记忆中。 强大的参考周期导致它不会被释放。 找出周期发生的地方实际上并不难。 通常认为强周期的原因是: 强委托:如果您在代码中使用委托模式,则很可能忘记了使用弱引用定义委托。 将委托工作的实体应具有如下定义的委托: 私有弱变量myDelegate:ImportantDelegate? =。无 如果没有weak关键字,则会立即引入一个强循环。 这也是我的问题! 我已经使用强大的委托初始化了LoaderView。 那造成了强大的循环! 2.封闭性强:大多数网络完成处理程序都作为封闭性从UI传递。 网络调用完成后,将调用这些处理程序,并且UI会自行更新。 如果您的网络处理程序实体对您的ViewController有很强的引用,则VC永远不会从内存中删除。 网络处理程序仍在指向它。 通过使用捕获列表使self成为弱引用,可以防止此情况: myNetworkHandler.fetchSomething {[弱自我](fetchedEntities)在 守卫让strongSelf =自我else {return} strongSelf.updateUI() […]

使用弱和无人修复自我关闭内存泄漏

大家好,在本文中,我只想澄清一些困惑,为什么有时我们不得不在闭包块中使用[弱自我]或[未知自我] 。 通过简单的用例,我们将对此进行讨论。 让我们开始吧。 如你所知 取消初始化 当没有SecondViewController的保留周期时,将调用该函数。 因此,以上代码没有复杂性。 您是从其他使用UINavigationController的控制器来的。 当您按下后退按钮时,控制台将显示以下消息。 因为没有保留周期,也没有内存泄漏, 所以当您返回时, deinit函数将立即触发。 现在,进入一个复杂的案例。 假设SecondViewController将从应用程序的其他部分获取通知。 因此,您需要在NotificationCenter中注册。 我们可以通过两种方式实现NotificationCenter。 一种是将选择器传递给NC,另一种是Closure。 在这里,我要关闭。 在上面的代码中,我们从NotificationCenter接收到一条消息,该消息需要显示在infoLabel中。 如果运行以上代码,则控制台如下所示。 是。 它只是空的。 好。 让我做以下更改。 您只是评论了infoLabel的设置文本,并在日志中打印了新消息。 如果您现在运行代码,则deinit函数将触发,控制台将填充“ ==========我正在腾出我的空间==========” 哦! 这到底是怎么回事。 每当我们在闭包块中使用self时 ,实际上就是在创建一个保留周期。 这意味着它的secondviewconroller保留数量增加到两个。 即使您从SecondViewController返回,也只会减少一个保留计数。 因此,SecondViewController不会脱离内存。 如何解决这个问题? 在这里,我们有两个选择。 [弱自我] [无主自我] 弱者与无人者之间的基本区别 弱:弱引用使对其引用的实例保持弱引用。 这意味着ARC不考虑对实例的引用。 请记住,如果没有其他对象对该实例进行强引用,则该实例将被释放。 无主:无主引用类似于弱引用,因为它们不对所引用的实例保持强引用。 它们的作用与弱引用相同,即避免强引用循环。 总是期望无主引用具有值,而弱引用并不期望变量具有值。 您可以将nil分配给弱对象。 如果您想了解弱和无主之间的区别,请查看此链接。 要使用弱势修复此问题,代码将更改为以下代码。 我们在闭包的开头添加了[weak self] ,并为self添加了可选绑定 。 弱引用会一直存在,直到对对象的强引用出现为止。 这意味着它不会增加保留数。 […]