0Pricing
Swift Academy · 课时

可复用性与约束

使用 类型约束 (例如 Value: Comparable )设计泛型包装器,通过 where 添加针对性 API,并组合多个包装器。

可复用性与约束 是 CoddyKit 上的免费 Swift Academy 课时。 这是第 2 节课,共 3 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Swift Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Swift Academy 课程共包含 3 节课。

为什么需要约束?

编写一个包装器,并通过添加约束使其成为泛型,从而在多种类型之间复用。使用条件子句添加专用接口,并组合小型包装器。

泛型 + 可比较

使用可比较,让一个Bounded适用于整数、字符串等类型。无效类型会在编译时被拒绝。

@propertyWrapper
struct Bounded<Value: Comparable> {
    private var value: Value
    private let range: ClosedRange<Value>

    var wrappedValue: Value {
        get { value }
        set {
            // clamp using Comparable
            if newValue < range.lowerBound { value = range.lowerBound }
            else if newValue > range.upperBound { value = range.upperBound }
            else { value = newValue }
        }
    }

    init(wrappedValue: Value, _ range: ClosedRange<Value>) {
        self.range = range
        self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
    }
}

struct Stats {
    @Bounded(0...100) var score: Int = 120
    @Bounded("a"..."z") var letter: String = "Swift"
}
var st = Stats()
print(st.score)   // 100
st.letter = "m"
print(st.letter)

受约束的扩展

使用约束条件,仅在类型支持相应功能时提供额外能力(例如整数标志位或浮点更新)。

extension Bounded where Value: BinaryInteger {
    var isMaxed: Bool { wrappedValue == range.upperBound }
}
extension Bounded where Value: FloatingPoint {
    mutating func bump(by delta: Value) { wrappedValue = wrappedValue + delta }
}

struct Meter {
    @Bounded(0...10) var steps: Int = 9
    @Bounded(0.0...1.0) var progress: Double = 0.25
}
var m = Meter()
print(m.$steps)          // projectedValue not defined; accessing wrapper is not allowed directly here
print(m._steps)          // compiler creates backing storage name; shown for illustration only
print(m.steps)           // 9
print(m._progress)       // backing storage (illustrative)
m._progress.bump(by: 0.6)
print(m.progress)        // 0.85 (clamped if exceeded)

仅适用于集合的包装器

通过约束为RangeReplaceableCollection,将接口限制为集合。它适用于字符串和数组。

@propertyWrapper
struct MaxLength<Value: RangeReplaceableCollection> where Value.Element: Sendable {
    private var storage: Value
    private let limit: Int

    var wrappedValue: Value {
        get { storage }
        set {
            var v = newValue
            if v.count > limit { v.removeLast(v.count - limit) }
            storage = v
        }
    }

    init(wrappedValue: Value, _ limit: Int) {
        self.limit = limit
        self.storage = wrappedValue
        if storage.count > limit { storage.removeLast(storage.count - limit) }
    }
}

struct Post {
    @MaxLength(10) var title: String = "hello swift learners"
    @MaxLength(5) var tags: [String] = ["swift","ios","spm"]
}
var p = Post()
print(p.title)  // "hello swif"
print(p.tags)   // ["swift","ios","spm"]

包装器组合

您可以叠加小型包装器。在这里,Trimmed先运行,然后由NonEmpty确保提供备用值。

@propertyWrapper
struct Trimmed {
    private var s: String = ""
    var wrappedValue: String {
        get { s }
        set { s = newValue.trimmingCharacters(in: .whitespacesAndNewlines) }
    }
    init(wrappedValue: String) { self.wrappedValue = wrappedValue }
}

@propertyWrapper
struct NonEmpty {
    private var s: String = ""
    var wrappedValue: String {
        get { s }
        set { s = newValue.isEmpty ? "N/A" : newValue }
    }
    init(wrappedValue: String) { self.wrappedValue = wrappedValue }
}

struct Profile {
    @NonEmpty @Trimmed var displayName: String = "  "
}
var prof = Profile()
print(prof.displayName)  // "N/A"

局部变量 + 复用

包装器同样适用于局部变量。让它们保持泛型,以便在多个模块之间复用。

@propertyWrapper
struct Default<Value> {
    private var value: Value
    private let make: () -> Value
    var wrappedValue: Value {
        get { value }
        set { value = newValue }
    }
    init(wrappedValue: Value, _ factory: @escaping () -> Value) {
        self.value = wrappedValue
        self.make = factory
    }
    mutating func reset() { value = make() }
}

// Local variable usage
do {
    @Default({ 0 }) var counter: Int = 5
    print(counter) // 5
    _counter.reset()
    print(counter) // 0
}

包装器的编译时约束

快速检查:如何将包装器限制为可比较类型?

回顾

回顾:让包装器保持泛型,添加约束以确保使用安全,使用带有条件子句的接口,并组合小型包装器以提升清晰度。

常见问题解答

「可复用性与约束」课时是免费的吗?

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

「可复用性与约束」这节课中我会学到什么?

使用 类型约束 (例如 Value: Comparable )设计泛型包装器,通过 where 添加针对性 API,并组合多个包装器。 你通过在浏览器中直接运行的动手代码来练习 Swift Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Swift Academy 需要有经验吗?

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

「可复用性与约束」课时需要多长时间?

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

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

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

此课程中的所有课时

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