1、单例模式
一、系统中的单例
1、UIApplication
1)一个iOS程序启动后创建的第一个对象就是UIApplication对象,且只有一个(通过代码获取两个UIApplication对象,打印地址可以看出地址是相同的)。
2)通过[UIApplication sharedApplication]可以获得这个单例对象
3)利用UIApplication对象,能进行一些应用级别的操作,如:
- 设置应用程序图标右上角的红色提醒数字 @property(nonatomic) NSInteger applicationIconBadgeNumber;
- 设置联网指示器的可见性 @property(nonatomic,getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible;
- 管理状态栏在iOS7中,默认情况下,状态栏都是由UIViewController管理的,所以,如果状态栏的样式只设置一次,那就用UIApplication来进行管理;如 果状态栏是否隐藏,样式不一样那就用控制器进行管理。UIApplication来进行管理有额外的好处,可以提供动画效果。
状态栏的样式 - (UIStatusBarStyle)preferredStatusBarStyle;
状态栏的可见性 -(BOOL)prefersStatusBarHidden;
4)openURL:方法
- 打电话 [app openURL:[NSURLURLWithString:@"tel://10086"]];
- 发短信 [app openURL:[NSURLURLWithString:@"sms://10086"]];
- 发邮件 [app openURL:[NSURLURLWithString:@"mailto://12345@qq.com"]];
- 打开一个网页资源 [app openURL:[NSURLURLWithString:@"http://ios.itcast.cn"]];
- 打开其他app程序 openURL方法,可以打开其他APP。
5)代理
// 当应用程序启动完毕的时候就会调用(系统自动调用) - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions NS_AVAILABLE_IOS(3_0); // 即将失去活动状态的时候调用(失去焦点, 不可交互) - (void)applicationWillResignActive:(UIApplication *)application; // 应用程序进入后台的时候调用 一般在该方法中保存应用程序的数据, 以及状态 - (void)applicationDidEnterBackground:(UIApplication *)application NS_AVAILABLE_IOS(4_0); // 应用程序即将进入前台的时候调用, 一般在该方法中恢复应用程序的数据,以及状态 - (void)applicationWillEnterForeground:(UIApplication *)application; // 应用程序即将被销毁的时候会调用该方法, 注意:如果应用程序处于挂起状态的时候无法调用该方法 (void)applicationWillTerminate:(UIApplication *)application; // 应用程序接收到内存警告的时候就会调用, 一般在该方法中释放掉不需要的内存 - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application;
2、NSNotificationCenter
1) 通过[NSNotificationCenter defaultCenter]可以获得这个单例对象
2)发通知
- [[NSNotificationCenter defaultCenter] postNotificationName:@"myNotice" object:nil userInfo:@{@"sendValue" forKey:@"sendKey"}];
3)接受通知
- - (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject;
4)只要往NSNotificationCenter
注册了,就必须有remove
的存在
3、NSUserDefaults
1) 通过[NSUserDefaults standardUserDefaults]可以获得这个单例对象
2) 支持的数据类型有:NSNumber(NSInteger、float、double),NSString,NSDate,NSArray,NSDictionary,BOOL.
3) 注意事项
- 对相同的Key赋值约等于一次覆盖,要保证每一个Key的唯一性
- NSUserDefaults 存储的对象全是不可变的
- 不支持自定义对象的存储,不过它支持NSData的类型
4) 存储自定义对象
- 将自定义类型转换为NSData类型:必须实现NSCoding 协议,实现 encodeWithCoder 方法 和 initWithCoder 方法
- 自定义类型数据存入 NSUserDefaults 中
5) 常用方法
- - (id)objectForKey:(NSString *)defaultName;通过标识符取出数据
- - (void)setObject:(id)value forKey:(NSString *)defaultName;通过标识符进行存储
- - (void)removeObjectForKey:(NSString *)defaultName;通过名字来移除数据
- - (BOOL)synchronize;同步数据到磁盘中,
这个方法是以一定周期间隔自动调用的,不想等待可以手动调用
4、NSFileManager
1)、沙盒机制:应用只能访问自己应用目录下的文件。(越狱机除外)
- Documents:苹果建议将程序创建产生的文件以及应用浏览产生的文件数据保存在该目录下,iTunes备份和恢复的时候会包括此目录
- Library:存储程序的默认设置或其它状态信息;
- Library/Caches:存放缓存文件,保存应用的持久化数据,用于应用升级或者应用关闭后的数据保存,不会被itunes同步,所以为了减少同步的时间,可以考虑将一些比较大的文件而又不需要备份的文件放到这个目录下。
- tmp:提供一个即时创建临时文件的地方,但不需要持久化,在应用关闭后,该目录下的数据将删除,也可能系统在程序不运行的时候清除
- 获取应用沙盒根路径:
NSString *dirHome=NSHomeDirectory();
- 获取Documents目录路径:
//[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0];
- 获取Library目录路径
//[NSHomeDirectory() stringByAppendingPathComponent:@"Library"]; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); NSString *libraryDirectory = [paths objectAtIndex:0];
- 获取Cache目录路径:
//[NSHomeDirectory() stringByAppendingPathComponent:@"tmp"]; NSString *tmpDirectory = NSTemporaryDirectory();
2)通过[NSFileManager defaultManager]可以获得这个单例对象
3)创建文件和创建文件夹
//创建文件 -(void *)createFile { NSString *documentsPath =[self dirDoc]; NSString *testDirectory = [documentsPath stringByAppendingPathComponent:@"test"]; NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *testPath = [testDirectory stringByAppendingPathComponent:@"test.txt"]; BOOL res=[fileManager createFileAtPath:testPath contents:nil attributes:nil]; if (res) { NSLog(@"文件创建成功: %@" ,testPath); }else NSLog(@"文件创建失败"); } //创建文件夹 -(void *)createDir { NSString *documentsPath =[self dirDoc]; NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *testDirectory = [documentsPath stringByAppendingPathComponent:@"test"]; // 创建目录 BOOL res=[fileManager createDirectoryAtPath:testDirectory withIntermediateDirectories:YES attributes:nil error:nil]; if (res) { NSLog(@"文件夹创建成功"); }else NSLog(@"文件夹创建失败"); }
}
4)删除文件和文件夹
//删除文件 -(void)deleteFile { NSString *documentsPath =[self dirDoc]; NSString *testDirectory = [documentsPath stringByAppendingPathComponent:@"test"]; NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *testPath = [testDirectory stringByAppendingPathComponent:@"test.txt"]; BOOL res=[fileManager removeItemAtPath:testPath error:nil]; if (res) { NSLog(@"文件删除成功"); }else NSLog(@"文件删除失败"); NSLog(@"文件是否存在: %@",[fileManager isExecutableFileAtPath:testPath]?@"YES":@"NO"); }
}
-(void)deleteDir
{
NSString *documentsPath =[self dirDoc]; NSString *testDirectory = [documentsPath stringByAppendingPathComponent:@"test"]; NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL res=[fileManager removeItemAtPath:testDirectory error:nil]; if (res) { NSLog(@"文件夹删除成功"); }else NSLog(@"文件夹删除失败"); NSLog(@"文件夹是否存在: %@",[fileManager isExecutableFileAtPath:testPath]?@"YES":@"NO");
}
}
5)移动文件到另一个文件夹和复制文件夹
//从一个文件夹中移动A文件到另一个文件夹
-(BOOL)moveDiectory:(NSString*)nowparh prePath:(NSString*)prePath { if (nowparh == nil || nowparh.length == 0) return NO; if (prePath == nil || prePath.length == 0) return NO; NSFileManager * fileManager = [NSFileManager defaultManager]; if ([fileManager fileExistsAtPath:prePath]) { NSError * error; [fileManager moveItemAtPath:prePath toPath:nowparh error:&error]; if (error) { PSLog(@"error:%@",error); return NO; }; return YES; } return NO; }
//从A文件夹中复制文件到B文件夹中 -(BOOL)copyDirectory:(NSString*)nowpath prepath:(NSString*)prepath { if (nowpath == nil || nowpath.length == 0) return NO; if (prepath == nil || prepath.length == 0) return NO; NSFileManager * fileManager = [NSFileManager defaultManager]; if ([fileManager fileExistsAtPath:prepath]) { NSError * error; [fileManager copyItemAtPath:prepath toPath:nowpath error:&error]; if (error) { PSLog(@"error:%@",error); return NO; } return YES; } return NO; }
二、单例模式定义
保证一个类仅有一个实例对象,并提供了一个访问它的全局访问点。
三、适合场合
1、类只能有一个实例。
2、只能共享资源而不能复制的资源类。
四、优缺点
1、共享信息,易于外界访问。
2、作为一个管理中心。
3、破坏了封装性,耦合度高。
五、创建严格的单例
1、防止子类继承
2、确保实例对象只创建一个
3、防止单例对象被提前释放掉
@implementation AcountManagerCenter
static AcountManagerCenter *center = nil; + (instancetype)sharedManagerCenter { static dispatch_once_t once; dispatch_once(&once, ^{ center = (AcountManagerCenter *)@"AcountManagerCenter"; center = [[AcountManagerCenter alloc]init]; }); //防止子类使用 NSString *classString = NSStringFromClass([self class]); if ([classString isEqualToString:@"AcountManagerCenter"] == NO) { NSParameterAssert(nil); } return center; } - (instancetype)init { NSString *string = (NSString *)center; //确保实例对象只创建一个 if ([string isKindOfClass:[NSString class]]&&[string isEqualToString:@"AcountManagerCenter"]) { self = [super init]; if (self) { } return self; }else{ return nil; } }
六、实际应用-为登录用户实现账号本地存储
1、创建一个账号管理中心的一个单例
.h 文件 #import <Foundation/Foundation.h> @interface AcountManagerCenter : NSObject +(AcountManagerCenter *)sharedManagerCenter; //存储数据 -(void)setAcountValue:(id)value withKey:(NSString *)key; //读取数据 -(id)valueWithKey:(NSString *)key; @end .m 文件 #import "AcountManagerCenter.h" #import "FastCoder.h" @implementation AcountManagerCenter static AcountManagerCenter *center = nil; + (instancetype)sharedManagerCenter { static dispatch_once_t once; dispatch_once(&once, ^{ center = (AcountManagerCenter *)@"AcountManagerCenter"; center = [[AcountManagerCenter alloc]init]; }); //防止子类使用 NSString *classString = NSStringFromClass([self class]); if ([classString isEqualToString:@"AcountManagerCenter"] == NO) { NSParameterAssert(nil); } return center; } - (instancetype)init { NSString *string = (NSString *)center; //确保实例对象只创建一个 if ([string isKindOfClass:[NSString class]]&&[string isEqualToString:@"AcountManagerCenter"]) { self = [super init]; if (self) { } return self; }else{ return nil; } } -(void)setAcountValue:(id)value withKey:(NSString *)key { NSParameterAssert(value); NSParameterAssert(key); NSData *data = [FastCoder dataWithRootObject:value]; if (data) { [[NSUserDefaults standardUserDefaults] setObject:data forKey:key]; } } -(id)valueWithKey:(NSString *)key { NSParameterAssert(key); NSData *data = [[NSUserDefaults standardUserDefaults] valueForKey:key]; return [FastCoder objectWithData:data]; }
2、封装上层接口(为NSObject拓展一个分类)
分类.h文件 #import <Foundation/Foundation.h> @interface NSObject (AcountManagerCenter) -(void)saveValueForKey:(NSString *)key; +(id)valueByKey:(NSString *)key; @end 分类.m文件 #import "NSObject+AcountManagerCenter.h" #import "AcountManagerCenter.h" @implementation NSObject (AcountManagerCenter) -(void)saveValueForKey:(NSString *)key { [[AcountManagerCenter sharedManagerCenter] setAcountValue:self withKey:key]; } +(id)valueByKey:(NSString *)key { return [[AcountManagerCenter sharedManagerCenter] valueWithKey:key]; } @end
3、为客户端实现存储账号
#import "ViewController.h" #import "CTAcount.h" #import "NSObject+AcountManagerCenter.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; CTAcount *acount = [[CTAcount alloc]init]; acount.name = @"chen"; acount.age = @(20); acount.acount = @"admin"; acount.password = @"123456"; [acount saveValueForKey:@"chen"]; CTAcount *acount1 = [CTAcount valueByKey:@"chen"]; NSLog(@"%@,%@",acount1.age,acount1.acount); } @end
七、推荐第三方-FastCoding
这是一个快速、灵活存储二进制文件格式的第三方,它可以不用手动的实现NSCoding协议就可以将自定义类型对象转化为NSData类型数据,它可以代替NSCoding,属性列表和JSON。
登录GitHub,在搜索栏中搜索FastCoding,然后下载下来将FastCoder.h 和 FastCoder.m添加到自己的工程中。