Skip to content
Calvin's Blog

摸鱼精选第 20 期

Oct 1, 2022 — Reading

1. The SwiftUI Layout Protocol

Part 1 – The Basics:

Part 2 – Advanced Layouts:

本文探讨了 iOS 的 Exception 捕获原理,对了解 Bugliy 之类的工具有些帮助。

C 语言里的高阶函数,实际是函数指针的运用。

Uber 公司推出的 Go 语言规范,网友翻译的中文版,原版点击这里 (opens in a new window)

作者 Michael Long 写了一系列关于 SwiftUI 的文章,涉及应用开发、架构设计等。

本文探讨了 iOS 开发从命令式 UIKit 到声明式 SwiftUI 的架构设计的进化过程。

在传统的 UIKit 开发中,MVVM 事实上已经占据大部分场景了,当然还是有些 MVP、RIBs、VIPER 的存在。而响应式 Rx 也已经深入人心,基本上和 MVVM 绑定在一起了,比如常见的框架 ReactiveObjc 和 RxSwift 之类的框架。

来到 SwiftUI 的世界,MVVM 可以开箱即用了。@State 、用于本地的状态更新,状态变化立马刷新 UI,Binding 用于双向状态变化。这样,传统的 MVC 已经无需实现了,不必在变量的didSet方法再写刷新 View 的更新操作了。更复杂的场景,我们有ObservableObject,这个就有些类似ViewModel了。

Combine 框架的出现,又对 RxSwift 等响应式框架进行了降维式打击,RxSwift 的大部分 operator 都能在 Combine 找到对应的,而且使用更方便,性能也更好。

作者创建了一个基于 Clean 架构的 Demo (opens in a new window),还有一个 mvvm (opens in a new window) 架构的分支进行对比。

作者认为 SwiftUI 本质上是基于 Elm (opens in a new window) 架构,早在 2017 年就有人尝试在 Swift 中应用 Elm 架构。

Elm 架构由三部分组成:

SwiftUI 和 Elm 唯一不同就是 Update 部分,但这里我们可以使用 Redux 来实现。

路由,比如在 MVVM-R 或 VIPER 里面的 Coordinator,在 SwiftUI 里面变的无关重要。我们先来看路由解决的两个问题:

首先 SwiftUI 的 View 都是 Struct 类型,都是静态的,在编译期就已经决定了。而且导航也是 View 的一种,通常导航状态改变通过Bindings 来实现,所以动态导航在 SwiftUI 里面无法实现。

View 的解耦就更不需要了,SwiftUI 可以将 view A 直接指向 view B。@ViewBuilderAnyView 也可以解耦 View。

最后,作者提出了 Clean 的架构设计:

整个配置过程还是有点复杂的,Clang 完整设置下来,配置文件很多,但是懂了之后,最终的效果也不错,毕竟是免费的。

本系列文章旨在向读者详细介绍用于动态检测 C++ 程序内存错误的 Sanitizers 工具集,由于内容较多,故分多篇来进行讲述。这是本系列文章的第一篇,它提纲挈领地概述了 Sanitizers 工具集。除此之外,本文还会带领读者回顾内存的基础知识、C++ 语言标准中关于内存的说明,目的是为后续的内容打下理论基础。

作者做 C++ 后台开发多年的沉淀总结,内容很多,主要针对 C++的特性的用法、编程技巧等内容。

一个专门讲内存管理的网站,也包含一些 GC 的东西。

Swift 中 class 对象相等比较有两种:

下面是===的内部实现:

public func === (lhs: AnyObject?, rhs: AnyObject?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return ObjectIdentifier(l) == ObjectIdentifier(r)
case (nil, nil):
return true
default:
return false
}
}

===两边都有值的时候,会用ObjectIdentifier再封装一下比较,下面是ObjectIdentifier的实现逻辑:

public struct ObjectIdentifier {
internal let _value: Builtin.RawPointer
public init(_ x: AnyObject) {
self._value = Builtin.bridgeToRawPointer(x)
}
public init(_ x: Any.Type) {
self._value = unsafeBitCast(x, to: Builtin.RawPointer.self)
}
}
extension ObjectIdentifier: Equatable {
public static func == (x: ObjectIdentifier, y: ObjectIdentifier) -> Bool {
return Bool(Builtin.cmp_eq_RawPointer(x._value, y._value))
}
}
extension ObjectIdentifier: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(Int(Builtin.ptrtoint_Word(_value)))
}
}

可以看到ObjectIdentifier 内部实现了 Equatable,比较的内容就是 rawPointer 指针。

另外,ObjectIdentifier 还可以用于元类型的封装,比如ProfileViewController.self当构造函数参数。当你使用Set当容器时,它的contains函数默认使用==进行是否相等比较,如果直接讲 class 对象存到 Set ,需要对象实现 Equtable 协议才行,这时就可以用 ObjectIdentifier 包装一下,存到 Set 里面。