什麼是設計模式
設計模式是為特定場景下的問題定製的解決方案,設計模式分三種型別:建立型,結構型,行為型.這裡只列舉iOS常用到的幾種:
建立型 : 單例 , 工廠
結構型 : 代理 , MVC
行為型 : 觀察者 , 策略
單例模式
單例類 : 確保某一個類只有一個例項,而且自行例項化並向整個系統提供這個例項
優點:
1-節省記憶體
每次獲取例項時會先進行判斷,例項存在則返回,否則建立例項,如果一直不用,則不會建立例項,從而節省了內記憶體空間
2-因為單例類控制例項化過程,所以可以更靈活的修改例項化過程
缺點:
1-執行費時間
上面的判斷會浪費一些時間
2-執行緒安全問題
併發情況下,如果執行緒A和執行緒B同時呼叫某一方法,會建立出兩個例項,此時單例模式失效,若想解決執行緒安全問題,需要加synchronized解決,但會降低訪問速度
使用場景:
1-如果建立某一個物件會耗費很多系統資源,此時採取單例模式,因為只需要一個例項,會節省alloc時間
2-若很多模組需要使用同一變數,可以將它放入單例類
3-實際應用:”我的音樂”專案中 “音樂播放器/列表管理/檔案管理”均為單例模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
1.定義 + (instancetype)defaultManager { static 單例類名 *instance = nil; static dispatch_once_t onceToken; dispatch_once( &onceToken, ^{ instance = [[self alloc] init]; }); return instance; } 2.使用 [單例類名 defaultManager]呼叫方法及屬性即可 3.注意 假如我們令 _yourList = [CCListManager defaultManager].defaultList; 若你對_yourList執行移除操作 [_yourList removeObjectAtIndex:indexPath.row]; 則單例類中的defaultList此行也被移除了 |
代理模式
當一個類的某些功能(協議)需要由別的類來實現,但是又不確定具體會是哪個類實現,本質是某個類持有了另一個類的指標
協議是約束一個類必須實現某些方法
協議中只能定義方法,不能定義成員變數,屬性,@required為必須實現的,@optional為可選擇實現的
因為協議這個介面和類並不存在關聯關係,所以我們要用到代理,宣告一個代理屬性,約定實現代理的物件去實現協議方法
說起來有點繞,但理解了就好了….
優點:
1-有利於程式碼的封裝
2-有利於程式的結構化和層次化
3-若是@required的方法,沒有實現會編譯警告/報錯
4-一個類可以定義不同的協議,當然,每個協議得對應不同的delegate
缺點:
1-程式碼量多,協議定義,delegate屬性,本身決定什麼時候執行代理,實現代理類的實現
2-釋放後,delegate要為nil,否則會是野指標,造成記憶體洩漏,這也是要用weak來宣告delegate的原因
3-只能一對一通訊,不過這個好像不算是缺點
備註:
-假如現在有類A,類B,類C
-類C協議為CDelegate,點選類C的按鈕時,相應協議,輸出一句話
-類A和類B都實現了方法
-執行的操作是,從類A跳轉到類B,從類B跳轉至類C,點選類C的按鈕,只有類B相應了
使用場景:
tableView的Cell內按鈕點選時
音樂播放器當前歌曲的進度
……
實現過程:
舉例:類A實現了類B的協議
類B
1.宣告協議及協議方法,這是要讓別的類,比如“A”實現的
1 2 3 |
@protocol BDelegate - (void)passValue: (NSString *)string; @end |
2.宣告此協議的代理物件,誰使用了這個代理物件,誰就實現上面的協議,比如“A”
1 |
@property(nonatomic,assign)id delegate; |
3.本類決定了,實現我代理物件的類,比如“A”,何時實現我的協議方法,例當我點選我本身的按鈕時實現
1 2 3 4 |
-(void)onClick { [self.delegate passValue:@"少壯不努力,老大徒傷悲啊”]; } |
類A
1.宣告”B”物件,並實現”B”協議的代理
1 2 |
BViewController *b = [[BViewController alloc] init]; b.delegate = self; |
2.實現協議方法,何時實現由”B”控制
1 2 3 4 |
- (void)passValue:(NSString *)string { NSLog(@"代理傳給了我什麼%@",string); } |
拿tableView的cell內按鈕點選舉例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
1.在cell中宣告協議,代理物件,及何時響應 //宣告協議 @class myCell; @protocol FQDelegate - (void)clickDelegateButton:(myCell *)cell; @end //宣告代理物件 @interface myCell : UITableViewCell //這個名字一般叫delegate,但其實你愛叫什麼叫什麼 @property (weak, nonatomic) id del; @end //何時響應 - (IBAction)onClickButton:(id)sender { if(self.del && [self.del respondsToSelector:@selector(clickDelegateButton:)]) { [self.del clickDelegateButton:self]; } } 2.tableView實現 @interface ViewController () - (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *identifier = @"myCell"; myCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; if (!cell) { cell = [[[NSBundle mainBundle] loadNibNamed:@"myCell" owner:self options:nil]lastObject]; //點選事件 cell.del = self; } //繪製cell內容 //姓名 cell.nameLabel.text = @"親愛的大倩倩"; //雙拼 NSMutableString *mutStr = [NSMutableString string]; [mutStr appendString:@"雙"]; [mutStr appendString:@" / "]; [mutStr appendString:@"拼"]; cell.doubleLabel.text = mutStr; return cell; } - (void)clickDelegateButton:(myCell *)cell { NSIndexPath *indexPath = [_mainTableView indexPathForCell:cell]; NSLog(@"你當前點選的是第幾行%ld",(long)indexPath.row); } |
擴充套件一:執行時機制
假如我們上面不用代理實現,改成如下依然可以實現:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
1.cell中 @property (nonatomic,weak) id delegate; @property (nonatomic,assign) SEL method; - (IBAction)onClickButton:(id)sender { if ([self.delegate respondsToSelector:self.method]) { [self.delegate performSelector:self.method withObject:self]; } } 2.tableView實現 - (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *identifier = @"myCell"; myCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; if (!cell) { cell = [[[NSBundle mainBundle] loadNibNamed:@"myCell" owner:self options:nil]lastObject]; //點選事件 cell.delegate = self; cell.method = @selector(onClickMainButton:); } return cell; } - (void)onClickMainButton:(myCell *) cell { NSIndexPath *indexPath = [_mainTableView indexPathForCell:cell]; NSLog(@"你點選的是第幾行%ld",(long)indexPath.row); } |
擴充套件二
1-代理用weak,不用assign的原因
assign和weak均是指標賦值(直接賦值),不改變索引計數,但當被銷燬時,
assign:如果使用完畢,不將其置為nil,會產生野指標,操作不當會崩潰
weak:在屬性所指的物件遭到摧毀時,屬性值也會清空(nil out),不會崩潰
假設你用malloc分配了一塊記憶體,並且把它的地址賦值給了指標a,後來你希望指標b也共享這塊記憶體,於是你又把a賦值給(assign)了b。此時a 和b指向同一塊記憶體,請問當a不再需要這塊記憶體,能否直接釋放它?答案是否定的,因為a並不知道b是否還在使用這塊記憶體,如果a釋放了,那麼b在使用這塊記憶體的時候會引起程式crash掉
2- assigin可以用非 OC 物件,而 weak 必須用於 OC 物件
3- 代理用weak,不用strong的原因,是因為防止迴圈引用
觀察者模式
觀察者模式是一種通知變化的模式,分下面兩種
- Notification
優點:
1-程式碼量少,實現簡單
2-對於一個發出的通知,多個物件能夠做出反應,即一對多
缺點:
1-釋放通知物件時,需要在通知中心移除註冊
2-觀察者需要提前知道通知的名字,如果未定義,會不同步
3-編譯器不會檢查通知是否能被觀察者處理(相對比delegate) - KVO
優點:
1-簡單
2-能夠對非我們建立的物件,即內部物件的狀態改變做出相應
3-用key path觀察屬性,因此也可以觀察巢狀物件
4-也可以一對多
缺點:
1-觀察的屬性必須使用NSString定義 - Delegate,NSNotification,KVO
1-三者均是類和類之間的通訊
2-NSNotification和KVO一對多,Delegate一對一
3-NSNotification和KVO不關心接受者的態度,意思就是發出通知後剩下的事就不管了,而delegate會關注結果
NSNotification例子(帶引數)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
1.宣告訊息 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(respondNotification:) name:@“FQNotification" object:nil]; 2.響應訊息 [[NSNotificationCenter defaultCenter] postNotificationName:@“FQNotification”object:@“llama"]; 3.實現訊息 - (void)respondNotification:(NSNotification*)aNotification { NSString *number= [aNotification object]; NSLog(@"接收到的內容是%@",number); } |
KVO會單開專題講解
MVC模式
Model-View-Controller的縮寫,是一種架構模式,講的是M和V的程式碼分離,通過資料模型,控制器邏輯,檢視展示將應用程式進行邏輯劃分。
優點:
1-層次清晰,程式碼簡潔
2-方便測試及改動,易於維護
缺點:
1-增加系統結構和實現的複雜性
2-檢視對模型資料低效率訪問
這個就不舉例子了.
工廠模式
簡單工廠模式: 為了向客戶提供方便,將分配和初始化合在一個步驟中,返回被建立的物件.說白了,就是物件的封裝.
優點:客戶端直接使用產品,而不用關心產品的具體實現
缺點:工廠類負責邏輯實現,一旦出現問題,均受影響,而且,每增加一個產品,就要增加一個工廠類
使用場景:多個地方用到某一物件,並且此物件屬性一樣時
簡單工廠模式例子:
每個應用程式都會有自己的主色調,字型顏色及大小等等吧,我們定義每一個Button時,若都寫給它設定顏色,字型,字號等太麻煩,可以封裝起來,這樣宣告一個Button只需要執行一句程式碼就行啦
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
1.定義一個工具類,裡面封裝了各種,這裡只拿button舉例 @interface ToolViewController : UIViewController #pragma mark - Button + (UIButton *)buttonWithFrame:(CGRect)frame buttonWithTitle:(NSString *)title buttonWithSuperView:(UIView *)view; + (UIButton *)buttonWithFrame:(CGRect)frame buttonWithTitle:(NSString *)title buttonWithSuperView:(UIView *)view { UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; button.frame = frame; button.backgroundColor = [UIColor colorWithRed:69/255.0 green:187/255.0 blue:237/255.0 alpha:1]; [button setTitle:title forState:UIControlStateNormal]; [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; [button.layer setCornerRadius:5.0]; [view addSubview:button]; return button; } 2.在你需要的ViewController裡,初始化物件 #import "ToolViewController.h" ToolViewController *nextStepButton = [ToolViewController buttonWithFrame:CGRectMake(10, 200, [UIScreen mainScreen].bounds.size.width - 20, 44) buttonWithTitle:@"下一步" buttonWithSuperView:self.view]; 3.一句話就搞定啦,如果設計突然變了顏色,只需要在工具類裡改一句話就行啦 |
類工廠模式:大致可以理解為,通過繼承,建立不同的物件
優點:方便替換
缺點:不能算缺點,只是建立的這些類是同一個父類
使用場景:若使用時類會有變動,比如後臺若傳你不同的資料,需要建不同的類,可以考慮用這個
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
1.父類為BaseClass,有兩個子類ONE和TWO,在父類中 @interface BaseClass : NSObject + (instancetype)init; @end #import "ONE.h" #import "TWO.h" @implementation BaseClass + (instancetype)init { BaseClass *mainClass = nil; mainClass = [[ONE alloc] init]; return mainClass; } 2.在ViewController中宣告10個ONE物件(其實應該是在不同的地方宣告) #import "BaseClass.h" - (void)viewDidLoad { [super viewDidLoad]; for (int i = 0; i 2016-07-29 15:05:07.986 基礎[1382:93824] 你建的類為 2016-07-29 15:05:07.986 基礎[1382:93824] 你建的類為 2016-07-29 15:05:07.986 基礎[1382:93824] 你建的類為 2016-07-29 15:05:07.986 基礎[1382:93824] 你建的類為 2016-07-29 15:05:07.986 基礎[1382:93824] 你建的類為 2016-07-29 15:05:07.987 基礎[1382:93824] 你建的類為 2016-07-29 15:05:07.987 基礎[1382:93824] 你建的類為 2016-07-29 15:05:07.987 基礎[1382:93824] 你建的類為 2016-07-29 15:05:07.987 基礎[1382:93824] 你建的類為 4.現在我們需要將這10個ONE變成TWO,只需要在BaseClass中改一句話,方便快捷 mainClass = [[TWO alloc] init]; 2016-07-29 15:09:46.111 基礎[1479:100444] 你建的類為 2016-07-29 15:09:46.112 基礎[1479:100444] 你建的類為 2016-07-29 15:09:46.112 基礎[1479:100444] 你建的類為 2016-07-29 15:09:46.112 基礎[1479:100444] 你建的類為 2016-07-29 15:09:46.113 基礎[1479:100444] 你建的類為 2016-07-29 15:09:46.113 基礎[1479:100444] 你建的類為 2016-07-29 15:09:46.113 基礎[1479:100444] 你建的類為 2016-07-29 15:09:46.113 基礎[1479:100444] 你建的類為 2016-07-29 15:09:46.113 基礎[1479:100444] 你建的類為 2016-07-29 15:09:46.113 基礎[1479:100444] 你建的類為 |
再舉一個例子,假如後臺返回的資料有不同的型別,根據傳來的key值不同返回不同的物件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
1.在BaseClass中 @interface BaseClass : NSObject + (instancetype)initWithDictionary:(NSDictionary *)dictionary; @end #import "ONE.h" #import "TWO.h" @implementation BaseClass + (instancetype)initWithDictionary:(NSDictionary *)dic { BaseClass *mainClass = nil; if ([dic[@"key"] isEqualToString:@"ONE"]) { mainClass = [[ONE alloc] init]; } else { mainClass = [[TWO alloc] init]; } return mainClass; } @end 2.在ViewController中 #import "BaseClass.h" - (void)viewDidLoad { [super viewDidLoad]; NSDictionary *dic1 = [[NSDictionary alloc] initWithObjectsAndKeys:@"ONE",@"key",@"AAA",@"name",nil]; NSDictionary *dic2 = [[NSDictionary alloc] initWithObjectsAndKeys:@"TWO",@"key",@"BBB",@"name",nil]; NSMutableArray *arr = [[NSMutableArray alloc] initWithObjects:dic1,dic2, nil]; for (NSDictionary *dic in arr) { BaseClass *myClass = [BaseClass initWithDictionary:dic]; NSLog(@"這個模型是%@",myClass); } } 3.結果 2016-07-29 15:13:58.714 基礎[1544:107073] 這個模型是 2016-07-29 15:13:58.715 基礎[1544:107073] 這個模型是 |
策略模式
和工廠模式相比,策略模式是對演算法的封裝,使演算法可以互相替換.
之前看過一個例子,假如你出去旅遊,有多種方案選擇,可以騎自行車,汽車,火車,飛機,每個策略都可以得到相同的結果,但使用的是不同的資源
有一篇講策略模式很好的文章,推薦一下
設計模式之策略模式(iOS開發,程式碼用Objective-C展示)