iOS學習
- 單例
- 代理
- 代理模式的原理
- 代理的循環引用
- 設計模式
單例
優點:
- 全局訪問:單例模式確保一個類只有一個實例,并提供全局訪問點,方便在整個應用中共享數據或功能。
- 節省資源:由于只創建一個實例,可以減少內存開銷,避免重復創建相同對象。
- 控制實例化:通過私有構造函數,防止外部代碼創建多個實例,確保數據一致性。
缺點:
- 隱藏依賴:使用單例可能導致代碼中隱藏的依賴關系,增加了代碼的耦合性,降低了可測試性。
- 難以擴展:單例模式不易于擴展,若需要改變實例的行為,可能需要修改單例類的代碼。
- 線程安全問題:在多線程環境下,單例的實現需要特別注意線程安全,若處理不當可能導致數據不一致。
系統為我們提供的單例類:
UIApplication(應用程序實例類)
NSNotificationCenter(消息中心類)
NSFileManager(文件管理類)
NSUserDefaults(應用程序設置)
NSURLCache(請求緩存類)
NSHTTPCookieStorage(應用程序cookies池)
分為懶漢模式和餓漢模式。懶漢模式是需要時創建,餓漢模式是程序啟動時創建,用的時候拿出來
實現單例模式只需要改寫四種方法:alloc init方法,類方法,copy方法,mutable Copy方法。
懶漢模式:
#import "Singletion.h"@implementation Singletion
static id instance = nil;+(instancetype)allocWithZone:(struct _NSZone *)zone
{if(instance == nil) {@synchronized (self) {if(instance == nil) {instance = [super allocWithZone:zone];}}}return instance;
}+(instancetype)mySingletion {if (instance == nil) {@synchronized (self) {instance = [[self alloc] init];}}return instance;
}-(id)copyWithZone:(NSZone *)zone
{return instance;
}-(id)mutableCopyWithZone:(NSZone *)zone
{return instance;
}
餓漢模式:
#import "Singletion.h"@implementation Singletion
static id instance = nil;
//區別就在于以下函數
+ (void)load{instance = [[self alloc] init];
}+(instancetype)mySingletion {if (instance == nil) {@synchronized (self) {instance = [[self alloc] init];}}return instance;
}+(instancetype)allocWithZone:(struct _NSZone *)zone
{if(instance == nil) {@synchronized (self) {if(instance == nil) {instance = [super allocWithZone:zone];}}}return instance;
}-(id)copyWithZone:(NSZone *)zone
{return instance;
}-(id)mutableCopyWithZone:(NSZone *)zone
{return instance;
}
由于多線程的原因,實現真正的單例模式需要加鎖,有以下兩種方法:
加鎖寫法:
+(instancetype)allocWithZone:(struct _NSZone *)zone
{if(instance == nil) {@synchronized (self) {//自旋鎖if(instance == nil) {instance = [super allocWithZone:zone];}}}return instance;
}
GCD寫法:
+ (instancetype)allocWithZone:(struct _NSZone *)zone {static dispatch_once_t onceToken = 0;dispatch_once(&onceToken, ^{instance = [super allocWithZone:zone];});return instance;
}
dispatch_once 主要是根據 onceToken 的值來決定怎么去執行代碼。
1.當 onceToken = 0 時,線程執行 dispatch_once 的 block 中代碼;
2.當 onceToken = -1 時,線程跳過 dispatch_once 的 block 中代碼不執行;
3.當 onceToken 為其他值時,線程被阻塞,等待 onceToken 值改變。
注意:此處使用GCD寫法要好過加鎖寫法。GCD不僅有更小的開銷,更好的性能,同時還不需要手動管理鎖。
代理
協議是多個類(或者對象)之間協商的一個公共接口,提供了一系列方法的聲明給類們使用;代理是協議的一個典型應用機制。代理模式的核心思想就是通過代理接口分離使用者和服務提供者,降低了模塊之間的耦合度。
協議的編寫規范:
-
一般情況下, 當前協議屬于誰, 我們就將協議定義到誰的頭文件中
-
協議的名稱一般以它屬于的那個類的類名開頭, 后面跟上protocol或者delegate
-
協議中的方法名稱一般以協議名稱protocol之前的作為開頭
-
一般情況下協議中的方法會將觸發該協議的對象傳遞出去
-
一般情況下一個類中的代理屬于的名稱叫做 delegate
-
當某一個類要成為另外一個類的代理的時候, 一般情況下在.h中用@protocol 協議名稱;告訴當前類 這是一個協議.在.m中用#import真正的導入一個協議的聲明
注意:
- 協議不能聲明成員變量,不能寫實現
- 只要父類遵守了某個協議,那么子類也遵守
- 協議可以遵守協議,一個協議遵守了另一個協議,就可以擁有另一份協議中的方法聲明
協議中有2個關鍵字可以控制方法是否要實現(默認是@required,在大多數情況下,用途在于程序員之間的交流)
- @required:這個方法必須要實現(若不實現,編譯器會發出警告)
- @optional:這個方法不一定要實現
代理模式的原理
在iOS中代理的本質就是代理對象內存的傳遞和操作,我們在委托類設置代理對象后,實際上只是用一個id類型的指針將代理對象進行了一個弱引用。委托方讓代理方執行操作,實際上是在委托類中向這個id類型指針指向的對象發送消息,而這個id類型指針指向的對象,就是代理對象。
組成部分:
- 協議(Protocol) - 定義了代理需要實現的方法
- 委托方(Delegator) - 持有代理對象并在適當時機調用代理方法
- 代理方(Delegate) - 實現協議中定義的方法
回顧一下使用:
首先我們定義一個協議,并且設置定義委托方
@protocol SecondViewControllerDelegate <NSObject>- (void)didUpdateText: (NSString *)text;@end@interface SecondViewController : UIViewController//定義委托方
@property (nonatomic, weak) id<SecondViewControllerDelegate> delegate;
@property (nonatomic, strong) UITextField *textField;@end
在委托方的.m文件中,合適的地方觸發委托方法。要注意確保該委托對象實現了委托方法。所以可以使用respondsToSelector:
方法進行檢查
- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = [UIColor lightGrayColor];self.textField = [[UITextField alloc] initWithFrame: CGRectMake(20, 100, 200, 30)];self.textField.center = self.view.center;self.textField.borderStyle = UITextBorderStyleRoundedRect;//self.textField.text = @"placeholder";self.textField.delegate = self;[self.view addSubview: self.textField];
}
//觸發委托方法
- (void)textFieldDidChangeSelection:(UITextField *)textField {if ([self.delegate respondsToSelector: @selector(didUpdateText:)]) {[self.delegate didUpdateText: textField.text];}
}
再定義代理方:首先要遵循委托方的協議。
#import <UIKit/UIKit.h>
#import "SecondViewController.h"@interface FirstViewController : UIViewController <SecondViewControllerDelegate>@property (nonatomic, strong)UITextField* textField;@end
其次要實現委托協議,并且將自己設置會委托方的代理。即.delegare = self。
- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = [UIColor lightGrayColor];self.textField = [[UITextField alloc] initWithFrame: CGRectMake(20, 100, 200, 30)];self.textField.center = self.view.center;self.textField.borderStyle = UITextBorderStyleRoundedRect;[self.view addSubview: self.textField];UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(pushSecondController)];[self.view addGestureRecognizer: tapGesture];
}- (void)pushSecondController {SecondViewController* secondViewController = [[SecondViewController alloc] init];//將委托對象(代理)設置為FirstViewControllersecondViewController.delegate = self;[self.navigationController pushViewController: secondViewController animated: YES];
}//實現委托協議
- (void)didUpdateText:(NSString *)text {//協議傳值self.textField.text = text;
}
代理的循環引用
一下代碼會發生代理的循環引用:B強引用A,而A的delegate屬性指向B,這里的delegate是用strong修飾的,所以A也會強引用B。因此,通常情況下,我們都是用弱引用weak來修飾delegate
@protocol ClssADelegate
- (void)eat;
@end@interface ClassA : UIViewController
@property (nonatomic, strong) id delegate;//改為弱引用weak
@end//ClassB:
@interface ClassB ()
@property (nonatomic, strong) ClassA *classA;
@end@implementation ClassB
- (void)viewDidLoad {[super viewDidLoad];self.classA = [[ClassA alloc] init];self.classA.delegate = self;
}
下面我們看幾種傳值方式的優缺點:
設計模式
此處簡介一下設計模式,后期再繼續補充。
KVO/通知 -------> 觀察者模式
觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態上發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。
優勢:解耦合
接口隔離原則、開放-封閉原則
KVC --------> KVC模式
單例模式
利用應用程序只有一個該類的實例對象這一特殊性來實現資源共享。
優勢:使用簡單,延時求值,易于跨模塊
劣勢:這塊內存知道程序退出時才能釋放
單一職責原則
舉例:[UIApplication sharedApplication]。
代理模式
委托方將不想完成的任務交給代理方處理,并且需要委托方通知代理方才能處理。
優勢: 解耦合
開放-封閉原則
舉例:tableview的數據源和代理
策略模式
策略模式定義了一系列的算法,并將每一個算法封裝起來,而且使它們還可以相互替換。策略模式讓算法獨立于使用它的客戶而獨立變化。
優勢:使算法的變化獨立于使用算法的用戶
接口隔離原則、多用組合,少用繼承、針對接口編程,而非實現
舉例:賬號密碼輸入格式的判斷、NSArray的sortedArrayUsingSelector等等
MVC模式
將程序書寫分為三層,分別為模型、視圖、控制器,每層都有各自的職責完成各自的工作。
優勢: MVC模式使系統,層次清晰,職責分明,易于維護
對擴展開放-對修改封閉
MVVM模式
用于解決MVC模式下C層代碼冗雜的情況(過多的網絡請求以及業務邏輯處理)而出現的MVVM模式,其相比于MVC多了一層ViweModel(業務處理和數據轉化)層,專門用于處理數據。
當功能簡單時,MVVM反而會增加很多代碼,所以對于簡單的功能,MVC更加的方便。
三種工廠模式
通過給定參數來返回對應的實例,完全對用戶隱藏其實現的原理。
優勢:易于替換,面向抽象編程
依賴倒置原則