2016-09-19 2 views
0

До перехода на Swift 3, я имел следующий код:Преобразование Swift 3 код без использования dispatch_once_t

//Set up singleton object for the tracker 
class func setup(tid: String) -> WatchGATracker { 
    struct Static { 
     static var onceToken: dispatch_once_t = 0 
    } 
    dispatch_once(&Static.onceToken) { 
     _analyticsTracker = WatchGATracker(tid: tid) 
    } 
    return _analyticsTracker 
} 

Я получаю следующее сообщение об ошибке:

'dispatch_once_t' is unavailable in Swift: Use lazily initialized globals instead 

По-видимому, инструмент преобразования конвертируется код для этого:

class func setup(_ tid: String) -> WatchGATracker { 
    struct Static { 
     static var onceToken: Int = 0 
    } 
    _ = WatchGATracker.__once 
    return _analyticsTracker 
} 

И в верхней части моего класса, он прибавил:

private static var __once:() = { 
     _analyticsTracker = WatchGATracker(tid: tid) 
}() 

Но я все еще получаю ошибку:

Instance member 'tid' cannot be used on type 'WatchGATracker' 

TID объявлен как:

fileprivate var tid: String 

Раньше быть объявлен как:

private var tid: String 

я не могу чтобы выяснить, как исправить мой код, есть ли у кого-нибудь предложения?

+1

Вы не должны использовать этот устаревший метод создания одиночек в любом случае, проверить учебник как [этот] (http://krakendev.io/blog/the-right-way-to-write- a-singleton), чтобы сделать это в Swift. – kabiroberai

+0

Почему вы не используете 'lazy' тогда? – holex

+0

@holex - Раздражающе, в отличие от 'static' и globals,' lazy' не обеспечивает безопасность потоков, поэтому вам нужно реализовать свой собственный механизм синхронизации. – Rob

ответ

0

Непонятно, что вы ожидаете от этого кода. Что произойдет, если кто-то позвонит WatchGATracker.setup(tid: "abc"), а затем звонит WatchGATracker.setup(tid: "123")? Кажется, последний тид тихо игнорируется. Я не верю, что это правильный синглтон.

Вам нужно более четко указать, что такое правильное поведение. Например, если это ошибка программирования не вызывать setup перед использованием трекера, вы хотите что-то вроде этого:

class WatchGATracker { 
    static private(set) var instance: WatchGATracker! 

    class func setup(tid: String) { 
     precondition(instance == nil) 
     instance = WatchGATracker(tid: tid) 
    } 

    init(tid: String) { . . . } 
} 

Если различные части программы могут позвонить с различными ПИВ, то вы должны сделать что-то больше похоже :

static private var instances: [String: WatchGATracker] = [:] 

class func setup(tid: String) -> WatchGATracker { 
    if let instance = instances[tid] { 
     return instance 
    } 

    let instance = WatchGATracker(tid: tid) 
    instances[tid] = instance 
    return instance 
} 

(Как отмечено в комментариях, если вы хотите потокобезопасность для этого извлечь, то вам нужно добавить очередь на уровне класса диспетчеризации, чтобы управлять таким же образом, как вы добавьте очередь на уровне экземпляра для обеспечения безопасности потоков на этом уровне. Вы не получаете автоматическую защиту потоков, если для инициализации требуется параметр.)

+0

Ни один из них не является потокобезопасным ... – Rob

+1

Отмечено. Решение, которое идентично решению в случае экземпляра. 'dispatch_once' не применим для вещей, которые берут параметры для построения, и не являются ленивыми глобальными. –

0

Когда ошибка говорит «Использование лениво инициализированы глобалам вместо», это что-то вроде предположить:

class WatchGATracker { 
    private var tid: String 
    static let shared = WatchGATracker(tid: "foo") 
    private init(tid: String) { ... } 
    ... 
} 

Или, если вы действительно хотите, чтобы дать абоненту возможность установить tid (что любопытно шаблон для одноточечного), изменить init не принимать параметр, сделать tid неявно развернутый, но не private на всех, и объявить shared как:

class WatchGATracker { 
    var tid: String! 
    static let shared = WatchGATracker() 
    private init() { ... } 
    ... 
} 

а п вы можете сделать

WatchGATracker.shared.tid = "foo" 

Кстати, о fileprivate, это предполагает, что только потому, что это то, что старый private в настоящее время переводится. Но если у вас нет причин, то это должно быть fileprivate, я бы, вероятно, переключил его обратно на private (что теперь делает его закрытым для лексической области).

+0

Спасибо, довольно прямое исправление, я попробую. Если приложение работает так, как это было в прошлом, я приму этот ответ. – iOShepherd

0

Мы все привыкли к чрезмерно сложному одноэлементным моделям ... Это на самом деле очень просто сейчас:

class WatchGATracker { 
    static let sharedInstance = WatchGATracker() 
    private override init() {} 
} 

Источник: http://krakendev.io/blog/the-right-way-to-write-a-singleton

Что касается функции setup(), я согласен с @Rob Напир выше в своем ответе, но я сделаю еще один шаг. Если вы пытаетесь перенастроить синглтон, вы делаете это неправильно. Если у вас есть требуемый параметр настройки, который варьируется от одного использования к другому, вы должны создать отдельные экземпляры.

Может быть, есть какие-то связи или другой функции, которую вы пытаетесь использовать повторно, что это приведет вас вниз по одноплодной пути, но если это так, вы должны извлечь только эту функцию в своем собственном классе, и пусть эти настраиваемые экземпляры совместно , что синглтон.

/// Watch Connection singleton 
class WatchConnection: NSObject { 
    static let sharedInstance = WatchConnection() 
    private override init() {} 

    func doSomething() {} 
} 

//Watch tracker class for each instance of a watch 
class WatchGATracker { 
    init(tid: String) { 
     //do something useful 
    } 

    let connection = { WatchConnection.sharedInstance }() 
} 

let one = WatchGATracker(tid: "one") 
one.connection.doSomething() 
Смежные вопросы