iOS有哪些常见的设计模式?
单例模式/委托模式/观察者模式/MVC模式
单例模式
单例保证了应用程序的生命周期内仅有一个该类的实例对象,而且易于外界访问.
在ios sdk中,UIApplication, NSBundle, NSNotificationCenter, NSFileManager, NSUserDefault, NSURLCache等都是单例.
在实际开发中,单例一般会分为ARC和非ARC两种不同的写法,这些代码可以通过判断统一整理到PCH文件中,这样每次使用单例时就不用多次输入重复的代码.
单例的代码简单样例:
//.h @interface Singleton:NSObject +(Singleton *)sharedManager; @property(nonatomic,strong)NSString *singletonData; @end
//.m #import "Singleton.h" @implementation Singleton static Singleton *sharedManager = nil; +(Singleton *)sharedManager { static dispatch_once_t once; dispatch_once(&once,^{ sharedManager = [[self alloc] init]; }); return sharedManager; } @end
委托模式
委托Delegate是协议的一种,通过@protocol方式实现. 顾名思义, 就是委托他人帮自己去做什么事. 也就是当自己不方便做什么事情的时候, 就可以建立一个委托, 这样就可以委托他人帮忙去做.
比如常见的UITableView, iOS SDK在写这个类的时候,肯定不知道开发者想要在点击某一行之后都要进行什么操作, 所以只好让程序员委托UIViewController去代理UITableViewDelegate来实现这个方法.
同样的,它也不知道开发者希望这个表有多少行,每个cell的内容都是什么样的,所以UITableView只好委托UIViewController去代理他的UITableViewDataSource来实现这些方法.
当然,代理方法一是可以传递事件, 二也可以用来传递值,
比如:tableview的datasource里的一个代理方法
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
委托者可以把行数通过indexpath传递给被委托者,被委托者取到行数之后,自定义如何表现这个cell,再把cell传回给委托者.
下面有用一个Man类的例子来详细解说一下.
Man类,可以初始化一个胖子或瘦子,然后完成他的早中晚的一天......然后吃饭工作睡觉之后都干什么他自己不知道.....只好告诉你他吃了什么,睡了多久 ,让你来帮他决定.....我瞎编的...你也许可以编一个好一点的....
//man.h #import //*************** delegate *********************** //创建代理.吃喝工作的结果让别人帮你完成,你只需要提供吃了啥,睡了多久就行了. @protocol ManDelegate @required //人可以吃不睡,但是一定要工作啊....所以required -(void)work; @optional //返回一个睡觉的时间给被委托的对象,通过这个时间来处理相关事宜 -(void)sleepWithTime:(int)time; //通过给被委托者一种食物,返回一个布尔值,来判断是否吃饱. -(BOOL)eatWithFood:(NSString *)food; @end //*************** delegate *********************** @interface Man : NSObject //delegate属性 @property (nonatomic, weak) id delegate; //是胖,是瘦呢,这个在实例化类的时候完成. @property (nonatomic, assign) BOOL isFat; //你这一天都分别干了啥,这个要你自己完成 -(void)morning; -(void)afternoon; -(void)everning; @end
//man.m #import "Man.h" @implementation Man //通过[self.delegate XXXX]确定代理出去的方法在自己的方法里的实现的位置; //早上睡了7个小时.吃了点面包,没吃饱就不工作,吃饱了就接着工作.. -(void)morning{ [self.delegate sleepWithTime:7]; BOOL isFull = [self.delegate eatWithFood:@"面包"]; //觉得面包吃饱了就工作,面包吃不饱就不工作. if(isFull == YES){ NSLog(@"早饭吃饱了,工作"); [self.delegate work]; } else{ NSLog(@"早饭吃饱了,不工作"); } } //中午睡了1个小时,睡完接着工作. -(void)afternoon{ [self.delegate sleepWithTime:1]; [self.delegate work]; } //晚上睡了3个小时... -(void)everning{ [self.delegate sleepWithTime:3]; } @end
//代理执行的位置 #import "ViewController.h" #import "Man.h" @interface ViewController ()@property (nonatomic,retain) Man *zhangsan; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //初始化一个张三实例,张三不胖. self.zhangsan = [[Man alloc] init]; self.zhangsan.isFat = NO; //把张三工作/吃/睡之后要干嘛,交给self也就是ViewController来决定,就是实现代理方法 self.zhangsan.delegate = self; //张三的一天. [self.zhangsan morning]; [self.zhangsan afternoon]; [self.zhangsan everning]; } #pragma mark ----ManDelegate //viewcontroller决定,让张三工作的时候打印一句话到控制台. -(void)work{ NSLog(@"Working"); } //viewcontroller决定,根据张三提供来的休息时间,把他的休息情况打印到控制台上. -(void)sleepWithTime:(int)time { NSLog(@"Slept for %d hour",time); if(time>4){ NSLog(@"长眠不起"); }else if(time == 4){ NSLog(@"不长不短"); }else if(time < 4){ NSLog(@"小眯一下"); } } //viewcontroller决定,通过张三提供吃的食物的内容,来判断张三是不是吃饱了...并且返回给张三...让他自己通过判断自己饱了没饱来处理一些事情 -(BOOL)eatWithFood:(NSString *)food{ if (self.zhangsan.isFat == YES && [food isEqualToString:@"面包"]) { return NO; } else if(self.zhangsan.isFat == NO && [food isEqualToString:@"面包"]){ return YES; } else{ return YES; } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
问题1:为什么delegate作为属性需要是weak?
防止retain cycle循环引用...
比如tableview....UIViewCotroller通过self.view addsubview.已经指向tableview了......所以tableview.delegate = self 和tableview.datasource又指向了viewcontroller,如果此时delegate是strong,就会发生循环引用无法释放,所以这里应该是weak.....详细见上一章属详解.
观察者模式
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。 简而言之,就是A和B,A对B的变化感兴趣,就注册为B的观察者,当B发生变化时通知A,告知B发生了变化。这个也叫做经典观察者模式。
在iOS中,观察者模式的具体实现有两种: 通知机制(notification)和KVO机制(Key-value Observing)
1.通知机制
注册通知接收者的代码:
[[NSNotificationCenter DefaultCenter] addObserver:self selector:@selector(registerCompeletion:) name:@"RigsterCompletionNotification" object:nil]; -(void)registerCompletion:(NSNotification *)notification{ NSDictionary *theData = [notification useInfo]; NSString *username = [theData objectForKey:@"username"]; NSLog(@"username = %@",username); }
投送通知的代码
NSDictionary *dataDic = [NSDictionary dictionaryWithObject:self.text forkey:@"username"]; [[NSNotificationCenter DefaultCenter] postNotificationName:@"RegisterCompeletionNotification" object:nil userInfo:dataDict];
问题1. object是干嘛的?是不是可以用来传值?
答:object是用来过滤Notification的,只接收指定的sender所发的Notification.....传值请用userInfo,而不是object
问题2. iOS中,广播通知(broadcast notification)/本地通知(local notification)/推送通知(push notification)有什么区别和不同?
出了名字相似以外,广播通知和其他两个通知是完全不一样的: 广播通知是cocoatouch中观察者模式的一种机制, 实现应用内部多个对象的通信...本地通知和推送通知中的"通知"是一种"提示"...通过警告框,发出声音,振动和在应用图标上显示数组等,在计划时间到达时,本地通知通过本地iOS发出,推送通知由第三方程序发送给苹果远程服务器,再由远程服务器推送给iOS特定应用.
2.KVO机制
对象的属性变化时,通知会直接发送到观察者对象.
可以用来实现 ,比如, UITableView滑动时导航自动变换颜色,或者,一个计算房贷的软件,当首付金额改变时,每月还款数目等数据相应自动改变等.
这里来观察UITableView一个实例的contentOffset属性.
//添加监听者 [self.tableView addObserver:self //监听者 forKeyPath:@"contentOffset" //被观察对象的属性字符串 options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld//设置.这里的设置表示把属性变换的新旧两个值都传递给观察者 context:nil];//上下文内容,c语言形式的任何指针类型
//监听属性变化时的回调 -(void)obserValueForKeyPath:(NSString *)keyPath //被观察的属性 ofObject:(id)object //被观察的对象 change:(NSDictionary *)change//字典类型包含了变化的内容,与options呼应 context:(void *)context//传递过来的上下文 { NSLog(@"%@-%@",keyPah, (NSString *)change[NSKeyValueChangeNewKey]);//通过change字典渠道变化的属性变化前或变化后的值. }