什么是设计模式?
设计模式是对读者经常遇到的设计问题的可再现的解决方案。
设计模式只是编写更好的面向对象程序的一种方法
简单理解就是针对某种问题所设计出来的更好的方法
常用的设计模式有哪些呢?
单例模式,观察者模式,代理模式,工厂模式……
详 情:http://blog.youkuaiyun.com/liwei3gjob/article/details/8926862
一、单例模式
什么是单例呢?
单例是生命周期与程序生命周期相同,仅能生成一次、且不能被销毁的唯一实例;
通俗来讲就是一个类始终只有一个实例,不管如果copy还是retain还是alloc等等,都只有一个实例,且这个实例一直存在,不能被销毁。
什么情况下使用单例?
需要确保应用中的一个特定类有且仅有一个实例(对象)
一旦单例存在,单例可在程序任何位置被访问,且一直存在。
比如:我们在做登陆的时候,通常我们会创建一个单例,来看我们这个程序是否登陆的状态,(Bool islogin),因为这个islogin在整个程序都是不断在变的,且唯一,所以我们将其作为一个单例,是非常合适的。
设计单利类的目的,限制这个类只能创建一个对象
单例设计模式的要点:
(1) 某个类只能有一个实例。
(2)他必须自行创建这个对象
(3)必须自行向整个系统提供这个实例;
(4)这个方法必须是一个静态类
单例必须满足以下几个点:
1.只初始化一次
2.全局存在
3.唯一,对象不可改
4.方便获取,任何位置可访问
接下来我们来创建单例。
2.单例的创建步骤
1.首先创建一个UserCenter类
2.在.h文件中:
@interface UserCenter : NSObject
//单例:以default,share,standard,getinstance开头
+ (UserCenter*)standardUserCenter;
//创建返回一个单例实例化对象,只分配一次内存,无法销毁,可以在任何地方使用
//保存的信息
@property (nonatomic, copy) NSString *name;
@end
.m
//声明一个全局单例对象
static UserCenter *instance = nil;//不能让外部访问,同时放在静态块中的
+ (UserCenter *)standardUserCenter {
if (!instance) { //保证第一次创建成功后不再新建对象
instance = [[UserCenter alloc] init];
}
return instance;
}
3.现在我们在main.m中去做实验,
我们还需要一些准备工作,创建一个Student类,在Student.m文件中,导入#import "UserCenter.h",然后重写父类init方法
- (instancetype)init {
self = [super init];
if (self) {
//获取用户信息
NSString *name = [UserCenter standardUserCenter].name;
NSLog(@"%@", name);
}
return self;
}
main.m
UserCenter *userCenter = [UserCenter standardUserCenter];
[userCenter setName:@"yaya"];
Student *student = [[Student alloc] init];
//打印信息:yaya
我们发现不同的对象通过单例拿到的都是同一个对象,所以,usercenter的name付的值,和student打印的值是一样的。
//当然如果你还没弄明白,你可以在main.m文件中
UserCenter *userCenter1 = [UserCenter standardUserCenter];
UserCenter *userCenter2 = [UserCenter standardUserCenter];
NSLog(@"%@%@", userCenter1,userCenter2);
// 你会发现他们的地址是一样的,所以他们其实打印是同一个对象
4.上面哪一种单例写法算简单实现,其实他还有一种最完整的写法,接下来我们看看完整单例的写法
//完整单例的实现
//声明一个全局单例对象
static UserCenter *instance = nil;
+ (UserCenter *)standardUserCenter {
if (!instance) { //保证第一次创建成功后不再新建对象
instance = [[UserCenter alloc] init];
}
return instance;
}
//重写alloc方法
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
if (!instance) { //保证第一次能够创建一个对象
return [super allocWithZone:zone];
}
return nil;
}
//release,
- (oneway void)release{
//将引用计数-1,但是我不让你-,万一引用计数为1,1-1= 0,内存就销毁了,但是我这块内存是不销毁,所以我就什么都不做,oneway,单项操作,没什么特别的
}
//autorelese,
- (instancetype)autorelease{
return self;//不让你-
}
//copy,
- (id)copyWithZone:(NSZone *)zone{
//不让你去开辟空间
return self;
}
//retain
- (instancetype)retain{
return self;
}
//retainCount
- (NSUInteger)retainCount{
return NSUIntegerMax;//无穷大-1,打印常量字符串,就不会去-1
}
二、观察者模式
1.了解什么是观察者模式
在面对对象编程时,我们很多情况下直接处理某些事件会很麻烦。如常见的程序运行中电话呼入、用户突然点击home键等等,一般我们使用观察者(代理)来处理类似事件;
在使用这些模式的时候,我们需要告诉这些角色,谁来响应这些事件,这就需要使用类的引用;
类的引用本质上就是如何让另外一个角色,“知道”另外一个角色,对应到代码中,就是如何让某一个类的对象,顺利的把信息传递给另外一个对象;
处理类的引用的时候,应注意以下几点:
每个类是否能正常接收到信息(响应动作)
信息传递时接收者是否存在
每个类是否能正常释放(不会引起循环引用)
2.观察者模式
观察者模式:kvo(设计模式,观察者模式的一种,kvo是基于kvc的一种模式,所以我们先了解kvc,kvc(key-valule-coding键-值-编码)),notification
KVC(key - value - code)键值编码
KVC,即是指 NSKeyValueCoding,一个非正式的 Protocol,提供一种机制来间接访问对象的属性。KVO 就是基于 KVC 实现的关键技术之一。
它主要用于更改对象状态的方法。
它的概念:
通过字符串描述来更改对象状态,这种方式称为键值编码(KVC)
它的存取方法:
属性或直接设置实例变量来直接更改对象状态
键值编码中基本调用包括两个方法:
-valueForKey:
-setValue:forKey:
接下来我们在代码示
//kvc
// KVC设置值时,如果属性有set方法,则优先调用set方法,如果没有则直接设置上去,get方法一样
Person *per = [[Person alloc] init];
//第一个参数是value,第二个参数是key(就是类的属性名称)
[per setValue:@"yayasang" forKey:@"name"];
Dog *dog = [[Dog alloc] init];
[per setValue:dog forKey:@"dog"];
//取值
NSString *name = [per valueForKey:@"name"];
NSLog(@"%@",name);
KVO 键值观察,建立在KVC基础上的
Key-Value Observing (KVO) 建立在 KVC 之上,它能够观察一个对象的 KVC key path 值的变化。
利用键值观察可以注册称为一个对象的观察者,在该对象的某个属性变化时收到通知。
被观察对象需要编写复合KVC标准的存取方法
举个例子,用代码观察一个 Dog 对象的 dogName属性 变化,以下是实现的三个方法(三步骤):
//第一步:注册观察者
//涉及到的几个参数:
//Observer 观察者是谁
//KeyPath监听的属性,比如监听学生的name属性
//options :监听的内容
//NSKeyValueObservingOptionNew,
//NSKeyValueObservingOptionOld这两个参数的意思是监听它的新值和旧值
//第四个参数,上下文(直接为nil)
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
//第二步: 响应事件
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary *)change context:(nullable void *)context;
//第三步:移除观察者
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
首先我们创建一个Dog类。
Dog.h
@interface Dog : NSObject
@property (nonatomic, strong)NSString *dogName;
@end
Dog.m
@implementation Dog
- (instancetype)init
{
self = [super init];
if (self) {
//第一步:注册观察者
[self addObserver:self forKeyPath:@"dogName" options: NSKeyValueObservingOptionNew |
NSKeyValueObservingOptionOld context:nil];
}
return self;
}
//第二步
//响应事件
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
NSLog(@"狗跑了");
NSLog(@"change = %@",change);
/*change = {
kind = 1;
new = kimi;
old = zhangsan;
*/
}
- (void)dealloc
{
NSLog(@"dog dealloc");
//第三步:移除观察者
[self removeObserver:self forKeyPath:@"dogName"];
[_dogName release];
[super dealloc];
}
@end
这就是 KVO 的作用,它通过 key path 观察对象的值,当值发生变化的时候会收到通知。
3.数据持久化
iOS应用程序使用沙盒机制作为文件存储方式;
应用程序有一个独立的沙盒文件夹,程序仅能访问自己沙盒路径下的文件,任何情况下都无法访问其它程序的沙盒;
那我们可以通过什么方法将数据存入沙盒,同时也可以取出------NSUserDefaluts
下面我们来看一个例子:
main.m
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
//存数据
[userDefaults setObject:@"tom" forKey:@"name"];
//取数据
NSString *name2 = [userDefaults objectForKey:@"name"];
NSLog(@"name2 = %@",name2);
//删除数据
[userDefaults removeObjectForKey:@"name"];
//更新数据同步文件
[userDefaults synchronize];
可能有同学问,那如果在不同的类里,我们也可以这样存,然后将我们存的数据取出来吗?—-of course yes
我们来创建一个Person类和Dog类
Person.h
@interface Person : NSObject
- (void)saveDate;
@end
Person.m
- (void)saveDate{
NSUserDefaults *userDefaluts = [NSUserDefaults standardUserDefaults];
[userDefaluts setObject:self.name forKey:@"name"];
[userDefaluts synchronize];
}
Dog.h
@interface Dog : NSObject
- (void)loadData;
@end
Dog.m
- (void)loadData{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSString *string = [userDefaults objectForKey:@"name"];
NSLog(@"string = %@",string);
[userDefaults synchronize];
}
main.m
Person *person6 = [[Person alloc] init];
[person6 saveDate];
Dog *dog1 = [[Dog alloc] init];
[dog1 loadData];
这样我们就可以达到在Person类中存,在Dog类中取,取出来的就是在Person中存的名字
4.读写文件
我们可以自己通过代码,根据一条路径自动在桌面上创建一个文件,来存取我们的数据,这个数据不是存在沙盒中的哦。
这里有个小技巧:如果你不知道你的桌面路径,可以通过在桌面上建一个文件夹,然后直接安装左键将它拖动到xcode里面,他会自动生成你的桌面路径,然后再稍微修改一下文件名就ok.
NSArray *arr = [NSArray arrayWithObjects:@"xiaomi", @"dami", nil];
//将数据存入文件
[arr writeToFile:@"/Users/gaoya/Desktop/abc.txt" atomically:YES];//把arr保存在相应路径下
//从文件里面将数据读出
NSArray *arr1 = [NSArray arrayWithContentsOfFile:@"/Users/gaoya/Desktop/abc.txt"];
NSLog(@"%@", arr1);