題外話:提及設計模式,感受本身把握不了筆頭,因此單拿出iOS開發中的幾種經常使用設計模式談一下java
概念:整個應用或系統只能有該類的一個實例設計模式
在iOS開發咱們常常碰到只須要某類一個實例的狀況,最多見的莫過於對硬件參數的訪問類,好比UIAccelerometer.這個類能夠幫助咱們得到硬件在各個方向軸上的加速度,可是咱們僅僅須要它的一個實例就夠了,再多,只會浪費內存。架構
因此蘋果提供了一個UIAccelerometer的實例化方法+sharedAccelerometer,從名字上咱們也能看出此方法讓整個應用共享一個UIAccelerometer實例(PS:iOS 的開放中,咱們每每能從方法名中就瞭解這個方法的做用),它內部的如何實現咱們暫且不談,先來看看還有哪些類一樣使用了單例模式。app
等等,蘋果的SDK中大量的遵循此設計模式,那麼它的內部是如何實現的呢?框架
首先給你們介紹一下GCD技術,是蘋果針對於多核CPU的多任務解決方案。你不須要了解更多,只須要知道是一組基於C語言開發的API(詳細內容能夠看一下唐巧前輩的這篇博文:http://blog.devtang.com/blog/2012/02/22/use-gcd/ )。GCD提供了一個dispatch_once函數,這個函數的做用就是保證block(代碼塊:暫時理解爲一個跟函數相近的東西,具體能夠參照這篇文章:http://blog.csdn.net/enuola/article/details/8674063 )裏的語句在整個應用的生命週期裏只執行一次。ide
OK,接下來就給出一個使用了單例模式新建和獲取實例的類模版,代碼以下:函數
//Singleton.h @interface Singleton : NSObject + (Singleton *)sharedSingleton; <1> @end /***************************************************************/ //Singleton.m #import "Singleton.h" @implementation Singleton static Singleton *sharedSingleton = nil;<2> + (Singleton *)sharedSingleton{ static dispatch_once_t once;<3> dispatch_once(&once,^{ sharedSingleton = [[self alloc] init];<4> //dosometing }); return sharedSingleton;<5> }
上述代碼中有5小步,解釋以下:post
OK,這就是iOS開發中單例模式的機制,下面咱們就看看如何在實際開發中使用此模式?(PS:爲了儘量的突出核心內容,咱們會對設計中的其餘模式或內容一筆帶過)ui
假如咱們須要在iOS應用中實現分層的架構設計,即咱們須要把數據的持久層,展現層,和邏輯層分開。爲了突出重點,咱們直接把目光轉到持久層,而根據MVC的設計模式,咱們又能夠把持久層細分爲DAO層(放置訪問數據對象的四類方法)和Domain層(各類實體類,好比學生),這樣就可使用DAO層中的方法,配合實體類Domain層對數據進行清晰的增刪改查。那麼咱們如何設計呢?atom
從使用者的角度看,咱們指望得到DAO層的類實例,而後調用它的增刪改查四大方法。但是這個類實例,咱們彷佛只須要一個就足夠了,再多的話不利於管理且浪費內存。OK,咱們可使用單例模式了,代碼以下:
.h文件:
//StudentDAO.h @interface StudentDAO:NSObject @property (nonatomic,strong) NSMutaleArray *StudentsInfo; + (StudentDAO *)sharedStudentDAO; -(int) create:(Student*)student; -(int) remove:(Student*)student; -(int) modify:(Student*)student; -(NSMutaleArray) findAll; @end
.m文件:
//StudentDAO.m #import "StudentDAO.h" #import "Student.h" @implementation StudentDAO static StudentDAO *studentDao = nil; + (StudentDAO)sharedStudentDAO{ static dispatch_once_t once; dispatch_once(&once,^{ Student *student1 = [[Student alloc]init]; student1.name = "MexiQQ"; student1.studentNum = "201200301101"; Student *student2 = [[Student alloc]init]; student2.name = "Ricardo_LI"; student2.studentNum = "201200301102"; studentDao = [[self alloc] init]; studentDao._StudentsInfo = [[NSMutaleArray alloc]init]; [studentDao._StudentsInfo addObject:student1]; [studentDao._StudentsInfo addObject:student2]; }); return studentDao; } //插入的方法 -(int)create:(Student*)stu{ [self._StudentsInfo addObject:stu]; return 0; } //刪除的方法 -(int)remove:(Student*)stu{ for(Student* s in self._StudentsInfo){ if([stu.studentNum isEqual:s.studentNum]){ [self._StudentsInfo removeObject:s] break; } } } -(int)modify...... //省略不寫 -(NSMutaleArray)findAll...... //省略不寫
上述例子不難理解,其中用到的Student類我這裏就不給出了,只是一個含有姓名和學號屬性的實體類。
概念:一個對象狀態改變,通知正在對他進行觀察的對象,這些對象根據各自要求作出相應的改變
圖例:
如圖所示:操做對象向被觀察者對象投送消息,使得被觀察者的狀態得以改變,在此以前已經有觀察者向被觀察對象註冊,訂閱它的廣播,如今被觀察對象將本身狀態發生改變的消息廣播出來,觀察者接收到消息各自作出應變。
OK,咱們先來看看在蘋果的Cocoa Touch框架中有誰使用了觀察者模式:
原理圖以下:
如圖所示,在通知機制中對某個通知感興趣的全部對象均可以成爲接受者。首先,這些對象須要向通知中心(NSNotificationCenter)發出addObserver:selector:name:object:消息進行註冊,在投送對象投送通知送給通知中心時,通知中心就會把通知廣播給註冊過的接受者。全部的接受者不知道通知是誰投送的,不去關心它的細節。投送對象和接受者是一對多的關係。接受者若是對通知再也不關注,會給通知中心發送removeObserver:name:Object:消息解除註冊,之後再也不接受通知。
(ps:這段話內容摘抄自關東昇先生的文章)
OK,咱們試着去使用一下通知機制:
新建一個Single view Project,對項目中的文件作如下修改:
AppDelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application { [[NSNotificationCenter defaultCenter]postNotificationName:@"APPTerminate" object:self]; }
ViewController.m
// // ViewController.m // TestNotification // // Created by liwenqian on 14-10-18. // Copyright (c) 2014年 liwenqian. All rights reserved. // #import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //注意此處的selector有參數,要加冒號 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(doSomething:) name:@"APPTerminate" object:nil]; // Do any additional setup after loading the view, typically from a nib. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; [[NSNotificationCenter defaultCenter]removeObserver:self]; // Dispose of any resources that can be recreated. } #pragma mark -處理通知 -(void)doSomething:(NSNotification*)notification{ NSLog(@"收到通知"); } @end
如上所示,對模版項目的兩個文件的方法或整個文件作出修改,Command+R運行你的APP,再按下Home鍵(Command+H),會發現打印出一行收到通知的文字,以下:
在APP退到後臺時,發出廣播,而viewController由於時觀察者,收到廣播,執行doSomething方法,打印出收到廣播的文字。
原理圖以下:
如圖所示:
該機制下觀察者的註冊是在被觀察者的內部進行的,不一樣於通知機制(由觀察者本身註冊),須要被觀察者和觀察者同時實現一個協議:NSKeyValueObserving,被觀察者經過addObserver:forKeypath:options:context方法註冊觀察者,以及要被觀察的屬性。
新建一個single view project,同時新建一個繼承自NSObject的TestWatche類:文件結構以下圖:
對文件進行以下修改:
AppDelegate.h
// // AppDelegate.h // TestNotification // // Created by liwenqian on 14-10-18. // Copyright (c) 2014年 liwenqian. All rights reserved. // #import <UIKit/UIKit.h> #import <CoreData/CoreData.h> #import "TestWatche.h" @interface AppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) UIWindow *window; @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext; @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel; @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator; @property (strong,nonatomic) NSString *state; @property (strong,nonatomic) TestWatche *watcher; - (void)saveContext; - (NSURL *)applicationDocumentsDirectory; @end
AppDelegate.m 對以下方法作出修改
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. self.watcher = [TestWatche alloc]; [self addObserver:self.watcher forKeyPath:@"state" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:@"pass content"]; self.state = @"launch"; return YES; } - (void)applicationDidEnterBackground:(UIApplication *)application { self.state = @"backgroud"; }
TestWatche.m(因爲繼承自NSObject,而NSObject已實現了NSKeyValueObserving協議,因此不須要作聲明)
// // TestWatche.m // TestNotification // // Created by liwenqian on 14-10-18. // Copyright (c) 2014年 liwenqian. All rights reserved. // #import "TestWatche.h" @implementation TestWatche -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ NSLog(@"state change:%@",change); } @end
OK,Command+B Command+R Command+H看看你的應用輸出了什麼,若是你的操做無誤的話,會顯示以下內容:
我的認爲委託模式大多數人解釋的複雜了,其實就像是java中的接口,類能夠實現或不實現協議(接口)中的方法。經過此種方式,達到最大的解耦目的,方便項目的擴展。不過你須要設置應用的委託對象,以肯定協議中的方法爲誰服務。
拿最經常使用的UITableViewDelegate UITableViewDataSource來舉例:
實現一個頁面有一個UItableView,UItableView的每一欄(cell)的數據由咱們指定,那麼咱們該如何作呢?蘋果也天然想到了這一點,因而定義了一個接口,這個接口有許多的方法,只須要咱們把要服務的對象傳進去,就可使用這些方法了,這個接口就是委託和協議。而UITableViewDelegate 和 UITableViewDataSource 就是專爲UITableView而寫的委託和協議。用法以下:
ViewController.h
// // ViewController.h // RecipeBookMe // // Created by liwenqian on 14-9-10. // Copyright (c) 2014年 liwenqian. All rights reserved. // #import <UIKit/UIKit.h> @interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> @property (nonatomic, strong) IBOutlet UITableView *mytableView; @end
ViewController.m
// // ViewController.m // RecipeBookMe // // Created by liwenqian on 14-9-10. // Copyright (c) 2014年 liwenqian. All rights reserved. // #import "ViewController.h" #import "DetailViewController.h" #import "Recipe.h" #import "RecipeTableCellTableViewCell.h" @interface ViewController () @end @implementation ViewController{ NSArray *recipes; } @synthesize mytableView; - (void)viewDidLoad { [super viewDidLoad]; // Initialize the recipes array Recipe *recipe1 = [Recipe new]; recipe1.name = @"Egg Benedict"; recipe1.prepTime = @"30 min"; recipe1.image = @"egg_benedict.jpg"; recipe1.ingredients = [NSArray arrayWithObjects:@"2 fresh English muffins", @"4 eggs", @"4 rashers of back bacon", @"2 egg yolks", @"1 tbsp of lemon juice", @"125 g of butter", @"salt and pepper", nil]; Recipe *recipe2 = [Recipe new]; recipe2.name = @"Mushroom Risotto"; recipe2.prepTime = @"30 min"; recipe2.image = @"mushroom_risotto.jpg"; recipe2.ingredients = [NSArray arrayWithObjects:@"1 tbsp dried porcini mushrooms", @"2 tbsp olive oil", @"1 onion, chopped", @"2 garlic cloves", @"350g/12oz arborio rice", @"1.2 litres/2 pints hot vegetable stock", @"salt and pepper", @"25g/1oz butter", nil]; recipes = [NSArray arrayWithObjects:recipe1, recipe2, nil]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } /*--------------------------------------------------------------------*/ //定義UITableview的欄目數量 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [recipes count]; } //定義UITableviewCell的顯示內容 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CoustomerTableIdentifier = @"RecipeTableCellTableViewCell"; RecipeTableCellTableViewCell *cell =(RecipeTableCellTableViewCell *) [tableView dequeueReusableCellWithIdentifier:CoustomerTableIdentifier]; if (cell == nil) { cell = [[RecipeTableCellTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CoustomerTableIdentifier]; } recipe = [recipes objectAtIndex:indexPath.row]; cell.nameLabel.text = recipe.name; cell.prepTimeLabel.text= recipe.prepTime; cell.thumbnailImageView.image = [UIImage imageNamed:recipe.image]; return cell; } //點擊每一欄執行跳轉時的處理 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.identifier isEqualToString:@"showRecipeDetail"]) { NSIndexPath *indexPath = nil; Recipe *recipe = nil; indexPath = [self.mytableView indexPathForSelectedRow]; recipe = [recipes objectAtIndex:indexPath.row]; DetailViewController *destViewController = segue.destinationViewController; destViewController.recipe = [recipes objectAtIndex:indexPath.row]; } } //定義cell的高度 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 71; } /*--------------------------------------------------------------------*/ @end
如上所示,兩條/------/註釋間的方法所有來自於委託和協議。利用委託和協議,你能夠把主要精力放到邏輯業務上,將數據綁定和事件處理交給委託和協議去完成。