Any reason not use use a singleton variable in Swift?
Functionally, these are very similar, but I'd advise using the Model.shared
syntax because that makes it absolutely clear, wherever you use it, that you're dealing with a singleton, whereas if you just have that model
global floating out there, it's not clear what you're dealing with.
Also, with globals (esp with simple name like "model"), you risk of having some future class that has similarly named variables and accidentally reference the wrong one.
For a discussion about the general considerations regarding globals v singletons v other patterns, see Global Variables Are Bad which, despite the fairly leading title, presents a sober discussion, has some interesting links and presents alternatives.
By the way, for your "OCD friends" (within which I guess I must count myself, because I think it's best practice), not only would declare init
to be private
, but you'd probably declare the whole class to be final
, to avoid subclassing (at which point it becomes ambiguous to what shared
references).
Why not use a struct-based singleton in Swift
You can't use a struct fora singleton because struct
is a value type so when you assign it to a variable you get a copy. This can be easily shown
struct Singleton {
static var shared = Singleton()
var value: Int
private init() {
value = 0
}
}
Singleton.shared.value = 1
var otherSingleton = Singleton.shared
otherSingleton.value = 2
Now if we print the value
of both
print(Singleton.shared.value, otherSingleton.value)
we get
1 2
So otherSingleton
is clearly a separate instance so now we have 2 singletons :)
But if we simply change the type of Singleton
to class that is a reference type and then run the same code the result of the print is
2 2
since it is the same instance we have changed the value
property for.
Swift - Using a singleton to share variables - Not passing over
Answer
Are you sure you are reading the value after writing it?
Please check it adding a breakpoint.
It this is the problem you could move the reading from viewDidLoad
to viewDidAppear
.
Less is more
Your Singleton
is working
// from my Playground
CardName.sharedInstance.returnedCardName = "123"
CardName.sharedInstance.returnedCardName // "123"
However is does contain lots of useless and redundant code :D
This is a better version
class CardName {
static var sharedInstance = CardName()
private init() {}
var cardName: String?
}
Please just use
CardName.sharedInstance.cardName = "something"
to save a value.
And
CardName.sharedInstance.cardName
to read that value.
Optional property
The new property, as you can see, is declared as Optional String
. It means it can contain nil
. This is a better way to represent the absence of value than the empty String
you were using.
Why is using a Global Variables class (singleton) bad practise?
A lot of objects in UIKit are singletons. The UIApplication object is a singleton. NSUserDefaults has the standardUserDefaults
singleton. UIDevice currentDevice
returns a singleton.
Just because it's a singleton doesn't mean it's bad. What is bad is when you start tying functionality in other classes to your singleton object, with it so deeply ingrained that you can't alter the singleton or the affected object easily.
I use a singleton to store my non-CoreData object structures. I also define some helper methods for getting the library directory, encoding data, and keyed archiving. So I can reference a master array object wherever I need it, or easily access methods that would otherwise just be copy and pastes.
Why does the suggested Swift singleton implementation use a struct?
with your implementation, the 'sharedInstance' is not a singleton cause each time it gets called, it creates new instance of MySingleton. And, to create a static variable, you have to put it in struct o enums, otherwise, you will get compiler error
Why we should use struct and class function in singleton pattern?
Actually it is recommended to use your second code because of the improvements in the versions of swift.One more thing that you should consider is to declare your singleton object using static let
and also make the initializer private
class Bar{
private init(){
// initialization
}
static let shared = Bar()
}
Singleton in Swift
The standard singleton pattern is:
final class Manager {
static let shared = Manager()
private init() { ... }
func foo() { ... }
}
And you'd use it like so:
Manager.shared.foo()
Credit to appzYourLife for pointing out that one should declare it final
to make sure it's not accidentally subclassed as well as the use of the private
access modifier for the initializer, to ensure you don't accidentally instantiate another instance. See https://stackoverflow.com/a/38793747/1271826.
So, returning to your image cache question, you would use this singleton pattern:
final class ImageCache {
static let shared = ImageCache()
/// Private image cache.
private var cache = [String: UIImage]()
// Note, this is `private` to avoid subclassing this; singletons shouldn't be subclassed.
private init() { }
/// Subscript operator to retrieve and update cache
subscript(key: String) -> UIImage? {
get {
return cache[key]
}
set (newValue) {
cache[key] = newValue
}
}
}
Then you can:
ImageCache.shared["photo1"] = image
let image2 = ImageCache.shared["photo2"])
Or
let cache = ImageCache.shared
cache["photo1"] = image
let image2 = cache["photo2"]
Having shown a simplistic singleton cache implementation above, we should note that you probably want to (a) make it thread safe by using NSCache
; and (b) respond to memory pressure. So, the actual implementation is something like the following in Swift 3:
final class ImageCache: NSCache<AnyObject, UIImage> {
static let shared = ImageCache()
/// Observer for `UIApplicationDidReceiveMemoryWarningNotification`.
private var memoryWarningObserver: NSObjectProtocol!
/// Note, this is `private` to avoid subclassing this; singletons shouldn't be subclassed.
///
/// Add observer to purge cache upon memory pressure.
private override init() {
super.init()
memoryWarningObserver = NotificationCenter.default.addObserver(forName: .UIApplicationDidReceiveMemoryWarning, object: nil, queue: nil) { [weak self] notification in
self?.removeAllObjects()
}
}
/// The singleton will never be deallocated, but as a matter of defensive programming (in case this is
/// later refactored to not be a singleton), let's remove the observer if deallocated.
deinit {
NotificationCenter.default.removeObserver(memoryWarningObserver)
}
/// Subscript operation to retrieve and update
subscript(key: String) -> UIImage? {
get {
return object(forKey: key as AnyObject)
}
set (newValue) {
if let object = newValue {
setObject(object, forKey: key as AnyObject)
} else {
removeObject(forKey: key as AnyObject)
}
}
}
}
And you'd use it as follows:
ImageCache.shared["foo"] = image
And
let image = ImageCache.shared["foo"]
For Swift 2.3 example, see previous revision of this answer.
Related Topics
Object X of Class Y Does Not Implement Methodsignatureforselector in Swift
How to Store 1.66 in Nsdecimalnumber
Setting Device Orientation in Swift Ios
Share Data Between Main App and Widget in Swiftui For iOS 14
String Value to Unsafepointer≪Uint8≫ Function Parameter Behavior
Xcode 8 Beta 3 Use Legacy Swift Issue
Swift Constants: Struct or Enum
Flatten an Array of Arrays in Swift
How to Compare Enum With Associated Values by Ignoring Its Associated Value in Swift
Number of Words in a Swift String For Word Count Calculation
Why Is 'Nil' Not Compatible With 'Unsafepointer≪Cgaffinetransform≫' in Swift 3
Get Associated Value from Enumeration Without Switch/Case
Xcode 8 Beta 3: Expected ',' Joining Parts of a Multi-Clause Condition
Storing Values in Completionhandlers - Swift
Fatal Error: Swapping a Location With Itself Is Not Supported With Swift 2.0