iOS设计模式 ——单例模式详解以及严格单例模式注意点 水深无声 2021-09-11 01:10 313阅读 0赞 **一、我们常用的单例有哪些?** [[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); } 三种方法结果能打印出来,单例实现了资源共享![Image 1][] **三、严格单例模式的注意点** 下面来简单谈谈严格的单例模式,有三个问题可能需要注意一下: 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,可以跳过这个问题。 当然严格的单例模式,只要注意避免类似情况发生,就可以不用过多考虑这些负担了。 [Image 1]:
还没有评论,来说两句吧...