• Swift、Objective-C 单例模式 (Singleton)


    Swift、Objective-C 单例模式 (Singleton)

    本文的单例模式分为严格单例模式不严格单例模式。单例模式要求一个类有一个实例,有公开接口可以访问这个实例。严格单例模式,要求一个类只有一个实例;不严格单例模式,可以创建多个实例。

    有的类只能有一个实例,例如 UIApplication,通过 shared 属性访问唯一的实例,属于严格单例模式。有用户登录功能的 App 中,如果当前用户的数据模型与其他用户的数据模型不同,那么当前用户的类也应该用严格单例模式。在逻辑上,当前用户只有一个,只能有一个实例;这样可以在各个地方访问当前用户的数据。如果当前用户的数据模型与其他用户的数据模型相同,则应用不严格单例模式。可以给其他用户创建实例,同时也可以在各个地方访问当前用户的数据。

    Swift 实现

    严格单例模式

    大多数 Objective-C 的类都继承自 NSObject,而 Swift 的类可以继承自 NSObject 或者不继承。

    继承自 NSObject

    class SingletonClass: NSObject {
    
        static let shared = SingletonClass()
        
        // Make sure the class has only one instance
        // Should not init or copy outside
        private override init() {}
        
        override func copy() -> Any {
            return self // SingletonClass.shared
        }
        
        override func mutableCopy() -> Any {
            return self // SingletonClass.shared
        }
        
        // Optional
        func reset() {
            // Reset all properties to default value
        }
    }
    

    静态属性 shared 持有唯一的实例,对外公开。

    重载 init() 方法,使其对外不可见,不可以在外部调用,防止在外部创建实例。

    重载 copy()、mutableCopy() 方法,返回 self,防止在外部复制实例。这里也可以返回 SingletonClass.shared,效果是一样的,因为只有一个实例。只有 shared 能调用 copy()、mutableCopy() 方法,那么 self 就是 shared。写 self,代码比较简洁。

    单例一旦创建,一直持有,不能手动销毁,但可以重置数据。如果需要的话,可以添加一个重置数据的方法 reset()。例如,当前用户退出登录,需要把当前用户实例的所有属性重置为默认值,防止数据错误。

    不继承自 NSObject

    class SingletonClass2 {
        
        static let shared = SingletonClass2()
        
        // Make sure the class has only one instance
        // Should not init outside
        private init() {}
        
        // Optional
        func reset() {
            // Reset all properties to default value
        }
    }
    

    不继承自 NSObject 的类没有 copy()、mutableCopy() 方法,不需要重载。其他同上。

    不严格单例模式

    把重载的 init() 方法去掉,或者把 private 去掉,即可创建多个实例。如果继承自 NSObject,重载 copy()、mutableCopy() 方法:创建新实例,传递数据给新实例,返回新实例。其他与严格单例模式相同。

    不严谨的重置数据方法

    如果单例有很多属性,重置数据需要把每个属性都变成默认值,则 reset() 方法要写很多。有一种不严谨的重置数据方法:重新生成一个实例并赋值给持有实例的静态变量。

    class SingletonClass3 {
        
        private static var _shared = SingletonClass3()
        
        static var shared: SingletonClass3 {
            return _shared
        }
        
        private init() {}
        
        // Not safe
        // We can obtain more than one instance outside with this function
        func reset() {
            SingletonClass3._shared = SingletonClass3()
        }
    }
    

    如果在外部访问单例都通过 shared 属性,这么写不会出错。然而,如果外部持有单例,就有可能出错。

    let s = SingletonClass3.shared
    s.reset()
    print(s === SingletonClass3.shared) // false
    

    以上会输出 false,s 在重置之后,和 SingletonClass3.shared 不是同一个对象。因此,这样的重置数据方法不严谨。还是要老老实实把每个属性赋为默认值。

    Objective-C 实现

    严格单例模式

    .h 文件

    @interface SingletonClassOC : NSObject
    
    + (nonnull instancetype)shared;
    
    + (instancetype)new NS_UNAVAILABLE;
    - (instancetype)init NS_UNAVAILABLE;
    - (id)copy NS_UNAVAILABLE;
    - (id)mutableCopy NS_UNAVAILABLE;
    
    @end
    

    .m 文件

    @implementation SingletonClassOC
    
    + (nonnull instancetype)shared {
        static id _shared;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _shared = [[self alloc] init];
        });
        return _shared;
    }
    
    // Optional
    - (void)reset {
        // Reset all properties to default value
    }
    
    @end
    

    在 .h 文件中,用 NS_UNAVAILABLE 禁用初始化和拷贝方法,只允许用过 shared 方法访问唯一实例。

    静态变量 _shared 持有唯一的实例,通过 shared 方法对外公开。由 dispatch_once 保证 _shared 只初始化一次。方法返回值的 nonnull 表示返回值不为空,这样写方便 Swift 调用。不加 nonnull,shared 方法在 Swift 中的返回值是 optional 类型 (SingletonClassOC?),不方便使用;加上 nonnull 则为 SingletonClassOC 类型。

    如果需要,用 reset 方法重置数据。

    不严格单例模式

    .h 文件

    @interface SingletonClassOC2 : NSObject
    
    + (nonnull instancetype)shared;
    
    @end
    

    .m 文件

    @implementation SingletonClassOC2
    
    + (nonnull instancetype)shared {
        static id _shared;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _shared = [[self alloc] init];
        });
        return _shared;
    }
    
    - (id)copyWithZone:(NSZone *)zone {
        SingletonClassOC2 *copiedObject = [[self.class allocWithZone:zone] init];
        // Copy data to copiedObject
        return copiedObject;
    }
    
    - (id)mutableCopyWithZone:(NSZone *)zone {
        SingletonClassOC2 *copiedObject = [[self.class allocWithZone:zone] init];
        // Copy data to copiedObject
        return copiedObject;
    }
    
    - (void)reset {
        // Reset all properties to default value
    }
    
    @end
    

    公开的 shared 方法与严格单例模式相同。外部可以通过 init 方法创建与 _shared 不同的实例。

    重载 copyWithZone: 和 mutableCopyWithZone: 方法,在里面创建新实例,传递数据给新实例,返回新实例。外部可以通过 copy 或 mutableCopy 方法复制实例。

    SDWebImage 中就用到不严格单例模式

    NSObject 的类方法 new 相当于 alloc 和 init 方法。

    转载请注明出处:http://www.cnblogs.com/silence-cnblogs/p/6776217.html

  • 相关阅读:
    如何阅读一篇论文
    FT232R驱动问题
    无线传感网-定位技术1
    无线传感网中常见路由协议2
    课程总结
    十四周总结以及实验报告
    第十三周总结
    第十二周课程总结
    第十周课程总结
    第九周课程总结&实验报告(七)
  • 原文地址:https://www.cnblogs.com/silence-cnblogs/p/6776217.html
Copyright © 2020-2023  润新知