Instrument和Tools

  • @warn_unqualified_access
  • 对PreView 设置不同的调试尺度
  • 怎样查找那些数据更改导致SwiftUI视图更新
  • 怎样运用Instrument分析SwiftUI代码并辨认缓慢布局

概述

文章首要共享SwiftUI Modifier的学习进程,将运用案例的方法进行阐明。内容深入浅出,Instruments和Tools首要表达东西运用,首要是对进程进行探求,可以移步Github下载code -> github案例链接

1、怎样使SwiftUI修饰符与@warn_unqualified_access一起运用更安全。

每个SwiftUI都会在某些时分犯相同的差错,并且会不止一次犯这种差错:将.someModifier()写成someModifier(),导致程序出现EXC_BAD_ACCESS。解决方案是用Swift的@warn_unqualified_access特色,这意味着假设不运用变量名或类似称谓,则无法访问特色和方法。

创建titleStyle()

//扩展一个titleStyle()方法,向视图添加一些修饰符以使其匹配子弟你主题,那么将@warn_unqualified_access在方法签名之前运用。

extension View {
    @warn_unqualified_access
    func titleStyle() -> some View {
        self
            .font(.largeTitle)
            .fontWeight(.black)
            .padding()
            .background(.blue)
            .foregroundStyle(.white)
            .cornerRadius(10)
    }
}

详细运用情况

struct FFToolingSafe: View {
    var body: some View {
        //当运用它是,正常情况下
        Text("Meta BBLv")
            .titleStyle()
        //异常情况,删去掉了.
        Text("Meta BBLv Error")
            titleStyle()
    }
}

这产生了差异,首要这是一个不合格的访问,我没有声明titleStyle()在哪里调用。所以SwiftUI假设我实际上FFToolingSafe在调用他。这意味着实际上正在调用self.padding(),因此会出现内存激增,导致出现一个无限递归视图并毕竟溃散。

SwiftUI的规则是不运用@warn_unqualified_access作用在自己的修饰符上,但是我们可以为自己构建的自定义修饰符添加它,假设出现了运用差错的情况下(比如删去了"."),这个时分SwiftUI会为你添出提示:

  • Use of ‘titleStyle’ treated as a reference to instance method in protocol ‘View’
  • Use ‘self.’ to silence this warning

Xcode贴图

SwiftUI根底篇Instruments和Tools

2、对PreView 设置不同的调试尺度

构建程序时,要对全部的支撑机型进行匹配,防止出现不同屏幕间的size差异。比如在大一些的屏幕(iPhone 14 Pro Max)上面布局是OK的,下放到小一些的屏幕(iPhone 14 Pro)出现问题。在SwiftUI中,全部组件自身都习惯Dynamic Type Size,这是非常完美的,但是假设你想在Preview中也想要预览不同size又该怎样操作呢?

Xcode 14

在Xcode 14的版别中,关于preview的设置还可以添加.environment(\.sizeCategory, .extraSmall)来设定多预览。在Xcode 14从前的版别不知道详细情况怎样,未研讨SwiftUI。

struct ContentView_Previews: PreviewProvider {
   static var previews: some View {
      Group {
         ContentView()
            .environment(\.sizeCategory, .extraSmall)
      }
   }
}

Xcode Version 15.0 beta 8 (15A5229m)

在此版别中,因为Swift现已添加了宏的相关内容,目前Preview经过宏来完结的。 经过检验,当想要在Xcode15中预览多个画面,也可设置多个宏。无法在#Preview下声明任何特色,和运用上述的方法。

#Preview {
    FFToolingDiffenertSize()
}
#Preview("Meta BBLv") {
    FFToolingDiffenertSize()
}

运用方法也愈加简洁,更改为可视化操作

SwiftUI根底篇Instruments和Tools

Xcode操作贴图

3、怎样查找那些数据更改导致SwiftUI视图更新

SwiftUI供应了一个特别的、仅供调试的方法,可以运用它来辨认导致视图从头加载自身的更改。该方法专门用于调试,不应在body实际程序中声明。但是在check视图正在重复调用特色还不承认原因时运用。 该方法是Self._printChanges(),并且应在特色内部调用。这意味着你或许暂时需求添加闪现返回来发挥惯例的代码视图。

class EvilStateObject: ObservableObject {
    var timer: Timer?
    init() {
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in
            if Int.random(in: 1...5) == 1 {
                self.objectWillChange.send()
            }
        })
    }
}
extension ShapeStyle where Self == Color {
    static var random: Color {
        Color(
            red: .random(in: 0...1),
            green: .random(in: 0...1),
            blue: .random(in: 0...1)
        )
    }
}
struct FFToolingSwiftUIChange: View {
    @StateObject private var eviObject = EvilStateObject()
    var body: some View {
        let _ = Self._printChanges()
        //经过设置背景色可以直观的查询body视图特色何时被调用。现象是假设Body被多次调用,那么视图背景色也会改动多次
        Text("META BBLV")
            .background(.random)
    }
}

监督视图的改写

4、怎样运用Instrument分析SwiftUI代码并辨认缓慢布局

Xcode的Instruments东西附带了一组超卓的SwiftUI分析功用,使我们可以承认视图重回的频率、核算视图主体缓慢的次数,甚至情况在实时改动的情况。

SwiftUI根底篇Instruments和Tools

创建检验条件

创建一个每0.01秒触发一次的计时器,并有一个闪现随机值UUID的视图和一个button

class FrequentUdater: ObservableObject {
    var timer: Timer?
    init() {
        timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true, block: { _ in
            self.objectWillChange.send()
        })
    }
}

检验ContentView

代码非常简单,有一个每0.01s就触发的Timer,动态切换UUID,一起创建了一个Button,点击时tapCount+1

struct FFToolingInstruments: View {
    @StateObject private var updater = FrequentUdater()
    @State private var tapCount = 0
    var body: some View {
        VStack {
            Text("\(UUID().uuidString)")
            Button("Tap count: \(tapCount)") {
                tapCount += 1
            }
        }
    }
}

检验进程

默许情况下,SwiftUI东西会告知我们许多数据:

  • 在此期间创建了多少视图以及创建它们所用的时间(View Body)
  • 视图的特色是什么以及它们怎样随时间改动(View Properties)
  • 产生了多少次核心动画commit( Core Animation Commits)
  • 每个函数调用详细花费了多少时间(Time Profiler)

这些东西都可以协助确诊和解决SwiftUI应用程序中的功用问题。

SwiftUI根底篇Instruments和Tools

View Body

当选择针对View Body进行探求时,Instroments将效果分解为SwiftUI和我自己的项目,前者是原始类型(如文本按钮等),后者是我的自定义类型视图。在我的例子中,“FFToolingInstruments”是我的自定义视图

SwiftUI根底篇Instruments和Tools

在此区域,重要的是Count数据以及均匀持续时间(每个事物被创建了多少次以及花费的时长)。我这儿做的是一个0.01的Timer,我暂时将它当作一个压力检验,这样会看到非常高的count,因为布局非常简单,在上面效果看来。均匀创建时间是微秒等级。

View Properties(跟踪情况改动)

选择View Properties,这儿闪现了全部视图的全部特色,包含当时的值和全部从前的值。因为我为程序添加了一个Button,就是用来调试这儿的,每次点击都会使tapCount+1。查找State<Int>的Property Type。这儿有一个小技巧,当你选择停止检验的时分,在时间轴的最后会有一个倒三角图形的时间线符号,拖动这根时间线,就可以看见你在检验进程中的tapCount跟着Button点击+1的作用了。

Core Animation Commits(辨认慢速制作)

在SwiftUI中,可以直接运用Metal来提高功用,但大多数时分SwiftUI都运用Core Animation进行烘托。运用Instroments内置的Core Animation分析东西可以对其进行分析。当多个更改一起放入一个事物时,core Animation作用最佳。可以在一个 事物中有用的堆叠一系列的作业,再要求CA继续烘托,那么这个进程称为commit事物。

因此,当Instruments向我标明Core Animation的commit时,这代表着很昂贵的价值,我无法描绘,就像Ben老头讲的dirty memory一个道理,这儿是一个dirty commit。它真正向我们展现的是SwiftUI因为更新而被逼从头制作屏幕上的像素的次数。理论上,只有当App的实际情况导致不同的视图层次结构时,才会产生这种情况,因为SwiftUI应该可以将body特色的新输入与旧输出进行比较。

SwiftUI根底篇Instruments和Tools

Time Profiler(查找用时较长的函数)

Time Profiler精确的向我闪现了代码每个部分花费多少时间。这与Instruments中惯例时间分析器的作业原理相同。

  • 默许情况下,右侧扩展的详细信息窗口闪现最重要的库房跟踪,
  • 在左面,可以看到全部的线程,以及可让你深入了解它们调用的函数以及这些函数的公开指示器。
  • 可以对“Call Tree”进行过滤,选择“Hide System Libraries”可以隐藏系统库,这是才智闪现我自己编写的代码。
  • 还可以对“Call Tree”进行再次过滤,选择“invert Call Tree”进行树的回转,以便更清晰的检查函数。

SwiftUI根底篇Instruments和Tools