iOS 单例模式全面解析


我先列举一个苹果官方文档中的写法。

  1. static AccountManager *DefaultManager = nil;  

  2.    

  3. + (AccountManager *)defaultManager {  

  4.     if (!DefaultManager) DefaultManager = [[self allocWithZone:NULL] init];  

  5.     return DefaultManager;  

  6. }  

iOS4之后新增加的方法:

  1. + (AccountManager *)sharedManager  

  2. {  

  3.         static AccountManager *sharedAccountManagerInstance = nil;  

  4.         static dispatch_once_t predicate;  

  5.         dispatch_once(&predicate, ^{  

  6.                 sharedAccountManagerInstance = [[self alloc] init];   

  7.         });  

  8.     return sharedAccountManagerInstance;  

  9. }  

关于dispatch_once 函数官方文档解释如下:

  • Declaration

    void dispatch_once( dispatch_once_t *predicate, dispatch_block_t block);

    Parameters

    predicate

    A pointer to a dispatch_once_t structure that is used to test whether the block has completed or not.

    block

    The block object to execute once.

    Discussion

    This function is useful for initialization of global data (singletons) in an application. Always call this function before using or testing any variables that are initialized by the block.

    If called simultaneously from multiple threads, this function waits synchronously until the block has completed.

    The predicate must point to a variable stored in global or static scope. The result of using a predicate with automatic or dynamic storage (including Objective-C instance variables) is undefined.

  • Executes a block object once and only once for the lifetime of an application.

  • 翻译之后:

  • 1.参数 predicate是一个指向dispatch_once_t结构体的指针,用来判断下面的block体是否执行完毕

  • 2.参数block即要被执行的block

  • 3.在应用中这个函数是用来初始化一个全局变量(单例)的。在用block初始化任何变量之前总是调用这个函数。如果这个函数被多个线程同时调用,那么函数会并发执行,直到block被执行完毕。换句话,函数支持多线程。predicate指针必须指向一个存储在全局或者静态存储区的变量,否则导致的结果不可预知。

  • 4.在一个应用的生命周期这个block执行且只执行一次

  • 那么按照官方文档要求我们先创建一个AccountManager类看一下,.m文件如下

  • static AccountMangager *_instance;
    @implementation AccountMangager
    + (instancetype)shareManager
    {
       static dispatch_once_t predicate;
        dispatch_once(&predicate, ^{
            _instance = [[self alloc] init];
        });
        return _instance;
    }

    外界引用:

  • - (void)viewDidLoad {
        [super viewDidLoad];
        AccountMangager *manager1 = [AccountMangager shareManager];
        NSLog(@"manager1 = %@",manager1);
        AccountMangager *manager2 = [AccountMangager shareManager];
        NSLog(@"manager2 = %@",manager2);
        AccountMangager *manager3 = [[AccountMangager alloc] init];
        NSLog(@"manager3 = %@",manager3);
    }
  • 打印:

  • 2015-12-13 14:17:54.642 单例模式[49159:17491715] manager1 = <AccountMangager: 0x7fc398f2cdb0>

  • 2015-12-13 14:17:54.642 单例模式[49159:17491715] manager2 = <AccountMangager: 0x7fc398f2cdb0>

  • 2015-12-13 14:17:54.642 单例模式[49159:17491715] manager3 = <AccountMangager: 0x7fc398f29c10>

  • 大家可以看到当我们调用shareManager方法时获取到的对象是相同的,但是当我们通过alloc和init来构造对象的时候,得到的对象却是不一样的。

  • 那么问题就来了,我们通过不同的途径得到不同的对象,显然是不行的。我们必须要确保对象的唯一性,所以我们就需要封锁用户通过alloc和init以及copy来构造对象这条道路。

  • 我们知道,创建对象的步骤分为申请内存(alloc)、初始化(init)这两个步骤,我们要确保对象的唯一性,因此在第一步这个阶段我们就要拦截它。当我们调用alloc方法时,oc内部会调用allocWithZone这个方法来申请内存,我们覆写这个方法,然后在这个方法中调用shareInstance方法返回单例对象,这样就可以达到我们的目的。拷贝对象也是同样的原理,覆写copyWithZone方法,然后在这个方法中调用shareInstance方法返回单例对象

  • +(instancetype)shareManager
    {
        static dispatch_once_t predicate;
        dispatch_once(&predicate, ^{
            _instance = [[super allocWithZone:NULL]init ];
        });
        return _instance;
    }
    + (id)allocWithZone:(struct _NSZone *)zone
    {
        return [AccountMangager shareManager];
    }
    - (id)copy{
        return [AccountMangager shareManager];
    }

    再次运行打印:

  • 2015-12-13 14:31:57.413 单例模式[49215:17499767] manager1 = <AccountMangager: 0x7fcf58e233a0>

  • 2015-12-13 14:31:57.414 单例模式[49215:17499767] manager2 = <AccountMangager: 0x7fcf58e233a0>

  • 2015-12-13 14:31:57.414 单例模式[49215:17499767] manager3 = <AccountMangager: 0x7fcf58e233a0>



你可能感兴趣的:(单例,ios)