单例模式

作者: 码小菜 | 来源:发表于2019-04-06 18:52 被阅读0次

    目录
    一,基本概念
    二,优缺点
    三,如何实现
    四,封装成宏
    五,实现可继承

    一,基本概念

    保证一个类只有一个实例,并且提供一个全局的入口访问该实例

    二,优缺点

    1,优点:全局只创建一个实例,节省内存资源
    2,缺点:不能继承

    三,如何实现
    • 第一步:实现shared方法来创建单例

    方法一:线程不安全,多线程情况下可能会创建多个实例

    static id _instance = nil;
    + (instancetype)sharedInstance {
       if(!_instance) {
           _instance = [[self alloc] init];
       }
       return _instance;
    }
    

    方法二:加锁可以保证线程安全,但是会消耗性能

    static id _instance = nil;
    + (instancetype)sharedInstance {
       @synchronized(self) { // 加锁
           if(!_instance) {
               _instance = [[self alloc] init];
           }
       }
       return _instance;
    }
    

    方法三:GCD是线程安全的,也能保证性能(官方推荐)

    static id _instance = nil;
    + (instancetype)sharedInstance {
       static dispatch_once_t onceToken;
       dispatch_once(&onceToken, ^{
           _instance = [[self alloc] init];
       });
       return _instance;
    }
    
    • 第二步:避免外部直接调用alloc/new/copy/mutableCopy来创建实例

    方法一:重写alloc/copy/mutableCopy方法

    + (instancetype)allocWithZone:(struct _NSZone *)zone { 
        static dispatch_once_t onceToken; 
        dispatch_once(&onceToken, ^{ 
            _instance = [super allocWithZone:zone]; 
        }); 
        return _instance; 
    } 
    
    - (id)copyWithZone:(NSZone *)zone { 
        return _instance; 
    }
    
    - (id)mutableCopyWithZone:(NSZone *)zone { 
        return _instance; 
    }
    

    方法二:禁用init/new/copy/mutableCopy方法

    - (instancetype)init OBJC_UNAVAILABLE("use sharedInstance instead");
    + (instancetype)new OBJC_UNAVAILABLE("use sharedInstance instead");
    - (instancetype)copy OBJC_UNAVAILABLE("use sharedInstance instead");
    - (instancetype)mutableCopy OBJC_UNAVAILABLE("use sharedInstance instead");
    
    四,封装成宏

    作用:避免每个类都写一遍实现代码

    1,重写方法的宏

    // .h文件
    #define singletonH \
    + (instancetype)sharedInstance;
    
    // .m文件
    #define singletonM \
    static id _instance = nil; \
    + (instancetype)sharedInstance { \
        static dispatch_once_t onceToken; \
        dispatch_once(&onceToken, ^{ \
            _instance = [[self alloc] init]; \
        }); \
        return _instance; \
    } \
    + (instancetype)allocWithZone:(struct _NSZone *)zone { \
        static dispatch_once_t onceToken; \
        dispatch_once(&onceToken, ^{ \
            _instance = [super allocWithZone:zone]; \
        }); \
        return _instance; \
    } \
    - (id)copyWithZone:(NSZone *)zone { \
        return _instance; \
    } \
    - (id)mutableCopyWithZone:(NSZone *)zone { \
        return _instance; \
    }
    

    2,禁用方法的宏

    // .h文件
    #define singletonH \
    + (instancetype)sharedInstance; \
    - (instancetype)init OBJC_UNAVAILABLE("use sharedInstance instead"); \
    + (instancetype)new OBJC_UNAVAILABLE("use sharedInstance instead"); \
    - (instancetype)copy OBJC_UNAVAILABLE("use sharedInstance instead"); \
    - (instancetype)mutableCopy OBJC_UNAVAILABLE("use sharedInstance instead");
    
    // .m文件
    #define singletonM \
    static id _instance = nil; \
    + (instancetype)sharedInstance { \
        static dispatch_once_t onceToken; \
        dispatch_once(&onceToken, ^{ \
            _instance = [[self alloc] init]; \
        }); \
        return _instance; \
    }
    

    3,如何使用

    // .h文件
    @interface HttpManager : NSObject
        singletonH
    @end
    
    // .m文件
    @implementation HttpManager
        singletonM
    @end
    
    五,实现可继承

    1,为什么不能继承:子类和父类共用一个实例,导致子类对象无法访问子类的属性和方法

    // Person实现了单例,Man继承自Person
    Person *p1 = [Person sharedInstance];
    Person *p2 = [[Person alloc] init];
    NSLog(@"p1: %@", p1);
    NSLog(@"p2: %@", p2);
        
    Man *m1 = [Man sharedInstance];
    Man *m2 = [[Man alloc] init];
    NSLog(@"m1: %@", m1);
    NSLog(@"m2: %@", m2);
        
    [m1 run];
    
    // 打印结果
    p1: <Person: 0x600000b95650>
    p2: <Person: 0x600000b95650>
    m1: <Person: 0x600000b95650>
    m2: <Person: 0x600000b95650>
    -[Person run]: unrecognized selector sent to instance 0x600000b95650
    

    2,如何实现继承:使用runtime的关联函数

    // Person单例实现代码
    + (instancetype)sharedInstance {
        id _instance = objc_getAssociatedObject(self, @"instance");
        @synchronized(self) {
            if(!_instance) {
                _instance = [[self alloc] init];
                objc_setAssociatedObject(self, @"instance", _instance, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
            }
        }
        return _instance;
    }
    
    + (instancetype)allocWithZone:(struct _NSZone *)zone {
        id _instance = objc_getAssociatedObject(self, @"instance");
        @synchronized(self) {
            if(!_instance) {
                _instance = [super allocWithZone:zone];
                objc_setAssociatedObject(self, @"instance", _instance, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
            }
        }
        return _instance;
    }
    
    - (id)copyWithZone:(NSZone *)zone {
        return objc_getAssociatedObject(self, @"instance");
    }
    
    - (id)mutableCopyWithZone:(NSZone *)zone {
        return objc_getAssociatedObject(self, @"instance");
    }
    
    // 打印结果
    p1: <Person: 0x600000eeb4a0>
    p2: <Person: 0x600000eeb4a0>
    m1: <Man: 0x600000ef0ce0>
    m2: <Man: 0x600000ef0ce0>
    man is running
    

    相关文章

      网友评论

        本文标题:单例模式

        本文链接:https://www.haomeiwen.com/subject/xzhqiqtx.html