iOS开发——单例的实现、使用与架构
来源:互联网 发布:小学生学编程 编辑:程序博客网 时间:2024/06/06 00:53
单例在我们开发中是最常用的设计模式,在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 <Foundation/Foundation.h>@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 <Foundation/Foundation.h>@interface NSObject (Singleton)// "+" 表示类的方法,由类调用+ (instancetype)sharedInstance;@end
#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"@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad];}- (void)viewDidAppear:(BOOL)animated{ [super viewDidAppear:true]; //使用sharedInstance创建类对象; ViewController *vc1 = [ViewController sharedInstance]; ViewController *vc2 = [ViewController sharedInstance]; NSLog(@"ViewController---vc1地址:%@",vc1); NSLog(@"ViewController---vc2地址:%@",vc2); if (vc1 == vc2) { NSLog(@"ViewController---vc1 == vc2"); } //循环创建5个Person对象,5个对象都相同; for (int i = 0; i < 5; i++) { Person *per1 = [Person sharedInstance]; NSLog(@"ViewController---per1地址:%@",per1); } //使用alloc创建对象,每个对象都是不同的; for (int i = 0; i < 5; i++) { StudentModel *stud = [[StudentModel alloc] init]; NSLog(@"ViewController---stud地址:%@",stud); }}@end
第二个界面SecondViewController.m实现如下:
#import "SecondViewController.h"#import "NSObject+Singleton.h"#import "Person.h"#import "StudentModel.h"#import "ViewController.h"@interface SecondViewController ()@end@implementation SecondViewController/** * 在另一个界面中做同样的测试; */- (void)viewDidLoad { [super viewDidLoad];}- (void)viewDidAppear:(BOOL)animated{ [super viewDidAppear:animated]; SecondViewController *secondVc1 = [SecondViewController sharedInstance]; SecondViewController *secondVc2 = [SecondViewController sharedInstance]; NSLog(@"SecondViewController---secondVc1地址:%@",secondVc1); NSLog(@"SecondViewController---secondVc2地址:%@",secondVc2); if (secondVc1 == secondVc2) { NSLog(@"SecondViewController---secondVc1 == secondVc2"); } for (int i = 0; i < 5; i++) { Person *per1 = [Person sharedInstance]; NSLog(@"SecondViewController---per1地址:%@",per1); } for (int i = 0; i < 5; i++) { StudentModel *stud = [[StudentModel alloc] init]; NSLog(@"SecondViewController---stud地址:%@",stud); }}/** * 返回上一界面,再次生成对象查看; * * @param sender <#sender description#> */- (IBAction)back:(id)sender { [self dismissViewControllerAnimated:true completion:nil];}@end
下面分别是三个步骤打印log:启动第一个界面、跳转到第二个界面、返回第一个界面。
启动第一个界面的输出:
。
跳转到第二个界面的输出:
。
再次返回到第一个界面的输出:
总结,通过以上打印输出,使用sharedInstance创建单例后,无论在哪一个界面,每个类的对象是唯一的。而使用alloc创建的对象往往都是不同的。通过以上的设计,就不需要在每一个类中都去实现sharedInstance方法了。
github主页:https://github.com/chenyufeng1991 。欢迎大家访问!
最近极客学院Wiki正在进行IT职业技能图谱的制定,我主要负责iOS方向,大家感兴趣的可以一起参加,有问题或者修改可以直接给我发issues或者pull request。https://github.com/chenyufeng1991/skillmap 。
- iOS开发——单例的实现、使用与架构
- 单例的实现、使用与架构
- ios开发单例模式——使用GCD实现单例模式 & 非ARC单例模式 &使用GCD和线程锁实现单例模式
- iOS开发单例的实现
- iOS开发47-iOS 单例的学习和使用
- 单例的实现与使用
- iOS开发—使用GCD实现多线程(单次或重复执行任务)
- IOS开发——单例
- iOS开发——单例
- iOS 单例的实现
- ios 单例的实现
- IOS单例的实现
- ios单例的实现
- iOS开发 单例使用问题
- iOS 单例的使用
- iOS--使用GCD实现单例模式
- Magento单例模式的实现与使用
- iOS开发—使用NSThread实现多线程
- Android 人名 随机抽奖
- Codeforces 607B - Zuma (区间DP)
- Errors running builder 'DeploymentBuilder' on project '工程名' xxxNullpointException
- JDK,JRE,JVM,SDK,API,ADT,OOM,ANR
- WCF生成客户端对象方式解析
- iOS开发——单例的实现、使用与架构
- 葡萄城报表-核心代码展示
- Win8如何查看已保存的无线网络
- POJ1035——Spell checker
- spring头
- Android-Creating an Input Method(IME)
- ios程序中的内存分配 栈区堆区全局区
- 关于hbase的一些调优问题
- 带头节点单链表的实现