0Pricing
Swift Academy · 课时

常见包装器模式(验证、缓存)

构建常见的 验证 和 缓存 包装器:保护输入、提供默认值、缓存开销较大的操作结果,并通过投影值提供辅助标志。

常见包装器模式(验证、缓存) 是 CoddyKit 上的免费 Swift Academy 课时。 这是第 3 节课,共 3 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Swift Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Swift Academy 课程共包含 3 节课。

我们将构建什么

本课将展示两种实用模式:

  • 验证:使用默认值和消息接受或修正输入。
  • 缓存:存储结果,并提供类似辅助功能的辅助项,例如clear()和isCached。

验证:非空

@NonEmpty会阻止空输入并提供默认值。投影值会报告上一次赋值是否被自动修正。

@propertyWrapper
struct NonEmpty {
    private var value: String = ""
    private(set) var lastFixed: Bool = false

    var wrappedValue: String {
        get { value }
        set {
            if newValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
                value = "N/A"          // default fallback
                lastFixed = true
            } else {
                value = newValue
                lastFixed = false
            }
        }
    }

    var projectedValue: Bool { lastFixed } // $name -> was fixed?

    init(wrappedValue: String) { self.wrappedValue = wrappedValue }
}

struct Profile {
    @NonEmpty var displayName: String = "  "
}
var p = Profile()
print(p.displayName)  // "N/A"
print(p.$displayName) // true (was fixed)

验证:基于规则

@Validated是泛型构造,接受一个规则闭包和一个备用值。您可以将它用于长度、范围或自定义检查。

@propertyWrapper
struct Validated<Value> {
    private var value: Value
    private let rule: (Value) -> Bool
    private let fallback: Value
    private(set) var lastValid: Bool = true

    var wrappedValue: Value {
        get { value }
        set {
            if rule(newValue) {
                value = newValue
                lastValid = true
            } else {
                value = fallback
                lastValid = false
            }
        }
    }

    var projectedValue: Bool { lastValid } // $prop -> is valid?

    init(wrappedValue: Value, rule: @escaping (Value) -> Bool, fallback: Value) {
        self.rule = rule
        self.fallback = fallback
        self.value = wrappedValue
        self.lastValid = rule(wrappedValue)
        if !lastValid { self.value = fallback }
    }
}

struct Form {
    @Validated(rule: { $0.count >= 3 }, fallback: "???")
    var nickname: String = "hi"
}
var f = Form()
print(f.nickname)   // "???" (invalid -> fallback)
print(f.$nickname)  // false

缓存:延迟记忆化

@LazyCached会根据闭包对值进行记忆化。$result提供isCached和clear()辅助功能。

@propertyWrapper
struct LazyCached<Value> {
    private var storage: Value?
    private let build: () -> Value

    var wrappedValue: Value {
        mutating get {
            if let v = storage { return v }         // hit
            let v = build()                         // miss -> compute
            storage = v
            return v
        }
        // Optional: allow manual set to override cache
        set { storage = newValue }
    }

    struct Controls {
        private let getter: () -> Bool
        private let clearer: () -> Void
        var isCached: Bool { getter() }
        func clear() { clearer() }
    }

    var projectedValue: Controls {
        Controls(
            getter: { self.storage != nil },
            clearer: { self.storage = nil }
        )
    }

    init(_ build: @escaping () -> Value) {
        self.build = build
        self.storage = nil
    }
}

// Demo: expensive computation (simulated)
var calls = 0
func heavy() -> Int { calls += 1; return (1...10_000).reduce(0, +) }

struct Engine {
    @LazyCached(heavy) var result: Int
}
var e = Engine()
print(e.$result.isCached) // false
print(e.result)           // computes once
print(e.result)           // uses cache
print(calls)              // 1

缓存:基于 TTL

@TimedCache会在 TTL 到期后刷新。投影值可以提供age,让调用位置决定何时刷新。

@propertyWrapper
struct TimedCache<Value> {
    private var storage: Value?
    private var timestamp: Date?
    private let ttl: TimeInterval
    private let build: () -> Value

    var wrappedValue: Value {
        mutating get {
            let now = Date()
            if let t = timestamp, let v = storage, now.timeIntervalSince(t) < ttl {
                return v                              // fresh
            }
            let v = build()                           // refresh
            storage = v
            timestamp = now
            return v
        }
    }

    struct Status { let age: TimeInterval? }
    var projectedValue: Status {
        let age = timestamp.map { Date().timeIntervalSince($0) }
        return Status(age: age)
    }

    init(ttl: TimeInterval, _ build: @escaping () -> Value) {
        self.ttl = ttl
        self.build = build
    }
}

// Demo (age only; TTL behavior depends on real time)
struct Stats {
    @TimedCache(ttl: 60) { Int.random(in: 0...1000) } var score: Int
}
var s2 = Stats()
print(s2.score)         // computes
print(s2.$score.age ?? -1)

设计建议

设计提示:

  • 让验证保持纯粹且可预测;使用标志报告修正情况。
  • 对于缓存,保持状态精简;提供clear()或isCached。
  • 策略(备用值、TTL)应优先通过初始化参数传入。

缓存的投影辅助功能

快速检查:缓存包装器中的$value应该提供什么?

回顾

回顾:使用包装器验证输入(默认值、规则),并缓存开销较大的结果(延迟计算、TTL)。将控制功能和状态放入投影值,让调用位置保持简洁。

常见问题解答

「常见包装器模式(验证、缓存)」课时是免费的吗?

是的 — 「常见包装器模式(验证、缓存)」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Swift Academy 课程的其余内容,请升级到 CoddyKit PRO。 Swift Academy 课程共包含 3 节课。

「常见包装器模式(验证、缓存)」这节课中我会学到什么?

构建常见的 验证 和 缓存 包装器:保护输入、提供默认值、缓存开销较大的操作结果,并通过投影值提供辅助标志。 你通过在浏览器中直接运行的动手代码来练习 Swift Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Swift Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Swift Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 3 节。

「常见包装器模式(验证、缓存)」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Swift Academy 课中编写并运行代码吗?

能。每节 Swift Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 创建包装器与 projectedValue
  2. 可复用性与约束
  3. 常见包装器模式(验证、缓存)
← 返回 Swift Academy