您的位置:首页 > 移动开发 > IOS开发

iOS设计模式 ——单例模式详解以及严格单例模式注意点

2016-08-17 14:29 489 查看
一、我们常用的单例有哪些?

[[UIApplication sharedApplication] statusBarStyle];//系统中的单例模式,通过它获取到状态栏的style


[NSNotificationCenter defaultCenter] addObserver:<#(nonnull id)#> selector:<#(nonnull SEL)#> name:<#(nullable NSString *)#> object:<#(nullable id)#>];//defaultCenter从控制中心类中获取到了单例的实例


[NSUserDefaults standardUserDefaults] setObject:<#(nullable id)#> forKey:<#(nonnull NSString *)#>];


[NSFileManager defaultManager];


从这些常用的单例可以发现,通过这些常用的单例方法来获取到这个类的唯一实例,再在这个实例的基础上进行相关的操作、作业。

二、单例模式基本原理

单例模式,一般用来管理某些资源的,用来管理某个对象,他这个对象持有了某些核心资源,这个资源可以全局共享。大部分情况我们使用单例模式就是为了共享信息 ,一般作为管理中心。

缺点是因为他共享了信息,就破坏了设计模式中的最少知识原则,产生了耦合,破坏了封装性,。但是在解决问题的过程中,这种不好的地方也是可以忽略的。

下面我们来自己写一个单例的例子,详细的讲一下对单例的理解。创建一个UserInfoManagerCenter的类

仿照系统的单例形式自己写这个类方法。
#import <Foundation/Foundation.h>


@interface UserInfoManagerCenter : NSObject

@property (nonatomic ,strong) NSString *name;

@property (nonatomic ,strong) NSString *age;


+ (instancetype)managerCenter;


@end


在UserInfoManagerCenter.m中实现这个方法
#import "UserInfoManagerCenter.h"


@implementation UserInfoManagerCenter


/**

*  常规做法

*/

+(instancetype)managerCenter

{

   static UserInfoManagerCenter *center = nil;//静态变量持有这个对象

   if (center == nil) {

       center = [[UserInfoManagerCenter alloc]init];

   }

   return center;

}

@end


但是这种方法并不好,当多个地方调用这个方法时,会造成同时都进入到alloc init。

因此,使用第二种方法

#import "UserInfoManagerCenter.h"

@implementation UserInfoManagerCenter

/**

*  第二种方案,用dispatch_once来解决竞争问题

*/

+(instancetype)managerCenter

{

   static UserInfoManagerCenter *center = nil;

   static dispatch_once_t onceToken;

   dispatch_once(&onceToken, ^{

       center = [[UserInfoManagerCenter alloc]init];

   });

   return center;

}

@end


当然还有第三种方法    ---   initialize的作用,同一个类初始化时只会调用一次。

#import "UserInfoManagerCenter.h"

static UserInfoManagerCenter *center = nil;


@implementation UserInfoManagerCenter

/**

*  第三种方法,每个类调用任意方法时都会提前调用的这个initialize方法,initialize的作用,同一个类初始化时只会调用一次。所以说我们将单例写在这个地方也是没有问题的,但是不推荐

*/

+(void)initialize

{

if (self == [UserInfoManagerCenter class]) {

center = [[UserInfoManagerCenter alloc]init];

}

}

+(instancetype)managerCenter

{

return center;

}

@end


让我们来验证下,然后在AppDelegate里赋值

#import "AppDelegate.h"

#import "UserInfoManagerCenter.h"

@interface AppDelegate ()

@end


@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {


UserInfoManagerCenter *center = [UserInfoManagerCenter managerCenter];

center.name = @"YUSIR";


return YES;

}


在ViewController的viewDidLoad里取出值查看结果

#import "ViewController.h"

#import "UserInfoManagerCenter.h"

@interface ViewController ()

@end


@implementation ViewController

- (void)viewDidLoad {

[super viewDidLoad];

UserInfoManagerCenter * center = [UserInfoManagerCenter managerCenter];

NSLog(@"name:%@",center.name);

}


三种方法结果能打印出来,单例实现了资源共享

三、严格单例模式的注意点

下面来简单谈谈严格的单例模式,有三个问题可能需要注意一下:

1.如何防止继承;

2.如何确保实例对象只出现一个;

3.防止实例对象被释放掉;

第一个问题,防止继承

+(instancetype)managerCenter

{

static UserInfoManagerCenter *center = nil;

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

center = [[UserInfoManagerCenter alloc]init];

});


//防止子类重载调用使用

NSString *classString = NSStringFromClass([self class]);//获取当前类的名字

if ([classString isEqualToString:@"UserInfoManagerCenter"] == NO) {

NSParameterAssert(nil); //填nil会导致程序崩溃

}

return center;

}


创建一个子类继承自UserInfoManagerCenter,调用managerCenter会直接崩溃,比较简单这里就不截图了

第二个问题,如何确保实例对象只出现一个。除了类方法之类还有init方法,只能重写他的init方法,来实现init方法失效

static UserInfoManagerCenter *center = nil;

@implementation UserInfoManagerCenter

+(instancetype)managerCenter

{

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

center = (UserInfoManagerCenter *)@"UserInfoManagerCenter";

center = [[UserInfoManagerCenter alloc]init];

});

//防止子类重载调用使用

NSString *classString = NSStringFromClass([self class]);//获取当前类的名字

if ([classString isEqualToString:@"UserInfoManagerCenter"] == NO) {

NSParameterAssert(nil); //填nil会导致程序崩溃

}

return center;

}

- (instancetype)init {

NSString *string = (NSString *)center;

if ([string isKindOfClass:[NSString class]]== YES && [string isEqualToString:@"UserInfoManagerCenter"]) {

self = [super init];

if (self) {

//防止子类重载调用使用

NSString *classString = NSStringFromClass([self class]);//获取当前类的名字

if ([classString isEqualToString:@"UserInfoManagerCenter"] == NO) {

NSParameterAssert(nil); //填nil会导致程序崩溃

}

}

return self;

}else {

return nil;

}

}


第三个,由于现在是项目是ARC开发的,是引用计数管理的。无法重载release,可以跳过这个问题。

当然严格的单例模式,只要注意避免类似情况发生,就可以不用过多考虑这些负担了。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息