【问题标题】:Swift Initialize Struct with optional stored properties使用可选存储属性 Swift 初始化结构
【发布时间】:2020-04-05 22:33:56
【问题描述】:

我是 Swift 新手,我正在尝试使用带有可选属性的 Structs。我做了很多搜索,得到了一些可行的方法,但感觉效率非常低,所以想知道是否有更好/更易于管理的方法来实现我的目标。

我想使用 Structs 来代表一个业务,但我事先不知道任何特定业务可能具有哪些属性组合。这似乎意味着我必须为每个可能的参数组合创建一个 init()。

这是一个简化的示例(我还有更多属性):

import Foundation

struct Business {
    let name : String
    var web : String?
    var address: String?

    // just the business name
    init(busName: String) {
        self.name = busName
    }

    // business name + website
    init(busName: String, website: String) {
        self.name = busName
        self.web = website
    }

    // business name + address
    init(busName: String, address: String) {
        self.name = busName
        self.address = address
    }

    // business name + website + address
    init(busName: String, website: String, address: String) {
        self.name = busName
        self.web = website
        self.address = address
    }
}

然后我可以像这样初始化这个类:

Business(busName: "Dave's Cafe", website: "http://www.davescafe.com")

Business(busName: "Sarah's Brewhouse", address: "41 Acacia Ave, Smalltown")

有没有办法创建某种 init() 参数是可选的?如果您能指出我要搜索的术语或概念的方向,那就太好了。

【问题讨论】:

    标签: ios swift


    【解决方案1】:

    使用默认值:

    init(busName: String, website: String? = nil, address: String? = nil) {
        self.name = busName
        self.web = website
        self.address = address
    }
    

    然后你可以这样调用init:

    _ = Business(busName: "Foo")
    _ = Business(busName: "Foo", website: "www.foo.bar")
    _ = Business(busName: "Foo", address: "bar")
    _ = Business(busName: "Foo", website: "www.foo.bar", address: "bar")
    

    【讨论】:

    • 应该是String? 而不是String
    • 谢谢@dasdom 我尝试在类型之后使用?(例如... website: String? ...,因为我认为使用该语法会自动将值设置为nil。那不起作用所以我没有尝试将值显式设置为 nil。我现在就试试。
    • 是的,StringString? 只是一个类型;它与默认值没有太大关系(只要我们谈论的是函数参数)。您也可以为 String 参数设置默认值,例如 init(busName: String = ""),尽管我认为可选项是实现此目的的正确方法。
    • @dasdom @jtbandes - 非常感谢你们。有用!跟进问:我在示例代码顶部初始化变量的地方我使用了var web : String?,这是没有意义/错误的吗?我认为我需要使用?,因为这些变量是“可选的”。
    • @James 如果它是可选的并且没有显式分配值,则该值将自动设置为nil,但仅限于实例变量,而不是方法参数。
    【解决方案2】:

    您可以从其他 OOP 语言中借鉴的一种方法是参数构建器模式。从返回构建器的静态方法开始,然后为各个参数添加方法,最后调用build()

    let bakery = Business
        .withName("Black Forest")
        .andWebSite("www.blackforest.com")
        .andAddress("1 Main St, Springfield, IA 98765")
        .build()
    

    这是一个启用这种 API 的框架实现:

    class Business {
        // Users never call this init, it's for the builder to use
        init(name: String, webSite: String?, address: String?) {
            ...
        }
        // Here is the method the users call:
        static func withName(name: String) {
            return BusinessBuilder(name)
        }
        // This class collects parameters before calling init
        class BusinessBuilder {
            var name : String
            var webSite : String?
            var address: String?
            func andAddress(address: String) -> BusinessBuilder {
                self.address = address
                return self
            }
            func andWebSite(webSite: String) -> BusinessBuilder {
                self.webSite = webSite
                return self
            }
            func build() -> Business {
                return Business(name, webSite, address)
            }
            init(name: String) {
                self.name = name
            }
        }
    }
    

    这使您可以按照在给定情况下认为方便的任何顺序传递尽可能少或尽可能多的初始化器参数。

    这种方法的主要用途是当您不知道要获取哪些参数时,例如,当它们来自 XML 或数据库时。您可以循环调用andXyz 方法,然后在没有其他要设置的属性时调用build()

    【讨论】:

    • 对于 OP 的简单示例来说,Builder 似乎是不必要的,但如果 OP 打算扩展到更多领域,它可能会很有用。
    • @AMomchilov 有个笑话说设计模式是编程语言中缺少的功能的变通方法。 Java 中引入了构建器模式,主要是为了弥补语言中缺少的命名参数特性。另一方面,Swift 本身就解决了这个问题,因此在 Swift 中使用 builder 的主要原因是能够一次传递一个初始化参数。
    • Java 也没有默认参数,因此您可能不得不为只有几个参数的类打破构建器模式。
    【解决方案3】:

    希望对你有帮助

     struct Business {
        let name : String
        var web : String?
        var address: String?
    
    
    // you also need use question mark in init progress
    init(name: String, web: String?, address: String?) {
        self.name = name
        self.web = web
        self.address = address
        }
    }
    

    一旦你创建了一个对象,你就可以在你的可选值上使用 nil 例如:

    var newBusiness = Business(name: "AWS", web: nil, address: nil)   
    

    【讨论】:

      猜你喜欢
      • 2020-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-19
      • 1970-01-01
      • 2021-10-11
      • 2016-02-16
      • 1970-01-01
      相关资源
      最近更新 更多