什么是設計模式
設計模式是為特定場景下的問題定制的解決方案,設計模式分三種類型:創建型,結構型,行為型.這里只列舉iOS常用到的幾種:
**創建型 : **單例 , 工廠
**結構型 : **代理 , MVC
**行為型 : **觀察者 , 策略
單例模式
單例類 : 確保某一個類只有一個實例,而且自行實例化并向整個系統提供這個實例
優點:
1-節省內存
每次獲取實例時會先進行判斷,實例存在則返回,否則創建實例,如果一直不用,則不會創建實例,從而節省了內內存空間
2-因為單例類控制實例化過程,所以可以更靈活的修改實例化過程
缺點:
1-運行費時間
上面的判斷會浪費一些時間
2-線程安全問題
并發情況下,如果線程A和線程B同時調用某一方法,會創建出兩個實例,此時單例模式失效,若想解決線程安全問題,需要加synchronized解決,但會降低訪問速度
使用場景:
1-如果創建某一個對象會耗費很多系統資源,此時采取單例模式,因為只需要一個實例,會節省alloc時間
2-若很多模塊需要使用同一變量,可以將它放入單例類
3-實際應用:"我的音樂"項目中 "音樂播放器/列表管理/文件管理"均為單例模式
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都實現了<CDelegate>方法
-執行的操作是,從類A跳轉到類B,從類B跳轉至類C,點擊類C的按鈕,只有類B相應了
使用場景:
tableView的Cell內按鈕點擊時
音樂播放器當前歌曲的進度
......
實現過程:
舉例:類A實現了類B的協議
類B
1.聲明協議及協議方法,這是要讓別的類,比如“A”實現的
@protocol BDelegate <NSObject>
- (void)passValue: (NSString *)string;
@end
2.聲明此協議的代理對象,誰使用了這個代理對象,誰就實現上面的協議,比如“A”
@property(nonatomic,assign)id< BDelegate > delegate;
3.本類決定了,實現我代理對象的類,比如“A”,何時實現我的協議方法,例當我點擊我本身的按鈕時實現
-(void)onClick
{
[self.delegate passValue:@"少壯不努力,老大徒傷悲啊”];
}
類A
1.聲明”B”對象,并實現”B”協議的代理
BViewController *b = [[BViewController alloc] init];
b.delegate = self;
2.實現協議方法,何時實現由”B”控制
- (void)passValue:(NSString *)string
{
NSLog(@"代理傳給了我什么%@",string);
}
拿tableView的cell內按鈕點擊舉例
1.在cell中聲明協議,代理對象,及何時響應
//聲明協議
@class myCell;
@protocol FQDelegate <NSObject>
- (void)clickDelegateButton:(myCell *)cell;
@end
//聲明代理對象
@interface myCell : UITableViewCell
//這個名字一般叫delegate,但其實你愛叫什么叫什么
@property (weak, nonatomic) id<FQDelegate> del;
@end
//何時響應
- (IBAction)onClickButton:(id)sender
{
if(self.del && [self.del respondsToSelector:@selector(clickDelegateButton:)])
{
[self.del clickDelegateButton:self];
}
}
2.tableView實現
@interface ViewController ()<UITableViewDataSource,UITableViewDelegate,FQDelegate>
- (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.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.聲明消息
[[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.定義一個工具類,里面封裝了各種,這里只拿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.父類為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 < 10; i++)
{
BaseClass *myClass = [BaseClass init];
NSLog(@"你建的類為%@",myClass);
}
}
3.此時打印你會發現,你建的是10個ONE對象
2016-07-29 15:05:07.985 基礎[1382:93824] 你建的類為<ONE: 0x7fca21f7dc50>
2016-07-29 15:05:07.986 基礎[1382:93824] 你建的類為<ONE: 0x7fca21d78ba0>
2016-07-29 15:05:07.986 基礎[1382:93824] 你建的類為<ONE: 0x7fca21d78ba0>
2016-07-29 15:05:07.986 基礎[1382:93824] 你建的類為<ONE: 0x7fca21e17630>
2016-07-29 15:05:07.986 基礎[1382:93824] 你建的類為<ONE: 0x7fca21e17630>
2016-07-29 15:05:07.986 基礎[1382:93824] 你建的類為<ONE: 0x7fca21e17630>
2016-07-29 15:05:07.987 基礎[1382:93824] 你建的類為<ONE: 0x7fca21d78ba0>
2016-07-29 15:05:07.987 基礎[1382:93824] 你建的類為<ONE: 0x7fca21d78ba0>
2016-07-29 15:05:07.987 基礎[1382:93824] 你建的類為<ONE: 0x7fca21d78ba0>
2016-07-29 15:05:07.987 基礎[1382:93824] 你建的類為<ONE: 0x7fca21d78ba0>
4.現在我們需要將這10個ONE變成TWO,只需要在BaseClass中改一句話,方便快捷
mainClass = [[TWO alloc] init];
2016-07-29 15:09:46.111 基礎[1479:100444] 你建的類為<TWO: 0x7fc8d3f34490>
2016-07-29 15:09:46.112 基礎[1479:100444] 你建的類為<TWO: 0x7fc8d3d2af50>
2016-07-29 15:09:46.112 基礎[1479:100444] 你建的類為<TWO: 0x7fc8d3e2e600>
2016-07-29 15:09:46.112 基礎[1479:100444] 你建的類為<TWO: 0x7fc8d3e2e600>
2016-07-29 15:09:46.113 基礎[1479:100444] 你建的類為<TWO: 0x7fc8d3cbbbe0>
2016-07-29 15:09:46.113 基礎[1479:100444] 你建的類為<TWO: 0x7fc8d3d2af50>
2016-07-29 15:09:46.113 基礎[1479:100444] 你建的類為<TWO: 0x7fc8d3e2e600>
2016-07-29 15:09:46.113 基礎[1479:100444] 你建的類為<TWO: 0x7fc8d3e2e600>
2016-07-29 15:09:46.113 基礎[1479:100444] 你建的類為<TWO: 0x7fc8d3e2e600>
2016-07-29 15:09:46.113 基礎[1479:100444] 你建的類為<TWO: 0x7fc8d3e2e600>
再舉一個例子,假如后臺返回的數據有不同的類型,根據傳來的key值不同返回不同的對象
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] 這個模型是<ONE: 0x7fb450e0cda0>
2016-07-29 15:13:58.715 基礎[1544:107073] 這個模型是<TWO: 0x7fb450cae900>
策略模式
和工廠模式相比,策略模式是對算法的封裝,使算法可以互相替換.
之前看過一個例子,假如你出去旅游,有多種方案選擇,可以騎自行車,汽車,火車,飛機,每個策略都可以得到相同的結果,但使用的是不同的資源
有一篇講策略模式很好的文章,推薦一下
設計模式之策略模式(iOS開發,代碼用Objective-C展示)