首页 的iOS& Swift Tutorials

Swift的乐器教程:入门

在本Xcode教程中,您将学习如何使用Instruments来配置和调试iOS应用中的性能,内存和参考问题。

5/5 5个评分

  • Swift 5,iOS 14,Xcode 12
更新说明:Lea Marolt Sonnenschein更新了针对iOS 14,Xcode 12和Swift 5.2的本教程。 法布里佐·布兰卡蒂(Fabrizio Brancati)和Nicholas Sakaimbo撰写了较早的更新,Matt Galloway撰写了原始更新。

除了通过添加功能来改进其应用开奖结果3d外,所有优秀的应用开奖结果3d开发人员还应该做的一件事是:检测代码!这个Xcode 仪器 本教程将向您展示如何使用Xcode随附的Instruments工具的最重要功能。

在本教程中,您将学习:

  • 什么是工具及其包含的工具
  • 配置和自定义仪器的方法
  • 如何检查代码中的性能问题,内存问题,参考周期和其他问题
  • 调试这些问题的最佳方法

您可以通过浏览现有的应用开奖结果3d并使用Instruments对其进行改进,就像使用自己的应用开奖结果3d一样!

入门

使用以下链接下载项目资料 下载资料 本教程顶部或底部的按钮。

此示例应用使用 Flickr API 搜索图像。要使用API​​,您需要一个API密钥。对于演示项目,您可以在Flickr的网站上生成示例密钥:

  1. //identity.flickr.com 并创建一个新的Flickr帐户,或使用您现有的帐户登录。
  2. 成功登录后,转到 Flickr API资源管理器.
  3. 查找并单击 通话方式... 在页面底部。
  4. 这将产生一个 网址 页面最底部的链接看起来像:
    //www.flickr.com/services/rest/?method=flickr.photos.search
    &api_key=f0589d37afc0e29525f51ccb26932a06
    &format=rest
    &auth_token=72157717064637163-20d89cb35333d1eb
    &api_sig=80ada3ca6dba49f7fcc9ced2743de537
    
  5. Copy the API key from the 网址. You can find this by looking for the number between &api_key= 和 the next & you see. In the above example, the API key is f0589d37afc0e29525f51ccb26932a06.

要更新项目,请打开 FlickrAPI.swift 并将现有的API密钥值替换为新值。

注意:API密钥每天都会更改,因此有时您需要重新生成一个新密钥。只要密钥无效,该应用开奖结果3d就会通知您。

生成并运行,执行搜索,单击结果,您将看到类似以下的内容:

入门项目

玩该应用开奖结果3d并查看其基本功能。您可能会认为,一旦UI看起来不错,该应用就可以提交商店了。但是您将看到使用Instruments可以为您的应用添加的价值。

本教程的其余部分将向您展示如何查找和修复应用开奖结果3d中仍然存在的问题。您将看到Instruments如何使调试问题变得更加轻松!

分析时间

您要看的第一个乐器是 时间分析器。在确定的时间间隔,Instruments停止开奖结果3d的执行,并在每个正在运行的线程上进行堆栈跟踪。您可以将其视为单击Xco​​de调试器中的暂停按钮。

这是Time 个人资料r的预览:

时间分析器-调用树

此屏幕显示 呼叫树。调用树显示了在应用开奖结果3d中执行各种方法所花费的时间。每行是开奖结果3d执行路径遵循的不同方法。仪器工具通过计算分析器在每种方法中停止的次数来估算每种方法所花费的时间。

例如,如果您以1毫秒的间隔进行100个采样,并且在10个采样中的某个特殊方法出现在堆栈的顶部,则可以推断出该方法花费了该应用大约10%的总执行时间(10毫秒)。这是粗略的近似值,但是可以用!

注意:请始终在实际设备(而不是模拟器)上配置您的应用开奖结果3d。 iOS模拟器具有Mac的全部功能,而设备具有移动硬件的所有限制。也就是说,您的应用在模拟器中似乎运行良好,但是一旦在真实设备上运行,您可能会发现性能问题。

因此,事不宜迟,该花些时间进行检测了!

仪器仪表

在Xcode的菜单栏中,选择 产品▸简介 或按 Command-I。这将构建应用开奖结果3d并启动Instruments。您会看到一个选择窗口,如下所示:

Xcode时间分析器选择

这些是Instruments随附的所有不同模板。

选择 时间分析器 仪器并单击 选择 打开一个新的Instruments文档。单击左上角的录制按钮以开始录制并启动该应用开奖结果3d。 macOS可能会要求您输入密码以授权Instruments分析其他进程。别担心,可以在这里安全提供!

在“工具”窗口中,您可以看到时间在向上计数,并且在屏幕中心的图形上方有一个小箭头从左向右移动。这表明该应用开奖结果3d正在运行。

现在,开始使用该应用开奖结果3d。搜索一些图像,然后向下钻取一个或多个搜索结果。您会注意到进入搜索结果的速度很慢,并且滚动浏览搜索结果列表很烦人。这是一个笨拙的应用!

好吧,您很幸运,即将开始进行修复!但是首先您要快速了解一下Instruments中的内容。

通过切换工具栏右侧的视图选择器,确保所有细节视图均已打开:

仪器视图选择器

这样可以确保所有面板均打开。现在,研究以下屏幕截图:

时间分析器主窗口

这是您所看到的:

  1. 录音控制:“记录”按钮停止并启动当前正在测试的应用。暂停按钮可暂停应用开奖结果3d的当前执行。
  2. 运行计时器:计时器会计算已配置的应用开奖结果3d已运行了多长时间以及已运行了多少次。上面的屏幕截图是第二次运行, 运行2之2.
  3. 乐器轨道:这是时间分析器轨道。您将在本教程的后面部分详细了解图表的详细信息。
  4. 详细面板:这显示有关您正在使用的特定乐器的主要信息。在这种情况下,它将显示使用最多CPU时间的服务器。

    在详细信息面板的顶部,单击 个人资料 然后选择 样品.

    配置文件与样本选择器

    在这里您可以查看每个样本。点击一些样本;您会看到捕获的堆栈跟踪出现在 扩展细节 检查员在右边。切换回 个人资料 完成后。

  5. 检查员面板:有两名检查员— 扩展细节运行信息 -稍后您将详细了解。

现在您已经有了一个概述,是时候深入探讨了!

深钻

执行图像搜索并深入研究结果。

上下滚动列表几次,以便在Time 个人资料r中获得大量数据。请注意,屏幕中间的数字正在更改,并且图形已填写。这告诉您应用开奖结果3d正在使用CPU周期。

在模拟器中,它看起来可能很微妙,但是请注意它的不连贯性以及在上下滑动时滚动如何错开。直到它像黄油一样滚动之前,任何收藏视图都无法交付!

为了帮助查明问题,您将设置一些选项。请点击 停止,然后在详细信息面板下方,单击 呼叫树。在出现的弹出窗口中,选择 线程分开, 反转呼叫树隐藏系统库.

时间分析器调用树设置

以下是每个选项对“详细信息”面板中显示的数据的处理方式:

  • 按州分开:此选项按应用开奖结果3d的生命周期状态对结果进行分组,是一种检查应用开奖结果3d正在进行的工作量和时间的有用方法。
  • 线程分开:这将线程分开,使您能够了解哪些线程占用了大量CPU。
  • 反转呼叫树:此选项在堆栈跟踪中首先显示最近的帧。
  • 隐藏系统库:选择此选项后,您只会看到自己应用中的符号。选择此选项通常很有用,因为您不能对系统库使用多少CPU进行太多操作。
  • 展平递归:此选项显示递归函数(即调用自身的函数),每个堆栈跟踪中都有一个条目,而不是多次。
  • 主要功能:启用此功能后,Instruments会将在函数中花费的总时间视为该函数内的时间之和,以及该函数调用的函数所花费的时间。因此,如果函数A调用了B,则Instruments将报告A的时间报告为在A中花费的时间 花费在B上的时间。这很有用,因为它使您每次下降到调用堆栈时都能选择最大的时间数字,从而使您最耗时的方法归零。

扫描结果以找出哪些行中的百分比最高 重量 柱。与行 主线 正在消耗大量的CPU周期。通过单击文本左侧的小箭头来展开该行,并一直查看直到您看到自己的方法中标有“人”符号的一种。尽管某些值可能会有所不同,但条目的顺序应与下表所示相似:

调用树结果

好吧,那看起来并不好。该应用开奖结果3d花费大量时间创建带有“色调”滤镜的UIImage的缩略图。表格加载和滚动是UI中最笨拙的部分,并且表格单元不断更新,这对您来说应该不会太令人震惊。

要了解有关该方法中发生的事情的更多信息,请双击表中其行以拉出以下视图:

时间分析器代码

withTonalFilter is a property extension on UIImage, 和 it spends a lot of time invoking the method that creates the CGImage output after applying the image filter.

There’s not much you can do to speed this up. Creating the image is an intensive process 和 takes as long as it takes. Try stepping back to see where the app calls withTonalFilter. 请点击 在代码视图顶部的面包屑路径中,返回到先前的屏幕:

呼叫树面包屑

Now click the small arrow to the left of the withTonalFilter row at the top of the table. This will show the caller of withTonalFilter — you may need to unfold the next row too. When profiling Swift, there will sometimes be duplicate rows in 呼叫树 which are prefixed with @objc. You’re interested in the first row that’s prefixed with the “person” icon, which indicates it belongs to your app’s target:

音调过滤器调用树

In this case, this row refers to (_:cellForItemAt:) in SearchResultsViewController.swift. Double-click the row to see the associated code from the project.

第71行的项目单元格代码

Now you can see what the problem is. Take a look at line 71: Creating a UIImage with the tonal filter takes a long time to execute, 和 you create it from collectionView(_:cellForItemAt:).

分担工作

To solve this, you’ll do two things: First, offload the image filtering onto a background thread with DispatchQueue.global().async. Second, cache each image after it’s generated. There’s a small, simple image caching class — with the catchy 名称 ImageCache — included in the starter project. It stores 和 retrieves images in memory with a given key.

现在,您可以切换到Xcode,并在Instruments中手动找到要查看的源文件。但有一个方便 在Xcode中打开 仪器中的按钮。在代码上方的面板中找到它,然后单击它:

在Xcode中打开按钮

你去! Xcode将在正确的位置打开。繁荣!

Now, within collectionView(_:cellForItemAt:), replace the call to loadThumbnail(for:completion:) with the following:

ImageCache.shared.loadThumbnail(for: flickrPhoto) { result in
  switch result {
  case .success(let image):
    if resultsCell.flickrPhoto == flickrPhoto {
      if flickrPhoto.isFavorite {
        resultsCell.imageView.image = image
      } else {
        // 1
        if let cachedImage =
          ImageCache.shared.image(forKey: "\(flickrPhoto.id)-filtered") {
          resultsCell.imageView.image = cachedImage
        } else {
          // 2
          DispatchQueue.global().async {
            if let filteredImage = image.withTonalFilter {
              ImageCache.shared
                .set(filteredImage, forKey: "\(flickrPhoto.id)-filtered")

              DispatchQueue.main.async {
                resultsCell.imageView.image = filteredImage
              }
            }
          }
        }
      }
    }
  case .failure(let error):
    print("Error: \(error)")
  }
}

该代码的第一部分与之前相同,并从网络上加载Flickr照片的缩略图。如果照片是收藏夹,则单元格将显示缩略图而无需修改。但是,如果照片不是您的最爱,则会应用色调滤镜。

这是您更改内容的地方:

  1. 检查图像缓存中是否存在针对该照片的过滤图像。如果是,则显示该图像。
  2. 如果没有,请分派调用以使用色调滤镜创建图像到后台队列。这允许UI在过滤器运行时保持响应状态。筛选器完成后,将图像保存在缓存中并更新主队列上的图像视图。

这会照顾到过滤后的图像,但是仍然有原始的Flickr缩略图需要解析。打开 Cache.swift 和 find loadThumbnail(for:completion:). Replace it with the following:

func loadThumbnail(
  for photo: FlickrPhoto, 
  completion: @escaping FlickrAPI.FetchImageCompletion
) {
  if let image = ImageCache.shared.image(forKey: photo.id) {
    completion(Result.success(image))
  } else {
    FlickrAPI.loadImage(for: photo, withSize: "m") { result in
      if case .success(let image) = result {
        ImageCache.shared.set(image, forKey: photo.id)
      }
      completion(result)
    }
  }
}

这类似于您处理过滤图像的方式。如果缓存中已存在图像,则立即使用缓存的图像调用完成闭包。否则,请从Flickr加载图像并将其存储在缓存中。

Command-I 再次在Instruments中运行该应用。请注意,这次Xcode不会询问您使用哪种乐器。这是因为您仍然为应用开奖结果3d打开了一个窗口,并且Instruments假设您想使用相同的选项再次运行。

再进行几次搜索。用户界面现在不那么笨拙!该应用开奖结果3d现在在后台应用图像过滤器并缓存结果,因此图像仅需过滤一次。你会看到很多 dispatch_worker_threads 在调用树中。这些解决了应用图像滤镜的繁重任务。

看起来很棒!现在该发货了吗?还没!

分配,分配,分配

那么,接下来您将查找哪些错误?

项目中隐藏了一些您可能尚未发现的东西。您可能听说过内存泄漏。但是您可能不知道的是实际上存在两种泄漏:

  1. 真实内存泄漏:当对象不再被任何东西引用但仍被分配时,就会发生这些情况。这意味着内存将永远无法重用。

    即使借助Swift和ARC帮助管理内存,最常见的内存泄漏还是 保留周期, 要么 强参考周期。当两个对象相互之间拥有强引用时,就会发生这种情况,从而使每个对象都不会释放另一个对象。结果,他们的记忆永远不会释放。

  2. 无限的内存增长:当连续分配内存并且从没有机会释放内存时,会发生这种情况。如果继续取消选中该选项,则会耗尽内存。在iOS上,这意味着系统将终止您的应用。

现在,您将探索 分配 仪器。该工具为您提供有关您的应用开奖结果3d创建的所有对象以及支持它们的内存的详细信息。它还显示您保留每个对象的计数。

仪器分配

要以新的乐器配置文件重新开始,请退出“乐器”应用。不必担心保存此特定运行。按 Command-I 在Xcode中,选择 分配 从列表中,然后按 选择.

仪器分配

片刻之后,您会看到 分配 仪器。它看起来应该很熟悉,因为它看起来很像Time 个人资料r。

仪器分配开始

单击左上角的记录按钮以运行该应用开奖结果3d。就本教程而言,请仅注意 所有堆和匿名VM 跟踪。

分配开始

在分配运行的情况下,在应用开奖结果3d中执行五种不同的搜索。

搜索结果

确保搜索得到一些结果,然后等待几秒钟,让应用稍作调整。

所有堆和匿名VM都在上升

您是否注意到 所有堆和匿名VM 轨道一直在上升?这告诉您应用正在分配内存。此功能将指导您发现无限的内存增长。

世代分析

您要执行的是 世代分析。为此,请单击详细信息面板底部标有“ 标记生成:

标记生成按钮

您会看到一条红旗出现在曲目中,如下所示:

红旗

生成分析的目的是多次执行一个动作,并查看内存是否以无限的方式增长。打开搜索结果,等待几秒钟以加载图像,然后返回主页。再次标记一代。重复执行此操作以进行不同的搜索。

检查了几次搜索后,Instruments将如下所示:

世代

你变得可疑了吗?请注意,每次搜索时蓝色图表的上升趋势。那不好但是等等,内存警告呢?你知道这些吧?

模拟内存警告

内存警告是iOS告知应用开奖结果3d内存部门出现问题的一种方法,您需要清除一些内存。

这种增长可能不是由于您的应用开奖结果3d引起的。这可能是UIKit深入内存的原因。让系统框架和您的应用有机会先清除它们的内存,然后再将它们指向任何一个。

通过选择模拟内存警告 文档▸模拟内存警告 在仪器的菜单栏中,或者 调试▸模拟内存警告 从模拟器的菜单栏中您会发现内存使用量略有下降或根本没有下降-当然不会回到应有的水平。因此,某处仍然存在无限的内存增长。

您在检查搜索的每次迭代之后都标记了一个世代,这样您就可以看到每个世代之间分配了什么内存。在详细信息面板中查看,您会看到很多代。

在每一代中,您将看到标记该世代时已分配并仍驻留的所有对象。由于标记了上一代,因此后代将仅包含对象。

看着那(这 成长性 专栏,您肯定会看到某处确实有增长。打开其中的几代,您将看到以下内容:

世代分析

哇,有很多东西!你从哪里开始?

简单。点击 成长性 标头按大小排序。确保最重的物体在顶部。在每一代的顶部附近,您会看到一行标记为 虚拟机:CoreImage,听起来很熟悉!单击左侧的箭头 虚拟机:CoreImage 显示与此项目关联的内存地址。选择第一个内存地址以在 扩展细节 右侧面板上的检查器:

扩展细节检查器

此堆栈跟踪显示了创建此特定对象的时间。灰色的堆栈跟踪部分位于系统库中。黑色部分在您的应用代码中。

Hmm, something looks familiar: Some of the black entries show your old friend collectionView(_:cellForItemAt:). Double-click on any of those entries 和 仪器 will bring up the code in its context.

Take a look through the method. It calls set(_:forKey:) on ImageCache.shared. Remember, this method caches an image in case it’s used again in the app. That sounds like it could be a problem!

再次点击 在Xcode中打开 跳回Xcode。打开 Cache.swift 和 take a look at the implementation of set(_:forKey:):

func set(_ image: UIImage, forKey key: String) {
  images[key] = image
}

这会将图像添加到字典,并在Flickr照片的照片ID上键入。但是您会注意到该图片永远不会从该词典中清除!

那就是无限内存增长的来源。一切都按预期进行,但是该应用开奖结果3d永远不会从缓存中删除内容-它只会添加内容!

To fix the problem, have ImageCache listen for the memory warning notification UIApplication fires. When ImageCache receives this, it must be a good citizen 和 clear its cache.

To make ImageCache listen for the notification, open Cache.swift 并将以下初始化开奖结果3d添加到该类:

init() {
  NotificationCenter.default.addObserver(
    forName: UIApplication.didReceiveMemoryWarningNotification,
    object: nil,
    queue: .main) { [weak self] _ in
      self?.images.removeAll(keepingCapacity: false)
  }
}

This registers an observer for UIApplication.didReceiveMemoryWarningNotification to execute a closure that clears images.

所有代码所需要做的就是删除缓存中的所有对象。这样可以确保不再保留任何图像并将它们释放。

要测试此修复开奖结果3d,请再次启动Instruments,然后重复之前执行的步骤。别忘了 模拟内存警告 在最后。

注意:进行概要分析之前,先从Xcode生成并运行。如果您进行配置,有时Xcode似乎不会将模拟器中的应用开奖结果3d版本更新为最新版本。

这次,世代分析应如下所示:

分配已解决

出现内存警告后,您会注意到内存使用率下降了。内存仍在增长,但远未达到以前。

仍然会有增长的原因是由于系统库,您对此无能为力。系统库似乎并没有释放它们的所有内存,这可能是设计使然,也可能是错误。您在应用中可以做的就是释放尽可能多的内存,而您已经做到了!

做得好!修补了另一个问题!现在该是发货的时候了!哦,等等-仍然存在您尚未解决的第一类泄漏问题。

强大的参考周期

如前所述,当两个对象彼此保持强引用时,就会发生强引用循环,从而防止两个对象都被释放。您可以使用“分配”工具以其他方式检测这些周期。

关闭仪器并返回到Xcode。选择 产品▸简介,然后选择 分配 模板。

分配模板

这次,您将不再使用世代分析。取而代之的是,您将查看在内存中徘徊的不同类型的对象的数量。点击 记录 按钮开始运行。您会看到大量对象填满了详细信息面板-太多了无法浏览!为了帮助仅缩小感兴趣的对象的范围,请键入 仪器 作为左下角字段中的过滤器。这会过滤掉所有其他值,与应用开奖结果3d“ 乐器教程”相关的值除外。

分配参考周期

仪器中值得注意的两列是 #持久# 短暂的。 #Persistent列保留当前内存中每种类型有多少个对象的计数。 “#瞬态”列显示了已存在但已释放的对象数。持久对象正在消耗内存;暂态对象不是。

查找持久对象

You’ll see a persistent instance of ViewController. This makes sense because that’s the screen you’re currently looking at. There’s also an instance of the app’s AppDelegate.

Back to the app! Perform a search 和 look at the results. A bunch of extra objects are now showing up in 仪器: SearchResultsViewControllerImageCache, among others. The ViewController instance is still persistent, because it’s needed by its navigation controller.

Now tap the back button in the app. This pops SearchResultsViewController off the navigation stack so that it’s deallocated. But it’s still showing a #持久 计数为1 分配 概要!为什么还在那里?

Try performing another two searches 和 tap the back button after each one. There are now three SearchResultsViewControllers?! Looks like you have a 强参考周期!

搜索结果仍然可见

Your main clue in this situation is that not only is SearchResultsViewController persisting, but so are all the SearchResultsCollectionViewCells. It’s likely the reference cycle is between these two classes.

幸运的是, 视觉内存调试器 Xcode 8中引入的是一个简洁的工具,可以帮助您进一步诊断内存泄漏并保留周期。 视觉内存调试器不是Xcode的工具套件的一部分,而是一个非常有用的工具,值得在本教程中包括。来自分配工具和可视内存调试器的交叉引用见解是一种强大的技术,可以使您的调试工作流更加有效。

视觉化

退出仪器。

在启动可视内存调试器之前,请启用 Malloc堆栈 像这样登录Xcode方案编辑器: 单击选项 乐器教程 在窗口顶部(在“停止”按钮旁边)。在出现的弹出窗口中,单击 然后切换到 诊断开奖结果3d。选中显示以下内容的框 Malloc堆栈 然后选择 仅实时分配,然后单击 .

视觉内存调试器方案

从Xcode启动应用开奖结果3d。与以前一样,至少执行三个搜索以累积一些数据。

现在,像这样激活可视内存调试器:

内存图

  1. 请点击 调试内存图.
  2. 请点击 the entry for SearchResultsCollectionViewCell.
  3. 您可以单击图形上的任何对象以在Inspector中查看详细信息。有多个检查器面板,例如“文件”,“历史记录”和“快速帮助”,您可以在其中查看更多详细信息。
  4. 不过,您最想看到的是 内存检查器.

可视内存调试器会暂停您的应用开奖结果3d,并显示内存中对象及其之间的引用的实时可视快照表示。

如上面的屏幕快照中突出显示的那样,视觉内存调试器显示以下信息:

  • 堆内容 (“调试导航器”窗格):这将显示您暂停应用开奖结果3d时在内存中分配的所有类型和实例的列表。单击一种类型将展开该行,以向您显示该类型在内存中的单独实例。
  • 记忆图 (主窗口):主窗口显示内存中对象的可视表示。对象之间的箭头表示它们之间的引用(强关系和弱关系)。
  • 内存检查器 (“实用开奖结果3d”窗格):这包括详细信息,例如类名和层次结构,以及引用是强还是弱。

Some rows in the Debug navigator have a number in parentheses next to them. The number indicates how many instances of that specific type exist in memory. In the screenshot above, you’ll see that after a handful of searches, the 视觉内存调试器 confirms the results you saw in the 分配 instrument. In other words, anywhere from 20 to — if you scrolled to the end of the search results — 60 SearchResultsCollectionViewCell instances for every SearchResultsViewController instance are retained in memory.

Use the arrow on the left side of the row to unfold the type 和 show each SearchResultsViewController instance in memory. 请点击ing an individual instance displays that instance 和 any references to it in the main window.

视觉内存调试器

Notice the arrows pointing to SearchResultsViewController. It looks like there are a few Swift闭包上下文 with references to the same view controller instance. Looks a little suspect, doesn’t it? Take a closer look. Select one of the arrows to display more information in the Utilities pane about the reference between one of these closure instances 和 SearchResultsViewController.

视觉内存调试器

在“内存检查器”中,您可以看到 Swift闭包上下文SearchResultsViewController is 强大. If you select the reference between SearchResultsCollectionViewCellSwift闭包上下文,您会看到此标记 强大 as well. You can also see that the closure’s 名称: heartToggleHandler. A-ha! SearchResultsCollectionViewCell declares this!

视觉内存调试器

选择 instance of SearchResultsCollectionViewCell in the main window to show more information in 内存检查器.

In the backtrace, you can see that the cell instance was initialized in collectionView(_:cellForItemAt:). When you hover over this row in the backtrace, a small arrow appears. 请点击ing the arrow takes you to this method in Xcode’s code editor.

In collectionView(_:cellForItemAt:), locate where each cell’s heartToggleHandler property is set. You’ll see the following lines of code:

resultsCell.heartToggleHandler = { _ in
  self.collectionView.reloadItems(at: [indexPath])
}

当用户在集合视图单元格中轻按“心脏”按钮时,将执行此关闭操作。这是强大的参考周期所在,但是除非您之前遇到过,否则很难发现。多亏了视觉内存调试器,您才能够一直追踪到这段代码!

The closure cell refers to the instance of SearchResultsViewController using self, which creates a 强大 reference. The closure 捕获 self. Swift actually forces you to explicitly use the word self in closures, whereas you can usually drop it when referring to methods 和 properties of the current object. This helps you be more aware of the fact you’re capturing it. SearchResultsViewController also has a 强大 reference to the cells via its collection view.

打破那个周期

要打破强大的参考周期,请定义一个 捕获清单 作为封顶定义的一部分。您将使用捕获列表将闭包捕获的实例声明为 要么 无人的:

  • : Use this when the captured reference might become nil in the future. If the object it refers to is deallocated, the reference becomes nil. As such, it’s an optional type.
  • 无人: Use this when the closure 和 the object it refers to always have the same lifetime 和 are deallocated at the same time. An 无人的 reference can still become nil, 和 will be treated as 显式展开的可选, if accessed beyond its lifetime. So use it wisely, when you know it’s not expected to become nil at the time it’s referenced.

To fix this 强参考周期, add a 捕获清单 to heartToggleHandler, like this:

resultsCell.heartToggleHandler = { [weak self] _ in
  self?.collectionView.reloadItems(at: [indexPath])
}

Declaring self as 弱 means SearchResultsViewController can be deallocated even though the collection view cells hold a reference to it, as they’re now 弱 references. And deallocating SearchResultsViewController will deallocate its collection view 和, in turn, the cells.

在Xcode中,按 Command-I 再次构建并在Instruments中运行该应用。

在Instruments中使用再次查看该应用 分配. Remember to filter the results down to show only the classes that are part of the starter project. Perform a search, 和 navigate into the results 和 back again. You’ll see that SearchResultsViewController 和 its cells are now deallocated when you navigate back. They show transient instances, but no persistent ones.

循环坏了!装运它!

然后去哪儿?

您可以使用来下载完成的项目 下载资料 本教程顶部或底部的按钮。

现在,您已经掌握了此Instruments教程的知识,现在就去尝试自己的代码,看看会出现什么有趣的事情!另外,请尝试使Instruments成为常规开发工作流程的一部分。

经常通过Instruments运行您的代码,并在发布前对您的应用开奖结果3d进行全面扫描,以确保尽可能多地遇到内存管理和性能问题。

要获得更多的调试乐趣,请观看我们的视频课程 中级iOS调试 或阅读 先进的Apple调试& Reverse Engineering 书。

现在,去制作一些很棒的,高效的应用开奖结果3d。

希望您喜欢本教程。如果您有任何疑问或意见,请加入下面的论坛讨论!

平均评分

5/5

为此内容添加评分

5个评分

更像这样

贡献者

评论