首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >带有参数的Singleton和init

带有参数的Singleton和init
EN

Stack Overflow用户
提问于 2015-02-10 10:36:52
回答 6查看 34.4K关注 0票数 35

我想在我的类中使用单例模式,它有一个带有参数的私有init。它还有一个名为setup的类函数,它配置和创建共享实例。我的目标-c代码是:

代码语言:javascript
复制
@interface MySingleton: NSObject

+ (MySingleton *)setup:(MyConfig *)config;
+ (MySingleton *)shared;
@property (readonly, strong, nonatomic) MyConfig *config;

@end


@implementation MySingleton

static MySingleton *sharedInstance = nil;

+ (MySingleton *)setup:(MyConfig *)config {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] initWithConfig:config];
    });

    // Some other stuff here

    return sharedInstance;
}

+ (MySingleton *)shared {
    if (sharedInstance == nil) {
        NSLog(@"error: shared called before setup");
    }
    return sharedInstance;
}

- (instancetype)initWithConfig:(RVConfig *)config {
    self = [super init];
    if (self) {
        _config = config;
    }
    return self;
}

@end

我被斯威夫特困住了:

代码语言:javascript
复制
class Asteroid {
    var config: ASTConfig? // This actually should be read-only

    class func setup(config: ASTConfig) -> Asteroid {
        struct Static {
            static let instance : Asteroid = Asteroid(config: config)
        }

        return Static.instance
    }

    class var shared: Asteroid? {
        // ???
    }

    private init(config: ASTConfig) {
        self.config = config
    }
}

我想我还在用目标-c的方式思考,无法用斯威夫特来理解它。有什么帮助吗?

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2015-05-01 02:38:31

目标-C代码的直译可能是:

代码语言:javascript
复制
private var _asteroidSharedInstance: Asteroid!

class Asteroid {
    private var config: ASTConfig?

    class func setup(config: ASTConfig) -> Asteroid {
        struct Static {
            static var onceToken: dispatch_once_t = 0
        }
        dispatch_once(&Static.onceToken) {
            _asteroidSharedInstance = Asteroid(config: config)
        }
        return _asteroidSharedInstance
    }

    class var sharedInstance: Asteroid! {                 // personally, I'd make this `Asteroid`, not `Asteroid!`, but this is up to you
        if _asteroidSharedInstance == nil {
            println("error: shared called before setup")
        }

        return _asteroidSharedInstance
    }

    init(config: ASTConfig) {
        self.config = config
    }
}

或者,在SWIFT1.2中,您可以消除该Static结构,并稍微简化setup

代码语言:javascript
复制
private static var setupOnceToken: dispatch_once_t = 0

class func setup(config: ASTConfig) -> Asteroid {
    dispatch_once(&setupOnceToken) {
        _asteroidSharedInstance = Asteroid(config: config)
    }
    return _asteroidSharedInstance
}

这真的不是单身。(我怀疑你知道这一点,但为了将来读者的利益,我提到这一点)。通常,可以在第一次使用的任何地方和任何时间实例化单例。这是一个场景,它只在一个特定的地方被实例化和配置,在尝试在其他地方使用之前,您必须小心地这样做。这是非常奇怪的方法。我们失去了一些单例功能,但仍然受到所有传统的单例限制。

很明显,如果你不介意的话,那没问题。但如果你是在娱乐其他人,两个人跳到我面前:

  1. 让这成为真正的单例:您可以通过在setup方法中移动ASTConfig的实例化来实现这一点(消除了在使用sharedInstance之前必须调用init的依赖性)。然后你可以让setup退休,像正常一样使用你的独生子女。结果的实现也大大简化了。它被简化为如下所示: 类Asteroid { static = Asteroid()私有let config: ASTConfig init() { self.config =ASTConfig(.)}} 显然,我怀疑这个ASTConfig对象的细节是问题所在,但是如果您能够执行适当的单例实现,正如您可以看到的那样,这要简单得多(尤其是)。在Swift 1.2中)。以上解决了setupsharedInstance之间的矛盾。消除了私人的全球。只是简单得多。 话虽如此,我想你有令人信服的理由照你的方式去做。也许,您必须将ASTConfig对象传递给setup方法,而不是仅仅在Asteroid类的init中实例化它,这是有关键原因的。 我只是觉得有必要指出,一个适当的独生子女将是非常可取的(这两个更简单的实现和消除理论种族条件)。
  2. 完全放弃单例模式:假设使用正确的单例(如上文所述)是不可能的,下一个问题是,您是否应该放弃任何剩余的单例外观,只需实例化当前正在调用Asteroid的简单setup,然后将其传递给真正需要它的对象,而不是依赖sharedInstance。 您已经指定了要预先手动setup Asteroid,所以让我们将这种关系正规化,并消除单个用户引入的许多结构缺陷(参见除了辛格尔顿还有什么可替代的或谷歌“单身汉是邪恶的”)。

别误会我的意思。我假设您有令人信服的理由可以这样做,而且如果当前的实现适合您,那就好了。但这是一种非常奇怪的方法,在这种方法中,你被单身人士的理论责任所困扰,而没有享受到所有的好处。

票数 32
EN

Stack Overflow用户

发布于 2016-11-07 11:30:37

我有一个稍微不同的解决方案。这依赖于

  1. 静态变量是延迟初始化的。
  2. 使用构造结构存储初始化参数
  3. 在init中使用fatalError强制执行安装调用(如果在访问单例之前没有调用安装调用)

代码语言:javascript
复制
class MySingleton {

    static let shared = MySingleton()

    struct Config {
        var param:String
    }
    private static var config:Config?

    class func setup(_ config:Config){
        MySingleton.config = config
    }

    private init() {
        guard let config = MySingleton.config else {
            fatalError("Error - you must call setup before accessing MySingleton.shared")
        }

        //Regular initialisation using config
    }
}

要使用这个,您可以设置它

代码语言:javascript
复制
MySingleton.setup(MySingleton.Config(param: "Some Param"))

(显然,如果需要,可以通过展开MySingleton.Config结构来使用多个参数)

然后,要访问单例,您可以使用

代码语言:javascript
复制
MySingleton.shared

我并不热衷于必须使用单独的安装结构,但我喜欢保持接近推荐的单例模式。保持安装结构在单例内保持相当干净。

注意-共享对象是一个单例。在后台,斯威夫特使用dispatchOnce来保证这一点。但是,没有什么可以阻止您使用来自不同线程的不同信任多次调用安装程序。

目前,对共享的第一个调用将“锁定”设置。

如果您想在第一次调用安装程序之后将其锁定,那么只需调用

代码语言:javascript
复制
_ = MySingleton.shared

在设置中

简单示例:

代码语言:javascript
复制
class ServerSingleton {
    static let shared = ServerSingleton()

    struct Config {
        var host:String
    }
    private static var config:Config?

    let host:String

    class func setup(_ config:Config){
        ServerSingleton.config = config
    }

    private init() {
        guard let config = ServerSingleton.config else {
            fatalError("Error - you must call setup before accessing MySingleton.shared")
        }

        host = config.host
    }

    func helpAddress() -> String {
        return host+"/help.html"
    }
}

ServerSingleton.setup(ServerSingleton.Config(host: "http://hobbyistsoftware.com") )
let helpAddress = ServerSingleton.shared.helpAddress()
//helpAddress is now http://hobbyistsoftware.com/help.html
票数 46
EN

Stack Overflow用户

发布于 2016-10-26 18:23:45

您可以通过创建static sharedInstance属性private并使用方法返回现有实例(可选地更改其属性值)或初始化新实例和设置其属性值来定义一个或多个参数的单例。例如,我还将您的config属性设置为只读:

代码语言:javascript
复制
typealias ASTConfig = String

class Asteroid  {

    private static var sharedInstance: Asteroid!

    var config: ASTConfig?

    private init(config: ASTConfig?) {
        self.config = config
        Asteroid.sharedInstance = self
    }

    static func shared(config: ASTConfig? = "Default") -> Asteroid {
        switch sharedInstance {
        case let i?:
            i.config = config
            return i
        default:
            sharedInstance = Asteroid(config: config)
            return sharedInstance
        }
    }
}

let asteroidA = Asteroid.shared()

asteroidA.config // Default

let asteroidB = Asteroid.shared(config: "B")

asteroidA.config // B

通过将config属性的setter定义为private,可以使其属性只读.

代码语言:javascript
复制
private(set) var config: ASTConfig?

...but调用shared(config:)的人仍然能够更改配置。为了防止这种情况,您需要将shared(config:)作为抛出方法:

代码语言:javascript
复制
typealias ASTConfig = String

class Asteroid  {

    enum E : Error {
        case config(message: String)
    }

    private static var sharedInstance: Asteroid!

    private(set) var config: ASTConfig?

    private init(config: ASTConfig?) {
        self.config = config
        Asteroid.sharedInstance = self
    }

    static func shared(config: ASTConfig? = nil) throws -> Asteroid {
        switch (sharedInstance, config) {
        case let (i?, nil):
            return i
        case _ where sharedInstance != nil && config != nil:
            throw E.config(message: "You cannot change config after initialization!")
        case let (nil, c?):
            sharedInstance = Asteroid(config: c)
            return sharedInstance
        default:
            sharedInstance = Asteroid(config: "Default")
            return sharedInstance
        }
    }
}

let asteroidA = try! Asteroid.shared(config: "A")

asteroidA.config // A

let asteroidB = try! Asteroid.shared()

asteroidB.config // A

do {
    let asteroidC = try Asteroid.shared(config: "C")
} catch {
    print(error) // "config("You cannot change config after initialization!")\n"
}

//asteroidB.config = "B" // Error: Cannot assign to property: 'config' setter is inaccessible
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28429544

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档