Внедрение Swift 1.2 Singleton

Я довольно новичок в Swift, как разработчик соло, мне было интересно, может ли кто-нибудь передать комментарии к реализации singleton ниже. Код действительно работает, но, будучи новым для Swift и зная, что есть много ошибок, я хотел получить некоторые отзывы от кого-то более опытного.

// Test Singleton Swift 1.2
class FGSingleton {
    static var instance: FGSingleton!
    var gameScore: Int = 0

    // SHARED INSTANCE
    class func sharedInstance() -> FGSingleton {
        self.instance = (self.instance ?? FGSingleton())
        return self.instance
    }

    // METHODS
    init() {
        println(__FUNCTION__)
    }
    func displayGameScore() {
        println("\(__FUNCTION__) \(self.gameScore)")
    }
    func incrementGameScore(scoreInc: Int) {
        self.gameScore += scoreInc
    }
}

Тесты:

FGSingleton.sharedInstance().displayGameScore()
FGSingleton.sharedInstance().incrementGameScore(100)
FGSingleton.sharedInstance().incrementGameScore(1)
FGSingleton.sharedInstance().displayGameScore()
FGSingleton.sharedInstance().incrementGameScore(1000)
FGSingleton.sharedInstance().displayGameScore()

Вывод:

init()
displayGameScore() 0
displayGameScore() 101
displayGameScore() 1101
37 голосов | спросил fuzzygoat 11 FebruaryEurope/MoscowbWed, 11 Feb 2015 14:38:36 +0300000000pmWed, 11 Feb 2015 14:38:36 +030015 2015, 14:38:36

4 ответа


25

Вы можете значительно упростить это:

class FGSingleton {
    static let sharedInstance = FGSingleton()

    var gameScore: Int = 0

    // METHODS
    private init() {
        println(__FUNCTION__)
    }
    func displayGameScore() {
        println("\(__FUNCTION__) \(self.gameScore)")
    }
    func incrementGameScore(scoreInc: Int) {
        self.gameScore += scoreInc
    }
}

И назовите его следующим образом:

FGSingleton.sharedInstance.displayGameScore()
ответил Rhuantavan 11 FebruaryEurope/MoscowbWed, 11 Feb 2015 17:52:35 +0300000000pmWed, 11 Feb 2015 17:52:35 +030015 2015, 17:52:35
18

Прежде всего, печать текста на консоль абсолютно бессмысленна для приложения iOS. Все в порядке, это простой способ проверить, работает ли наш код по назначению, но мы не хотим ошибочно оставлять это в финальной версии сборки.

Итак, шаг 1: Следуйте инструкциям в этом сообщении StackOverflow , чтобы настроить собственный флаг компилятора для вашего проекта Swift .

Шаг 2. Заверните все ваши операторы println в #if DEBUG и #endif.

Пример:

    init() {
#if DEBUG
        println(__FUNCTION__)
#endif
    }

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

Я очень рекомендую либо сделать функцию для этой отладочной печати:

    func debugPrintln(s: String) {
#if DEBUG
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "HH:mm:ss.zzz"
        let timeStamp = dateFormatter.stringFromDate(NSDate())
        println("[" + timeStamp + "]: " + s)    
#endif
    }

Или просто сделайте фрагмент кода Xcode из следующего:

#if DEBUG
    println(<#string#>)
#endif

И установите для автозаполнения значение "println"

введите описание изображения здесь

Выполняя это, каждый раз, когда вы набираете «println», Xcode будет пытаться автозаполнять фрагмент выше, а «строка» в скобках будет выбрана и готова к перезаписыванию путем ввода. У меня есть аналогичный для Objective-C, если вам интересно:

введите описание изображения здесь>> </p>

<hr>
<p> Итак, давайте рассмотрим материал, на который вы задали более прямые вопросы о ... </p>

<p> Во-первых, нам не нужен метод <code>class</code>, чтобы получить общий экземпляр. Я бы пошел с предложением  Rhuantavan  <code>static let</code>. </p>

<p> Но давайте обратим внимание на наши модификаторы доступа. </p>

<p> Наша переменная экземпляра <code>gameScore</code> и наш метод экземпляра <code>incrementGameScore</code> имеют одинаковый уровень доступа, так что в чем смысл? </p>

<p> Зачем мне это писать: </p>

<pre><code>FGSingleton.sharedInstance.incrementGameScore (3)
</code></pre>

<p> Когда я мог бы просто написать это: </p>

<pre><code>FGSingleton.sharedInstance.gameScore + = 3
</code></pre>

<p> Кроме того, </p>

<pre><code>FGSingleton.sharedInstance.incrementGameScore (-3)
</code></pre>

<p> кажется немного неудобным против: </p>

<pre><code>FGSingleton.sharedInstance.gameScore - = 3
</code></pre>

<p>, но становится еще хуже, если сравнивать настройку балла с определенным числом. </p>

<pre><code>let targetScore = 5
let currentScore = FGSingleton.sharedInstance.gameScore
FGSingleton.sharedInstance.incrementGameScore ((- 1 * currentScore) + targetScore)
</code></pre>

<p> по сравнению с просто: </p>

<pre><code>FGSingleton.sharedInstance.gameScore = targetScore
</code></pre>

<p> Я не рекомендую, что лучше. Конечно, очень возможно, что вы не хотите, чтобы пользователь напрямую менял счет игры. Но если это так, сделайте свойство <code>gameScore</code> переменной <code>private</code> и реализуйте больше методов и еще больше проверок безопасности на том, что на самом деле происходит в этих методах. Или, если вы не возражаете, чтобы они меняли счет игры напрямую, просто исключите ненужный метод <code>incrementGameScore</code>. </p>

<hr>
<p> Вообще говоря, в тех немногих случаях, когда мы действительно хотим иметь одноэлементный шаблон, класс не работает так хорошо, когда люди создают экземпляры других экземпляров объекта. Класс разработан с предположением, что он всегда будет использоваться как одноэлементный. Слишком часто разработчики допускают ошибку, не вставляя никаких гарантий, чтобы предотвратить создание объекта. </p>

<p> Давайте не будем делать ту же самую ошибку. </p>

<p> В Swift это так же просто, как маркировка метода <code>init</code> как <code>private</code>: </p>

<pre><code>private init () {}
</code></pre>

<p> Теперь, если мы попытаемся создать экземпляр объекта нашего класса в любом другом файле Swift, мы получим неприятную ошибку: </p>

<p> <img src =

(я назвал свой класс class)

Это то, что мы хотим. Если это синглтон, мы, вероятно, не хотим, чтобы у людей были другие доступные экземпляры ... поэтому не предполагайте, что они не будут пытаться. Это достаточно просто, чтобы полностью предотвратить это!


Как некоторая окончательная часть пищи для размышлений, нам действительно нужно углубиться в то, почему мы делаем одноэлементный класс вообще. Во всем моем профессиональном опыте в качестве разработчика iOS мне никогда не нужноиспользовать одноэлемент, который я могу вспомнить.

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

На самом деле вам нужен этот синглтон? Не лучше ли было бы иметь какой-то класс static let, который передается между объектами, которые должны изменять или читать данные, которые класс gameScore содержит?

По моему мнению, большинство случаев использования одноточечных пакетов - ленивый подход к тому, чтобы научиться правильно передавать данные между вашими объектами.

ответил nhgrif 14 FebruaryEurope/MoscowbSat, 14 Feb 2015 05:40:59 +0300000000amSat, 14 Feb 2015 05:40:59 +030015 2015, 05:40:59
4

Другие ответы уже прокомментировали другие аспекты кода; Я просто хотел бы оставить здесь свой предпочтительный способ внедрения синглонов в Swift:

public let FGSingleton = FGSingletonClass()

public class FGSingletonClass {
    private init() {}
    // Etc...
}

Чтобы использовать singleton:

let v = FGSingleton.myProperty

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

ответил Alex 4 PMpSat, 04 Apr 2015 15:09:58 +030009Saturday 2015, 15:09:58
1

Большинство других ответов охватывают все очень подробным и точным образом, но я хочу выделить некоторые из тех практик, которые мне нравились проповедовать, когда речь идет о одиночных классах.

Я использовал для создания singleton, а затем все общедоступные методы в классе были статическими. Эти статические методы, называемые затем, вызывают методы экземпляра реплики, используя private sharedInstance

Как его одноэлемент, все мои вызовы методов будут использовать общий экземпляр ТОЛЬКО, поэтому зачем его указывать снова и снова.

class FGSingleton {
private static let sharedInstance = FGSingleton()
private var gameScore: Int = 0

// METHODS
private init() {
    println(__FUNCTION__)
}
class func displayGameScore() {
    println("\(__FUNCTION__) \(self.sharedInstance.gameScore)")
}
class func incrementGameScore(scoreInc: Int) {
    self.sharedInstance.gameScore += scoreInc
}
}

и вызов будет просто

  FGSingleton.incrementGameScore(5)
  FGSingleton.displayGameScore()

UPDATE Просто для варианта, если вам нужно инициализировать некоторые переменные-члены в вашем singleton. Вы можете сделать что-то вроде:

    public static let sharedInstance: FGSingleton = {
    let objBirthDate: NSDate = NSDate()
    return FGSingleton(date: objBirthDate)
}()

required private init(data: NSDate){...}
ответил thesummersign 9 J000000Thursday15 2015, 16:29:38

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132