iOS有哪些常見的設(shè)計模式?
單例模式/委托模式/觀察者模式/MVC模式
單例模式
單例保證了應(yīng)用程序的生命周期內(nèi)僅有一個該類的實例對象,而且易于外界訪問.
在ios sdk中,UIApplication, NSBundle, NSNotificationCenter, NSFileManager, NSUserDefault, NSURLCache等都是單例.
在實際開發(fā)中,單例一般會分為ARC和非ARC兩種不同的寫法,這些代碼可以通過判斷統(tǒng)一整理到PCH文件中,這樣每次使用單例時就不用多次輸入重復(fù)的代碼.
單例的代碼簡單樣例:
//.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是協(xié)議的一種,通過@protocol方式實現(xiàn). 顧名思義, 就是委托他人幫自己去做什么事. 也就是當自己不方便做什么事情的時候, 就可以建立一個委托, 這樣就可以委托他人幫忙去做.
比如常見的UITableView, iOS SDK在寫這個類的時候,肯定不知道開發(fā)者想要在點擊某一行之后都要進行什么操作, 所以只好讓程序員委托UIViewController去代理UITableViewDelegate來實現(xiàn)這個方法.
同樣的,它也不知道開發(fā)者希望這個表有多少行,每個cell的內(nèi)容都是什么樣的,所以UITableView只好委托UIViewController去代理他的UITableViewDataSource來實現(xiàn)這些方法.
當然,代理方法一是可以傳遞事件, 二也可以用來傳遞值,
比如:tableview的datasource里的一個代理方法
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
委托者可以把行數(shù)通過indexpath傳遞給被委托者,被委托者取到行數(shù)之后,自定義如何表現(xiàn)這個cell,再把cell傳回給委托者.
下面有用一個Man類的例子來詳細解說一下.
Man類,可以初始化一個胖子或瘦子,然后完成他的早中晚的一天......然后吃飯工作睡覺之后都干什么他自己不知道.....只好告訴你他吃了什么,睡了多久 ,讓你來幫他決定.....我瞎編的...你也許可以編一個好一點的....
//man.h
#import <Foundation/Foundation.h>
//*************** delegate ***********************
//創(chuàng)建代理.吃喝工作的結(jié)果讓別人幫你完成,你只需要提供吃了啥,睡了多久就行了.
@protocol ManDelegate <NSObject>
@required
//人可以吃不睡,但是一定要工作啊....所以required
-(void)work;
@optional
//返回一個睡覺的時間給被委托的對象,通過這個時間來處理相關(guān)事宜
-(void)sleepWithTime:(int)time;
//通過給被委托者一種食物,返回一個布爾值,來判斷是否吃飽.
-(BOOL)eatWithFood:(NSString *)food;
@end
//*************** delegate ***********************
@interface Man : NSObject
//delegate屬性
@property (nonatomic, weak) id<ManDelegate> delegate;
//是胖,是瘦呢,這個在實例化類的時候完成.
@property (nonatomic, assign) BOOL isFat;
//你這一天都分別干了啥,這個要你自己完成
-(void)morning;
-(void)afternoon;
-(void)everning;
@end
//man.m
#import "Man.h"
@implementation Man
//通過[self.delegate XXXX]確定代理出去的方法在自己的方法里的實現(xiàn)的位置;
//早上睡了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
//代理執(zhí)行的位置
#import "ViewController.h"
#import "Man.h"
@interface ViewController ()<ManDelegate>
@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來決定,就是實現(xiàn)代理方法
self.zhangsan.delegate = self;
//張三的一天.
[self.zhangsan morning];
[self.zhangsan afternoon];
[self.zhangsan everning];
}
#pragma mark ----ManDelegate
//viewcontroller決定,讓張三工作的時候打印一句話到控制臺.
-(void)work{
NSLog(@"Working");
}
//viewcontroller決定,根據(jù)張三提供來的休息時間,把他的休息情況打印到控制臺上.
-(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決定,通過張三提供吃的食物的內(nèi)容,來判斷張三是不是吃飽了...并且返回給張三...讓他自己通過判斷自己飽了沒飽來處理一些事情
-(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循環(huán)引用...
比如tableview....UIViewCotroller通過self.view addsubview.已經(jīng)指向tableview了......所以tableview.delegate = self 和tableview.datasource又指向了viewcontroller,如果此時delegate是strong,就會發(fā)生循環(huán)引用無法釋放,所以這里應(yīng)該是weak.....詳細見上一章屬詳解.
觀察者模式
觀察者模式定義了一種一對多的依賴關(guān)系咧栗,讓多個觀察者對象同時監(jiān)聽某一個主題對象醉途。這個主題對象在狀態(tài)上發(fā)生變化時捐寥,會通知所有觀察者對象丧鸯,使它們能夠自動更新自己桑包。 簡而言之恭陡,就是A和B麦轰,A對B的變化感興趣河狐,就注冊為B的觀察者,當B發(fā)生變化時通知A垢村,告知B發(fā)生了變化割疾。這個也叫做經(jīng)典觀察者模式。
在iOS中,觀察者模式的具體實現(xiàn)有兩種: 通知機制(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所發(fā)的Notification.....傳值請用userInfo,而不是object
問題2. iOS中,廣播通知(broadcast notification)/本地通知(local notification)/推送通知(push notification)有什么區(qū)別和不同?
出了名字相似以外,廣播通知和其他兩個通知是完全不一樣的: 廣播通知是cocoatouch中觀察者模式的一種機制, 實現(xiàn)應(yīng)用內(nèi)部多個對象的通信...本地通知和推送通知中的"通知"是一種"提示"...通過警告框,發(fā)出聲音,振動和在應(yīng)用圖標上顯示數(shù)組等,在計劃時間到達時,本地通知通過本地iOS發(fā)出,推送通知由第三方程序發(fā)送給蘋果遠程服務(wù)器,再由遠程服務(wù)器推送給iOS特定應(yīng)用.
2.KVO機制
對象的屬性變化時,通知會直接發(fā)送到觀察者對象.
可以用來實現(xiàn) ,比如, UITableView滑動時導(dǎo)航自動變換顏色,或者,一個計算房貸的軟件,當首付金額改變時,每月還款數(shù)目等數(shù)據(jù)相應(yīng)自動改變等.
這里來觀察UITableView一個實例的contentOffset屬性.
//添加監(jiān)聽者
[self.tableView addObserver:self //監(jiān)聽者
forKeyPath:@"contentOffset" //被觀察對象的屬性字符串
options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld//設(shè)置.這里的設(shè)置表示把屬性變換的新舊兩個值都傳遞給觀察者
context:nil];//上下文內(nèi)容,c語言形式的任何指針類型
//監(jiān)聽屬性變化時的回調(diào)
-(void)obserValueForKeyPath:(NSString *)keyPath //被觀察的屬性
ofObject:(id)object //被觀察的對象
change:(NSDictionary *)change//字典類型包含了變化的內(nèi)容,與options呼應(yīng)
context:(void *)context//傳遞過來的上下文
{
NSLog(@"%@-%@",keyPah, (NSString *)change[NSKeyValueChangeNewKey]);//通過change字典渠道變化的屬性變化前或變化后的值.
}