美文网首页iOS精英班iOSiOS
iOS之单例模式初探

iOS之单例模式初探

作者: 350fee9655f0 | 来源:发表于2015-07-10 21:40 被阅读32198次

    单例模式可能是设计模式中最简单的形式了,这一模式的意图就是使得类中的一个对象成为系统中的唯一实例。它提供了对类的对象所提供的资源的全局访问点。因此需要用一种只允许生成对象类的唯一实例的机制。下面让我们来看下单例的作用:

    • 可以保证的程序运行过程,一个类只有一个示例,而且该实例易于供外界访问
    • 从而方便地控制了实例个数,并节约系统资源。

    单例模式的使用场合

    • 类只能有一个实例,并且必须从一个为人数值的访问点对其访问。
    • 这个唯一的实例只能通过子类化进行拓展,并且拓展的对象不会破坏客户端代码。
    在Objective-C中方法都是公有的,而且OC的语言本身是动态类型的,因此所有类都可以相互发送对方的消息。,并且Cocoa框架使用计数的内存管理方式来维护对象的内存中的生存期。
    下面让我们看一下OC当中的单例模式的写法,首先单例模式在ARC\MRC环境下的写法有所不同,需要编写2套不同的代码
    • 可以用宏判断是否为ARC环境
    #if _has_feature(objc_arc)
    #else
    //MRC
    #endif
    

    单例模式- ARC -方法一

    • ARC中单例模式的实现
    • 在 .m中保留一个全局的static的实例
     static id _instance;
     //重写allocWithZone:方法,在这里创建唯一的实例(注意线程安全)
     + (instancetype)allocWithZone:(struct _NSZone *)zone
    {
        @synchronized(self) {
            if (_instance == nil) {
                _instance = [super allocWithZone:zone];
            }
        }
        return _instance;
    }
    
    
    • 提供1个类方法让外界访问唯一的实例
        + (instancetype)sharedInstanceTool{
        @synchronized(self){
            if(_instance == nil){
                _instance = [[self alloc] init];
            }
        }
        return _instance;
    }
    
    
    • 实现copyWithZone:方法
       -(id)copyWithZone:(struct _NSZone *)zone{
       return _instance;
       }
       ```
    ##### 我们在`sharedInstanceTool`,首先检查类的唯一实例是否已经创建,如果就会创建实例并将其返回。而之所以调用super而不是self,是因为已经在self中重载了基本的对象分配的方法,需要借用父类的功能来帮助处理底层内存的分配。
    ##### 在`allocWithZone:(struct _NSZone*)zone`方法中,只是返回从`sharedInstanceTool`方法返回的类实例。而同样的在Cocoa框架中调用`allocWithZone:(struct _NSZone*)zone`会分配内存,引用计数会设置为1,然后返回实例。同样的重写`(id)copyWithZone:(struct _NSZone *)zone`方法,也是为了保证不会返回实例的副本,而是返回self.返回同一个实例。
    
    #### 方法二:
    ```objc
    +(instancetype)sharedInstance {
       static WMSingleton *singleton = nil;
       if (! singleton) {
           singleton = [[self alloc] initPrivate];
       }
       return singleton;
    }
    
    - (instancetype)init {
       @throw [NSException exceptionWithName:@"这个是个单例"
                                      reason:@"应该这样调用 [WMSingleton sharedInstance]"
                                    userInfo:nil];
       return nil;
    }
    //实现自己真正的私有初始化方法
    - (instancetype)initPrivate {
       self  = [super init];
       return self;
    }
    

    上面这段代码中将singleton指针声明为静态变量。当某个定义了静态变量的方法返回时,程序不会释放相应的变量。####singleton变量的初始值是nil,当程序第一次执行sharedInstance方法时会创建一个对象,并将新创建的对象的地址赋值给singleton变量。当徐成再次执行sharedInstance方法时,无论多少次singleton变量仍然会指向最初那个创建的对象。因为指向对象的singleton变量是强引用的,并且程序永远不会释放该变量,所以singleton变量指向的对象也不会释放。

    线程安全。

    上面的实例中我们通过@synchronized来添加了一个互斥锁,以此来保证线程安全。而现在我们开始尝试用线程的方式来实现一个加单的单例。

    static WMObject *_instance;
    
    + (instancetype)allocWithZone:(struct _NSZone *)zone
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _instance = [super allocWithZone:zone];
        });
        return _instance;
    }
    
    + (instancetype)sharedInstance
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _instance = [[self alloc] init];
        });
        return _instance;
    }
    
    - (id)copyWithZone:(NSZone *)zone
    {
        return _instance;
    }
    
    从上面的代码我们可以看到,实现的思路基本上也是一致的我们在sharedInstanceTool,首先检查类的唯一实例是否已经创建,如果就会创建实例并将其返回。而略有不同的地方就是我们这次通过dispatch_once_t来保证线程的安全性。至于dispatch_once_t的用法这里就一一赘述了,线程的相关教程都会有其相关的描述。

    到了这里一个简单的单例模式基本实现完成了,那么我们可以尝试着把它封装到一个宏里,然后方便其以后的调用

    创建一个WMSingleton.h

    // .h文件
    #define WMSingletonH(name) + (instancetype)shared##name;
    
    // .m文件
    #define WMSingletonM(name) \
    static id _instance; \
     \
    + (instancetype)allocWithZone:(struct _NSZone *)zone \
    { \
        static dispatch_once_t onceToken; \
        dispatch_once(&onceToken, ^{ \
            _instance = [super allocWithZone:zone]; \
        }); \
        return _instance; \
    } \
     \
    + (instancetype)shared##name \
    { \
        static dispatch_once_t onceToken; \
        dispatch_once(&onceToken, ^{ \
            _instance = [[self alloc] init]; \
        }); \
        return _instance; \
    } \
     \
    - (id)copyWithZone:(NSZone *)zone \
    { \
        return _instance; \
    }
    
    使用方法
    //.h类
    //引入这个宏文件
    #import "WMSingleton.h"
    @interface WMObject : NSObject
    WMSingletonH(object)
    @end
    //.m类
    
    @implementation WMObject
    WMSingletonM(Car)
    @end
    

    通过上面的演练我们基本上学习了一些基本的单例模式然后在Cocoa Touch框架中同样存在着大量的单例模式让我们来学习,比如UIApplicationUIAccelerometer、以及NSFileManager等等。所以在单例模式的学习上还是依旧的任重道远呀。。。

    相关文章

      网友评论

      • 7529e3cee684:Do not override allocWithZone: to include any initialization code. Instead, class-specific versions of init... methods.

        This method exists for historical reasons; memory zones are no longer used by Objective-C.
      • Onegeng:http://www.cnblogs.com/496668219long/p/4507407.html这篇文章和你的差不多,请问原作者是你吗?请别把简书变成第二个百度.谢谢
        350fee9655f0:@Onegeng 不是,这篇文章有借鉴《Objective-C 编程之道:ios设计模式》,当时本想讲书中所讲的所有模式都做笔记,后来项目较忙也就懒得打理了。
      • yourbigbug:不错
      • 烟灰o_O:allocWithZone 已经被废弃, 完全没有必要考虑, 直接return NULL 就可以.
        不然还有一堆的接口new, copyWithZone ... 都需要考虑.

        其实 arc 下和 mrc 下是可以能用的.
      • e19cc3567234:@小沐的写字台 如果不存在_instance,才需要创建,如果已经创建则不需要,你这样写,我觉得创建不了对象
        350fee9655f0:@alex_ios 嗯,多谢提醒,一时大意,代码已经更新。
      • 350fee9655f0:@alex_ios 不需要的,单例模式就是只需要创建一次就足够了,不需要重复创建的。
        350fee9655f0:@summer_liu 多谢提醒,代码已经更正。就是用来判定为nil的时候才会去创建对象的。
        微微笑的蜗牛:@晓风沐晨
        +(id)allocWithZone:(struct _NSZone*)zone{
        @synchronized(self){
        if(_instance){
        _instance = [super allocWithZone:zone];
        }
        }
        }
        return _instance;


        + (instancetype)sharedInstanceTool{
        @synchronized(self){
        if(_instance){
        _instance = [[self alloc] init];
        }
        }
        return _instance;
        }

        _instance初始化是nil的,这样判断怎么能创建对象呢?
      • e19cc3567234:在第二、第三个代码块里,不是应该判断if(!_instance) 吗

      本文标题:iOS之单例模式初探

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