iOS单例

IOS 中单例设计模式的解读与用法

一、单例的作用

      顾名思义,单例,即是在整个项目中,这个类的对象只能被初始化一次。它的这种特性,可以广泛应用于某些需要全局共享的资源中,比如管理类,引擎类,也可以通过单例来实现传值。UIApplication、NSUserDefaults等都是IOS中的系统单例。

二、单例的写法

       单例的写法常用的有两种方式:

       方式1、不考虑线程

1
2
3
4
5
6
7
8
static SingleCase *manager = nil;  
    
+ (SingleCase *)defaultManager {  
    if (!manager){ 
        SingleCase = [[self alloc] init];  
        return manager; 
        }
}

          方式2、考虑线程安全

1
2
3
4
5
6
7
8
9
+ (SingleCase *)sharedManager  
{  
        static SingleCase *ManagerInstance = nil;  
        static dispatch_once_t predicate;  
        dispatch_once(&predicate, ^{  
                ManagerInstance = [[self alloc] init];   
        });  
    return ManagerInstance;  
}

三、代码的优化

        通过上面的方法,我们已经可以使用类方法来得到这个单例,但很多时候,项目的工程量很大,还有可能会很多开发者同时参与一个项目的开发,为了安全与管理代码的方便,也为了给不是这个单例的创作者但会用到这个单例的开发人员一些提示,我们通常会重写一些方法:

首先我们自己实现一个alloc方法:

1
2
3
+(instancetype)myAlloc{
    return [super allocWithZone:nil];
}

将我们的单例实现方法略作修改:

1
2
3
4
5
6
7
+(ZYHPayManager *)sharedMamager{
    static ZYHPayManager * manager;
    if (manager==nil) {
        manager=[[ZYHPayManager myAlloc]init];
    }
    return manager;
}

将一些视图实例化对象的方法重写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+(instancetype)alloc{
    NSAssert(0, @"这是一个单例对象,请使用+(ZYHPayManager *)sharedMamager方法");
    return nil;
}
+(instancetype)allocWithZone:(struct _NSZone *)zone{
    return [self alloc];
}
-(id)copy{
    NSLog(@"这是一个单例对象,copy将不起任何作用");
    return self;
}
+(instancetype)new{
    return  [self alloc];
}

注意:这里的alloc使用了断言,让任何视图通过alloc创建对象的程序段断在此处,给程序员提示。copy方法这里只是简单的返回了原对象,并未做任何处理,打印信息给程序员提示。

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. }  


该写法来自 objcolumnist,文中提到,该写法具有以下几个特性:

1. 线程安全。

2. 满足静态分析器的要求。

3. 兼容了ARC

然后我还有点好奇的是dispatch_once,这个函数,没见过啊。

于是就到官方的文档里找找看,是怎么说的。

下面是官方文档介绍:

dispatch_once

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

  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 is undefined.

Availability

  • Available in iOS 4.0 and later.

Declared In

dispatch/once.h

我们看到,该方法的作用就是执行且在整个程序的声明周期中,仅执行一次某一个block对象。简直就是为单例而生的嘛。而且,有些我们需要在程序开头初始化的动作,如果为了保证其,仅执行一次,也可以放到这个dispatch_once来执行。

然后我们看到它需要一个断言来确定这个代码块是否执行,这个断言的指针要保存起来,相对于第一种方法而言,还需要多保存一个指针。

方法简介中就说的很清楚了:对于在应用中创建一个初始化一个全局的数据对象(单例模式),这个函数很有用。

如果同时在多线程中调用它,这个函数将等待同步等待,直至该block调用结束。

这个断言的指针必须要全局化的保存,或者放在静态区内。使用存放在自动分配区域或者动态区域的断言,dispatch_once执行的结果是不可预知的。

总结:1.这个方法可以在创建单例或者某些初始化动作时使用,以保证其唯一性。2.该方法是线程安全的,所以请放心大胆的在子线程中使用。(前提是你的dispatch_once_t *predicate对象必须是全局或者静态对象。这一点很重要,如果不能保证这一点,也就不能保证该方法只会被执行一次。)

原文地址:https://www.cnblogs.com/fengmin/p/5015833.html