Singleton with Swift 3.0

前端 未结 6 1653
灰色年华
灰色年华 2021-01-15 04:33

I had this implementation with Swift 2.0 and the Xcode suggestion is not only baffling but causes compilation error as well. it\'s a library where users are passing callfunc

相关标签:
6条回答
  • 2021-01-15 05:01

    The answers to date are great, but I like to be able to test my classes that use singleton with unit tests. The existing solutions require that you get a real singleton, not one that you can Fake, Mock, and Stub. In order to make testing easier, I use something like this:

    protocol ScheduleManaging {
        func add(_ schedule: TimerSchedule)
    }
    
    class ScheduleManager: ScheduleManaging {
        var schedule: TimerSchedule?
    
        static var sharedInstance: ScheduleManaging = {
            return ScheduleManager()
        }()
    
        private init() {
        }
    }
    
    typealias ScheduleManagingMethods = ScheduleManager
    extension ScheduleManagingMethods {
        func add(_ schedule: TimerSchedule) {
            self.schedule = schedule
        }
    }
    

    In classes under test that use the singleton, I can do something like this:

    class OtherTests: XCTestCase {
    
        class FakeSharedManager: ScheduleManaging {
            var addMethodCalled = false
            func add(_ schedule: TimerSchedule) {
                addMethodCalled = true
            }
        }
    
        func test_SomeMethodThatShouldCallAdd_CallsAdd() {
            ScheduleManager.sharedInstance = FakeSharedManager()
            // Test that some class that uses the ScheduleManager
            // calls the "add" method of the ScheduleManager
            let someClass = ManagerUsingClass()
            someClass.someMethodThatShouldCallAdd()
            XCTAssertTrue(ScheduleManager.sharedInstance. addMethodCalled)
        }
    }
    
    0 讨论(0)
  • 2021-01-15 05:02

    Create a singleTon class as follows :

         class mySingleTon{
            
            //1. gives you SingleTon object which is created only once.
            static let sharedInstance = mySingleTon() 
            
            //2. make init private so it  prevents others from using the default '()' initializer for this class.    
            private init(){
            print("i am born")
            }
        
            func foo()
            {
            print("hello")
            }
        }
    

    Example usage :

    class A{
    func bar()
    {
      mySingleTon.sharedInstance.foo()
    
    //3. Uncomment to get error : initializer is inaccessible due to 'private' protection level.
    // let newSharedInstance = mySingleTon()
    }
    
    let a = A()
    let b = A()
    a.bar()
    b.bar()
    

    Output :

    i am born

    hello

    hello

    As you noticed Initializer is only called once. As per apple docs :

    “The lazy initializer for a global variable (also for static members of structs and enums) is run the first time that global is accessed, and is launched as dispatch_once to make sure that the initialization is atomic. This enables a cool way to use dispatch_once in your code: just declare a global variable with an initializer and mark it private.”

    Explaining Comments :

    1. Static member of class implicitly calls "dispatch_once" hence it is thread safe.

    2. Making init private prevents initialisation again.

    3. Test code line to prove initialisation is private.

    0 讨论(0)
  • 2021-01-15 05:03

    I usually like this pattern:

    final class MyClass { static let shared = MyClass() }

    Then you can call MyClass.shared to get your singleton.

    0 讨论(0)
  • 2021-01-15 05:03

    This works (as in it won't call callfunc twice), if you don't mind the function becomes @escaping:

    class Main {
        private static var CALLER: (() -> MyProtocol)?
        private static let GETTER: MyProtocol = CALLER!()
    
        public class func getSingleton(_ callfunc: @escaping () -> MyProtocol) -> MyProtocol {
            CALLER = callfunc
            return GETTER
        }
    }
    

    Note that this does not address thread safety (CALLER can be changed before reaching GETTER), and the CALLER will be overwritten every time getSingleton is used which may impose some performance penalty.

    0 讨论(0)
  • 2021-01-15 05:08

    Another way, for me which is good enough using init private

    final class SingletonClass {
    
        // reachable from other classes
        static let sharedInstance: SingletonClass = SingletonClass()
    
        // properties
        var stringArray : [String] = []
    
        // not reachable from other classes
        private init() { }      
    
    }
    
    0 讨论(0)
  • 2021-01-15 05:24
    public class MyClass {
    
        static let sharedInstance = MyClass()
    
        private init() {
            print("MyClass Initialized")
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题