1. 什麼是單例模式?php
在iOS應用的生命週期中,某個類只有一個實例。設計模式
2. 單例模式解決了什麼問題?api
想象一下,若是咱們要讀取文件配置信息,那麼每次要讀取,咱們就要建立一個文件實例,而後才能獲取到裏面的相關配置信息,這樣若是,咱們若是要屢次讀取這個文件的配置信息,那就要建立多個實例,這樣嚴重浪費了內存資源。而實際應用中,當咱們要用到的類多是要反覆用到的,通常能夠考慮使用單例模式。這樣能夠大大下降建立新實例帶來的內存浪費。數組
3. 單例模式的實現原理瀏覽器
通常會封裝一個靜態屬性,並提供靜態實例的建立方法(該方法使用GCD技術保證了整個程序生命週期只運行一次:用了dispath_once()函數)。網絡
4. 應用實例架構
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"www.baidu.com"]]; NSFileManager *FileManager = [NSFileManager defaultManager];
註解:APP單例應用的建立,並在瀏覽器中打開URL地址。第二則是獲取文件管理者,整個程序運行週期內,只有一個文件管理者,第一次建立後,之後要用到就直接用不會再建立了。app
1. 什麼是委託模式? 框架
2. 委託模式解決了什麼問題?ide
委託時爲了下降一個對象的複雜度和耦合度,使其主要框架類可以具備通用性,其餘旁枝末節的方法留給委託對象去實現。
3. 委託模式的實現原理
4. 應用實例
UITextFieldDelegate
#import "ViewController.h" @interface ViewController () <UITextFieldDelegate> @property (strong, nonatomic) UITextField *textField; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. self.textField.delegate = self; } @end
註解:控制器遵循協議<UITextFieldDelegate>,而且成爲了textField的代理。這樣控制器就擁有了協議中的方法,textField也就能讓代理爲本身作一些事。
1. 什麼是觀察者模式?
觀察者模式也叫發佈/訂閱模式。好比訂閱天氣預報,其中有以下三個角色:
第一步:手機用戶訂閱中國移動短信中心的天氣預報業務。
第二步:下雨時,氣象局發佈通告信息給中國移動短信中心:「有雨」。
第三步:手機用戶就會收到中國移動短信中心的信息:「有雨」,接着用戶就會知道應該採起什麼的動做:「出門帶傘」。
第四步:當不須要此項功能服務時,取消訂閱。
氣象局與用戶之間的通訊是匿名的,用戶只知道是中國移動發的短息,不知道氣象局的存在。
2. 觀察者模式解決了什麼問題?
3. 觀察者模式的實現原理
有4個角色:
4. 觀察者模式的應用
主要有兩種,通知機制和KVO機制。
氣象臺:
// observatory.h #import <Foundation/Foundation.h> @interface Observatory : NSObject @property (getter=isRain) BOOL isRain; @end
// observatory.m #import "Observatory.h" @implementation Observatory @end
手機用戶:
// phoneUser.h #import <Foundation/Foundation.h> @interface PhoneUser : NSObject - (void)takeUmbrella; @end
// phoneUser.m #import "PhoneUser.h" @implementation PhoneUser - (void)takeUmbrella { NSLog(@"Take Umbrella"); } @end
測試用例:
// main.m #import <Foundation/Foundation.h> #import "Observatory.h" #import "PhoneUser.h" static NSString * const weatherForcast = @"weatherForecast"; int main(int argc, const char * argv[]) { // 用戶訂閱天氣預報 PhoneUser *pUser = [[PhoneUser alloc] init]; [[NSNotificationCenter defaultCenter] addObserver:pUser selector:@selector(takeUmbrella) name:weatherForcast object:nil]; // 氣象臺發佈下雨天氣預報 Observatory *observatory = [[Observatory alloc] init]; observatory.isRain = YES; if (observatory.isRain) { [[NSNotificationCenter defaultCenter] postNotificationName:weatherForcast object:observatory userInfo:nil]; } // 取消訂閱 [[NSNotificationCenter defaultCenter] removeObserver:pUser name:weatherForcast object:observatory]; return 0; }
運行結果:
2016-10-25 03:12:36.860998 02-通知機制[5172:2936788] Take Umbrella Program ended with exit code: 0
註解:手機用戶向服務中心預訂天氣預報服務,當下雨時,氣象局就會發送通告給服務中心,服務中心收到通告就會發送短信給全部已訂閱的用戶,全部已訂閱用戶收到短信後則能夠做出相應的動做。
股票後臺數據:
// BackgroundData.h #import <Foundation/Foundation.h> @interface BackgroundData : NSObject /** 股價漲 */ @property (getter=isShareRise) BOOL isShareRise; @end
// BackgroundData.m #import "BackgroundData.h" @implementation BackgroundData @end
前臺展現股票數據:
// foregroundDisplay.h #import <Foundation/Foundation.h> @interface ForegroundDisplay : NSObject @end
// foregroundDisplay.m #import "ForegroundDisplay.h" @implementation ForegroundDisplay #pragma mark - 當被觀察者的屬性值發送變化時調用改方法 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { NSLog(@"The shares rise"); } @end
測試用例:
// main.m #import <Foundation/Foundation.h> #import "BackgroundData.h" #import "ForegroundDisplay.h" int main(int argc, const char * argv[]) { // 前臺展現股票跌漲 ForegroundDisplay *fgDisplay = [[ForegroundDisplay alloc] init]; // 後臺數據 BackgroundData *bgData = [[BackgroundData alloc] init]; bgData.isShareRise = NO; // 爲後臺數據註冊觀察者 [bgData addObserver:fgDisplay forKeyPath:@"isShareRise" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil]; // 後臺數據屬性值改變 bgData.isShareRise = YES; // 移除觀察者 [bgData removeObserver:fgDisplay forKeyPath:@"isShareRise" context:nil]; return 0; }
運行結果:
2016-10-25 02:51:35.931473 03-KVO機制[5059:2820557] The shares rise Program ended with exit code: 0
註解:註冊一個觀察者觀察後臺股票數據的變更,一旦有變更,則會觸發觀察者的特定方法,在改方法中能夠配置後臺數據與前臺展現同步。最後應當移除觀察者,若是沒有移除,則至關於後臺數據類銷燬了,但觀察者還保存着對它的引用,這就會奔潰。
1. 什麼是MVC模式
所謂MVC模式,即model,view,controller。模型、視圖、控制器,其中各自有其職能所在:
Controller能夠直接與Model和View進行通訊,而View不能和Controller直接通訊。View與Controller通訊須要利用代理協議的方式,當有數據更新時,Model也要與Controller進行通訊,這個時候就要用Notification和KVO,這個方式就像一個廣播同樣,Model發信號,Controller設置監聽接受信號,當有數據更新時就發信號給Controller,Model和View不能直接進行通訊,這樣會違背MVC設計模式。
2. MVC模式解決了什麼問題?
MVC模式可以完成各司其職的任務模式,因爲下降了各個環節的耦合性,大大優化Controller的代碼量,便於調試與維護,並且還利於程序的可複用性 。有利於團隊合做。
3. MVC的實現原理
引用一張MVC通訊示例圖:
其中虛實線就比如交通規則路線,虛線表示可穿越,實線表示不可穿越。從圖中能夠看出:
4. MVC模式的應用
用MVC模式構建以下圖片所展現的demo,其中的數據都是經過網絡請求加載的,cell是自定義的。
1. 以MVC劃分文件
2. 控制器
1 // LKSubTableViewController.h 2 #import <UIKit/UIKit.h> 3 4 @interface LKSubscriptionTableViewController : UITableViewController 5
6 @end
1 // LKSubscriptionTableViewController.m 2 #import "LKSubscriptionTableViewController.h" 3 #import "LKSubscriptionItem.h" 4 #import "LKSubscriptionTableViewCell.h" 5 6 #import <AFNetworking/AFNetworking.h> 7 #import <MJExtension/MJExtension.h> 8 #import <UIImageView+WebCache.h> 9 #import <SVProgressHUD-0.8.1/SVProgressHUD.h> 10 11 /** 可重用單元標識 */ 12 static NSString *cellIdentifier = @"Cell"; 13 14 @interface LKSubscriptionTableViewController () 15 /** 會話管理者 */ 16 @property (strong, nonatomic) AFHTTPSessionManager *mgr; 17 /** 網絡數據 */ 18 @property (strong, nonatomic) NSArray *data; 19 20 @end 21 22 @implementation LKSubscriptionTableViewController 23 24 - (void)viewDidLoad { 25 [super viewDidLoad]; 26 27 // 註冊 28 [self.tableView registerNib:[UINib nibWithNibName:@"LKSubscriptionTableViewCell" bundle:nil] forCellReuseIdentifier:cellIdentifier]; 29 30 31 // 加載網絡數據 32 [self loadData]; 33 34 [SVProgressHUD showWithStatus:@"加載中..."]; 35 36 } 37 38 #pragma mark - 1. 數據源方法 39 40 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 41 42 return 1; 43 } 44 45 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 46 47 return self.data.count; 48 } 49 50 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 51 // 加載自定義的表單元 52 LKSubscriptionTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; 53 54 // 獲取模型 55 LKSubscriptionItem *item = self.data[indexPath.row]; 56 57 // 控制器經過接口傳遞模型數據給視圖 58 cell.item = item; 59 60 return cell; 61 } 62 63 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 64 return 80; 65 } 66 67 68 #pragma mark - 2. 加載網絡數據 69 - (void)loadData { 70 // 建立會話管理者 71 self.mgr = [AFHTTPSessionManager manager]; 72 73 // 封裝參數 74 NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; 75 parameters[@"a"] = @"tag_recommend"; 76 parameters[@"c"] = @"topic"; 77 parameters[@"action"] = @"sub"; 78 79 // 發送請求 80 [self.mgr GET:@"http://api.budejie.com/api/api_open.php" parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { 81 82 [SVProgressHUD dismiss]; 83 // 經過字典數組來建立一個模型數組 84 self.data = [LKSubscriptionItem mj_objectArrayWithKeyValuesArray:responseObject]; 85 86 // 刷新列表顯示到界面 87 [self.tableView reloadData]; 88 89 } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { 90 NSLog(@"加載推薦標籤出錯:%@", error); 91 [SVProgressHUD showErrorWithStatus:@"加載失敗,請檢查網絡是否正常"]; 92 [SVProgressHUD dismiss]; 93 }]; 94 } 95 96 97 - (void)viewWillDisappear:(BOOL)animated { 98 [super viewWillDisappear:animated]; 99 [SVProgressHUD dismiss]; 100 [self.mgr.tasks makeObjectsPerformSelector:@selector(cancel)]; 101 } 102 103 104 @end
2. 視圖
1 // LKSubscriptionTableViewCell.h 2 #import <UIKit/UIKit.h> 3 4 @class LKSubscriptionItem; 5 6 @interface LKSubscriptionTableViewCell : UITableViewCell 7 /** 模型數據 */ 8 @property (strong, nonatomic) LKSubscriptionItem *item; 9 10 @end
1 // LKSubscriptionTableViewCell.m 2 #import "LKSubscriptionTableViewCell.h" 3 #import "LKSubscriptionItem.h" 4 5 #import <UIImageView+WebCache.h> 6 7 8 @interface LKSubscriptionTableViewCell () 9 /** 訂閱圖標 */ 10 @property (weak, nonatomic) IBOutlet UIImageView *icon; 11 12 /** 訂閱名稱 */ 13 @property (weak, nonatomic) IBOutlet UILabel *name; 14 15 /** 訂閱人數 */ 16 @property (weak, nonatomic) IBOutlet UILabel *number; 17 18 @end 19 20 @implementation LKSubscriptionTableViewCell 21 22 #pragma mark - 根據控制器傳遞過來的模型數據來顯示cell 23 - (void)setItem:(LKSubscriptionItem *)item { 24 _item = item; 25 26 // 設置訂閱名稱 27 _name.text = item.theme_name; 28 29 // 設置訂閱人數 30 [self resolveNum]; 31 32 // 設置訂閱圖標 33 [_icon sd_setImageWithURL:[NSURL URLWithString:self.item.image_list] placeholderImage:[UIImage imageNamed:@"defaultUserIcon"]]; 34 35 } 36 37 #pragma mark - 處理訂閱數字 38 - (void)resolveNum { 39 NSString *numStr = [NSString stringWithFormat:@"有%@人訂閱",self.item.sub_number]; 40 NSInteger num = self.item.sub_number.integerValue; 41 if (num > 10000) { 42 CGFloat numF = num / 10000.0; 43 numStr = [NSString stringWithFormat:@"%.1f萬人訂閱",numF]; 44 numStr = [numStr stringByReplacingOccurrencesOfString:@".0" withString:@""]; 45 } 46 47 _number.text = numStr; 48 } 49 50 #pragma mark - 處理cell間的分割線 51 - (void)setFrame:(CGRect)frame { 52 frame.size.height -= 1; 53 // 纔是真正去給cell賦值 54 [super setFrame:frame]; 55 } 56 57 #pragma mark - 處理切割圖片爲圓形頭像 58 - (void)awakeFromNib { 59 [super awakeFromNib]; 60 61 self.icon.layer.cornerRadius = 30; 62 self.icon.layer.masksToBounds = YES; 63 } 64 65 66 @end
3. 模型數據
1 // LKSubscriptionItem.h 2 #import <Foundation/Foundation.h> 3 4 @interface LKSubscriptionItem : NSObject 5 /** 圖標 */ 6 @property (strong, nonatomic) NSString *image_list; 7 8 /** 名稱 */ 9 @property (strong, nonatomic) NSString *theme_name; 10 11 /** 訂閱人數 */ 12 @property (assign, nonatomic) NSString *sub_number; 13 14 @end
// LKSubscriptionItem.m #import "LKSubscriptionItem.h" @implementation LKSubscriptionItem @end
註解:經過上面的demo能夠知道:控制器主要負責將從網絡上請求數據獲得的數據經過接口存儲到數據模型之中,當view,也即便自定義的cell須要數據時,並協助view與model之間的通訊,也就是把model的數據傳給了cell,這樣cell就能及時顯示數據給用戶。上述demo中各角色職責分明: