摸鱼精选第 33 期
1. How to Automate Memory Leak Detection in Your Swift Code with XCTest
使用 XCTest 来自动化检测内存泄露。其实很简单,在测试用例结束的时候判断这个对象是否为 nil。
func test_start_doesNotCreateMemoryLeaks () {
let spy = TimerSpy()
let sut = PomodoroTimer(timer: spy, timeReceiver: { _ in })
sut.start()
addTeardownBlock {
XCTAssertNil(sut, "potential memory leak on \(String(describing: sut)")
XCTAssertNil(spy, "potential memory leak on \(String(describing: spy)")
}
}
2. How async await works internally in Swift
【深度好文】Swift async/await 的内部实现其实是封装了一个线程池来处理。
3. 最佳实践|如何使用 c++开发 redis module
Redis 在 5.0 版本开始支持以 module 插件的方式来扩展 redis 的能力,包括但不限于开发新的数据结构、实现命令监听和过滤、扩展新的网络服务等。可以说,module 的出现极大的扩展了 redis 的灵活性,也大大的降低了 redis 的开发难度。 目前为止,redis 社区已经涌现了很多 module,覆盖了不同领域,生态已经丰富起来了。它们之中大多都是使用 c 语言开发。但是,redis module 也支持使用其他语言开发,如 c++和 rust 等。本文将试着总结 Tair 用 c++开发 redis module 中遇到的一些问题并沉淀为最佳实践,希望对 redis module 的使用者和开发者带来一些帮助(部分最佳实践也适用于 c 和其他语言)。
4. 初见 Swift 宏
Swift 宏是 Swift 5.9 中引入的新特性,只支持 iOS 17+系统,用处不大。
5. Modern CSS Solutions for Old CSS Problems
使用现代 CSS 来处理老问题,在旧的 CSS 规范里需要处理某些繁琐的布局问题时,可能在现代 CSS 规范里只需要一个两行代码。
6. Prisma's Data Guide
Prisma 是一款 Node.js 的 TypeScript ORM 开源库,它在官网出了一系列数据库的基础教程。
7. iOS 点九图 NinePatch 解析
iOS 里面比较少用到点九图,主要方法是[image resizableImageWithCapInsets:insets resizingMode:UIImageResizingModeStretch];
8. Working With UIViewRepresentable
在 SwiftUI 中难免需要用到 UIKit 的一些视图,这时我们会使用 UIViewRepresentable
来封装一下。
但是在运行时经常会有这个警告:
Modifying state during view update, this will cause undefined behavior.
要避免这个警告,可以使用异步操作来刷新状态,比如在一个 MKMapView
的封装里面,
func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {
DispatchQueue.main.async {
self.parent.position = mapView.centerCoordinate
}
}
``
但是这样带来一个问题,无法拖动地图标志,`updateUIView(_:context:)` 方法不响应了,所以我们还需要判断一下位置状态,在需要的时候才刷新 UIView:
```swift
func updateUIView(_ view: MKMapView, context: Context) {
context.coordinator.parent = self
if view.centerCoordinate != position {
view.centerCoordinate = position
}
}
9. C++知识体系总结:语言核心与代码工程
本文梳理了 C++的知识体系,针对 C++的重点和难点做了细致说明,同时给出了可运行的源代码。
10. 深入理解 Flutter 图片加载原理 | 京东云技术团队
本文介绍了京东 APP 中 Flutter 探索遇到的问题以及图片的加载原理和使用过程中的一些 技巧
11. Goroutine Leaks 引发的思考(Go 语言)
作者介绍了自己在实际业务中排查 Goroutine 的内存泄露的问题。
当发生 goroutine 数量激增的时候,我们的主要排查的思路一般都是先通过直接调用 runtime.NumGoroutine()( Go 的 runtime 包里面包含了一些与 Go 运行时系统交互的操作,比如控制 goroutine 的函数。NumGoroutine 函数就是查看 goroutines 运行数量)或者使用可视化的 goroutine 数量监控工具来查看 goroutine 数量,通过对比异常时间段前后 goroutine 的数量是不是持续不正常增长(业务不在高峰期的时候突然翻 N 倍被视为异常)来确认是否是 goroutine 泄漏,一旦确认,那么接下来就可通过找到 goroutine 泄漏的问题作为切入点(忘记设置默认的请求超时时间、跟 redis、sql 交互超时、向已关闭的通道发数据等等)从而找到根因。
12. Mastering Thread Safety in Swift With One Runtime Trick
作者介绍一个在官方文档里没有出现的公共接口_modify
,可用多线程环境保证数据的安全性。
因为我们在使用wrapperProperty
时,通常会使用类似@ThreadSafe
装饰变量,get
/set
时来保证数据的安全性。
比如:
@ThreadSafe
var x: UInt64 = 0
x += 1
但是遇到 x += 1
这种操作时,其实是 x = x + 1
,先 get
后 set
。这两种操作都是独立的,但是可能在这两个操作中间另一个线程更新了 x 值。有经验的开发者会封装一个小工具来确保安全性:
public func write<V>(_ f: (inout T) -> V) -> V {
self.lock.lock(); defer { self.lock.unlock() }
return f(&self.value)
}
用的时候:
@ThreadSafe
var x: Int = 5
$x.write { $0 += 1 }
但这种方式还是不够优雅,人为干扰了正常的逻辑。下面是使用_modify
的例子:
var wrappedValue: T {
get { return value }
_modify { yield &self.value }
}
_modify
在 Swift 标准库的Array 也有应用,是原子性的,并且性能比较好,不是 copy 模式。
yield
是 swift 5.6 的新特性,可参考Yielding accessors in Swift