热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

iOS开发——单例的实现、使用与架构

单例在我们开发中是最常用的设计模式,在iOS中也是如此。单例可以保证某个类的实例在程序中是唯一的,便于进行资源和数据的共享。使用的设计原则是单一职责原则

       单例在我们开发中是最常用的设计模式,在iOS中也是如此。单例可以保证某个类的实例在程序中是唯一的,便于进行资源和数据的共享。使用的设计原则是单一职责原则。我们来看看在iOS中本身自带的类或者方法哪些使用了单例的模式:

(1)UIAccelerometer类和sharedAccelerometer方法,一般如果方法名中有shared这样的词,就可以认为这是一个可以整个应用程序共享的实例变量,一般是使用了单例。

(2)UIApplication类和sharedApplication方法,我们一般使用该方法来创建全局变量。

(3)NSBundle类和mainBundle方法。

(4)NSFileManager类和defaultManager方法。

(5)NSNotificationCenter类和defaultManager方法。其中NSNotificationCenter也实现了观察者模式。

(6)NSUserDefaults类和defaultUser方法。

示例代码上传至:https://github.com/chenyufeng1991/iOS-Singleton   。


【单例实现】

(1)新建一个普通的类,假设名字为Singleton.   在Singleton.h中声明一个类方法,到时候使用该类方法(注意:一定是类方法,而不是实例方法)可以创建该类的唯一的一个实例:

#import @class Singleton;@interface Singleton : NSObject
// "+" 表示类的方法,由类调用
+(Singleton *)sharedInstance;@end


(2)在Singleton.m中需要实现sharedInstance方法和你其他的业务逻辑:

#import "Singleton.h"// 用static声明一个类的静态实例;
static Singleton *_sharedInstance = nil;@implementation Singleton/*** 1.使用类方法生成这个类唯一的实例;*/
+(Singleton *)sharedInstance{if (!_sharedInstance) {_sharedInstance =[[self alloc]init];}return _sharedInstance;
}@end


注意:一定要声明一个static的静态变量。以后创建类的唯一实例就使用sharedInstance方法,而不是使用alloc ,init.


(3)我们使用一个简单的demo来演示一下单例:

#import "RootVC.h"
#import "Singleton.h"@interface RootVC ()@end@implementation RootVC- (void)viewDidLoad
{[super viewDidLoad];[self testSigleTon];
}-(void)testSigleTon
{//单例的结果就是,调用类方法,只返回一个共有的对象/*** single和single2是同一个对象;因为返回的数据是一个静态变量,全局唯一;*/Singleton *single = [Singleton sharedInstance];Singleton *single2 = [Singleton sharedInstance];if (single == single2) {NSLog(@"single == single2");}NSLog(@"single地址:%@",single);NSLog(@"single2地址:%@",single2);}@end


(4)输出结果如下:



      可以看到,两个对象的内存地址是一样的,表示这两个对象其实是同一个对象,单例也就实现了。这是单例最普遍也是最简单的实现方式,在项目中会经常用到,在不涉及多线程的情况下是完全正确的。但是,我们再多想一想,在多线程开发中,这种实现方式是否安全呢?那么应该如何实现。


【单例架构】

       在项目开发中,如果我们像上述实现方法一样,在每个类中都使用这样写一个方法来生成单例,会不会显得很麻烦,很冗余。这样重复在每个类中重复写代码不利于开发与架构,那么我们应该使用什么方法来进行代码抽取呢?解决方案就是使用类别(Category)。关于Category类别的简要介绍,请参考《Objective-C——类别(Category)详解》。具体的实现如下:

(1)新建一个Category,作为对NSObject类的扩展。因为NSObject类是大部分iOS类的基类,如果使用Category为NSObject增加额外方法(shareInstance方法),那么所有继承自NSObject的类都可以使用该方法。我们常用的UIViewController和UIView都是从NSObject继承的,这样就会很方便。



(2)类别Category生成以后文件如下:


需要在NSObject+Singleton.h头文件中对外暴露一个生成实例的方法,供其他类调用。

#import @interface NSObject (Singleton)// "+" 表示类的方法,由类调用
+ (instancetype)sharedInstance;@end


(3)在NSObject+Singleton.m中需要实现上述方法,用来生成某一个类的唯一实例。我这里使用字典来存储某一个类和该类的实例,也就是键值对的形式:键是类名,值是对象。根据类名去检索该类的对象是否已经被创建,如果检索到类名,表示已经被创建,则直接返回对象;如果没有检索到类名,则需要创建,创建完成后也存储到字典中;

#import "NSObject+Singleton.h"@implementation NSObject (Singleton)//使用可变字典存储每个类的单一实例,键为类名,值为该类的对象;
//声明为静态变量,可以保存上次的值;
static NSMutableDictionary *instanceDict;
id instance;+ (instancetype)sharedInstance {@synchronized(self){//初始化字典;if (instanceDict == nil) {instanceDict = [[NSMutableDictionary alloc] init];}//获取类名;NSString *className = NSStringFromClass([self class]);if (className) {//查找字典中该类的对象,使用类名去进行查找,可以确保一个类只被存储一次;instance = instanceDict[className];if (instance == nil) {//该类的对象还没实例化,就进行初始化,并根据键值对的形式存储;instance = [[self.class alloc] init];[instanceDict setValue:instance forKey:className];}else{//该类对象已经存储在字典中,直接返回instance即可;}}else{//没有获取类名,所以确保sharedInstance是一个类方法,用类进行调用;}return instance;}
}
@end


(4)单例的category已经写完,下面将要进行测试。我这里的测试方法如下:两个界面之间进行跳转并返回,使用相同的代码生成类对象;同时新建一个Person类和StudentModel类来测试,两个类都分别继承自NSObject,里面没有任何实现,只用来创建对象。别忘了导入头文件#import "NSObject+Singleton.h".

第一个界面ViewController.m实现如下:

#import "ViewController.h"
#import "NSObject+Singleton.h"
#import "Person.h"
#import "StudentModel.h"&#64;interface ViewController ()&#64;end&#64;implementation ViewController- (void)viewDidLoad {[super viewDidLoad];}- (void)viewDidAppear:(BOOL)animated{[super viewDidAppear:true];//使用sharedInstance创建类对象&#xff1b;ViewController *vc1 &#61; [ViewController sharedInstance];ViewController *vc2 &#61; [ViewController sharedInstance];NSLog(&#64;"ViewController---vc1地址:%&#64;",vc1);NSLog(&#64;"ViewController---vc2地址:%&#64;",vc2);if (vc1 &#61;&#61; vc2) {NSLog(&#64;"ViewController---vc1 &#61;&#61; vc2");}//循环创建5个Person对象&#xff0c;5个对象都相同&#xff1b;for (int i &#61; 0; i <5; i&#43;&#43;) {Person *per1 &#61; [Person sharedInstance];NSLog(&#64;"ViewController---per1地址&#xff1a;%&#64;",per1);}//使用alloc创建对象&#xff0c;每个对象都是不同的&#xff1b;for (int i &#61; 0; i <5; i&#43;&#43;) {StudentModel *stud &#61; [[StudentModel alloc] init];NSLog(&#64;"ViewController---stud地址&#xff1a;%&#64;",stud);}}&#64;end




第二个界面SecondViewController.m实现如下&#xff1a;

#import "SecondViewController.h"
#import "NSObject&#43;Singleton.h"
#import "Person.h"
#import "StudentModel.h"
#import "ViewController.h"&#64;interface SecondViewController ()&#64;end&#64;implementation SecondViewController/*** 在另一个界面中做同样的测试&#xff1b;*/
- (void)viewDidLoad {[super viewDidLoad];}- (void)viewDidAppear:(BOOL)animated{[super viewDidAppear:animated];SecondViewController *secondVc1 &#61; [SecondViewController sharedInstance];SecondViewController *secondVc2 &#61; [SecondViewController sharedInstance];NSLog(&#64;"SecondViewController---secondVc1地址:%&#64;",secondVc1);NSLog(&#64;"SecondViewController---secondVc2地址:%&#64;",secondVc2);if (secondVc1 &#61;&#61; secondVc2) {NSLog(&#64;"SecondViewController---secondVc1 &#61;&#61; secondVc2");}for (int i &#61; 0; i <5; i&#43;&#43;) {Person *per1 &#61; [Person sharedInstance];NSLog(&#64;"SecondViewController---per1地址&#xff1a;%&#64;",per1);}for (int i &#61; 0; i <5; i&#43;&#43;) {StudentModel *stud &#61; [[StudentModel alloc] init];NSLog(&#64;"SecondViewController---stud地址&#xff1a;%&#64;",stud);}}/*** 返回上一界面&#xff0c;再次生成对象查看&#xff1b;** &#64;param sender <#sender description#>*/
- (IBAction)back:(id)sender {[self dismissViewControllerAnimated:true completion:nil];
}&#64;end




下面分别是三个步骤打印log:启动第一个界面、跳转到第二个界面、返回第一个界面。

启动第一个界面的输出&#xff1a;



跳转到第二个界面的输出&#xff1a;



再次返回到第一个界面的输出&#xff1a;



      总结&#xff0c;通过以上打印输出&#xff0c;使用sharedInstance创建单例后&#xff0c;无论在哪一个界面&#xff0c;每个类的对象是唯一的。而使用alloc创建的对象往往都是不同的。通过以上的设计&#xff0c;就不需要在每一个类中都去实现sharedInstance方法了。



github主页&#xff1a;https://github.com/chenyufeng1991  。欢迎大家访问&#xff01;

最近极客学院Wiki正在进行IT职业技能图谱的制定&#xff0c;我主要负责iOS方向&#xff0c;大家感兴趣的可以一起参加&#xff0c;有问题或者修改可以直接给我发issues或者pull request。https://github.com/chenyufeng1991/skillmap  。



推荐阅读
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有