面試系列:
- iOS面試全解1:基礎(chǔ)/內(nèi)存管理/Block/GCD
- iOS面試全解2:Runloop
- iOS面試全解3:Runtime
-
iOS面試全解4:KVC/KVO、通知/推送/信號量、Delegate/Protocol、Singleton(當(dāng)前位置)
一、KVC
全稱是Key-value coding,鍵值編碼。使用字符串來標(biāo)識屬性,是間接訪問對象的屬性, 而不是直接通過調(diào)用存取方法(Setter、Getter方法)訪問。可以在運(yùn)行時動態(tài)訪問和修改對象的屬性。
KVC的方法定義在: Foundation/NSKeyValueCoding中。
特點(diǎn): 可以簡化程序代碼。

KVC詳解
-
1、KVC原理
- KVC 訪問私有變量
- setter 原理分析
- getter 原理分析
- forKeyPath、 valueForKeyPath
- 異常處理: 賦值/取值、正確性驗(yàn)證
2、KVC與字典
3、KVC的消息傳遞
4、KVC容器操作
5、KVC集合代理對象
6、KVC的應(yīng)用
一、KVC原理
1、 KVC 訪問私有變量
1.1 能夠訪問私有成員變量(給對象的私有成員進(jìn)行:取值/賦值 )
1.2 對數(shù)值和結(jié)構(gòu)體型的屬性進(jìn)行的 打包/解包 處理
- valueForUndefinedKey
- setValue:forUndefinedKey:
myName 、_myName
myAge、_myAge
KVC常用的方法
為對象的屬性:取值、賦值
# 賦值方法
-(void)setValue:(id)value forKeyPath:(NSString *)keyPath;
-(void)setValue:(id)value forKey:(NSString *)key;
# 取值方法
-(id)valueForKeyPath:(NSString *)keyPath;
-(id)valueForKey:(NSString *)key;
# 案例:
[person1 setValue:@"jack" forKey:@"name"]; # 賦值
NSString *name = [person1 valueForKey:@"name"]; # 取值
# forKeyPath 是對更“深層”的對象進(jìn)行訪問。如數(shù)組的某個元素,對象的某個屬性。如:
[myModel setValue:@"beijing" forKeyPath:@"address.city"];
# 返回所有對象的name屬性值
NSArray *names = [array valueForKeyPath:@"name"];
// 通過keyPath取值
? key:單層訪問
? keyPath:可以多層訪問
? key:@"age"
? keyPath:@"age"
? keyPath:@"student.age"
? keyPath:@"person.student.age"
注意:setter、getter:會按照 _key,_iskey,key,iskey 的順序搜索成員
2、setter 原理分析:(賦值過程順序如下)
先找相關(guān)方法
//1、-(void) setName
//2、-(void) _setName
//3、-(void) setIsName
//與 -(void) _setIsName 無關(guān)若沒有相關(guān)方法,判斷是否可以直接找方法成員變量
+ (BOOL)accessInstanceVariablesDirectly
2.1 NO:系統(tǒng)拋出一個異常,未定義key
2.2 YES:繼續(xù)找相關(guān)變量
//1、_name
//2、_isName
//3、name
//4、isName方法或成員變量都不存在:
使用setValue:forUndefinedKey:方法,拋出異常。
3、getter 原理分析(取值過程順序如下)
先找相關(guān)方法
//1、- getName
//2、- name若沒有相關(guān)方法,判斷是否可以直接找方法成員變量
+ (BOOL)accessInstanceVariablesDirectly
2.1 NO:系統(tǒng)拋出一個異常,未定義key
2.2 YES:繼續(xù)找相關(guān)變量
//1、_name
//2、_isName
//3、name
//4、isName方法或成員都不存在,使用
valueForUndefinedKey:方法,拋出異常
4、setValue:forKeyPath、 valueForKeyPath
尋找多級屬性(KeyPath)
賦值:- setValue:forKeyPath:
取值:- valueForKeyPath: :
dog.name
_placeholderLabel.textColor
5、異常處理: 賦值、取值;正確性驗(yàn)證
異常處理 總結(jié):
- 解決方案:重寫以下相關(guān)方法
賦值:value為空 setNilValueForKey:
賦值:Key值不存在 setValue: forUndefinedKey:
取值:Key值不存在 valueForUndefinedKey:
正確性驗(yàn)證: validateValue
該方法的工作原理:
1. 先找一下你的類中是否實(shí)現(xiàn)了方法.
-(BOOL)validate<Key>:error:
2. 如果實(shí)現(xiàn)了就會根據(jù)實(shí)現(xiàn)方法里面的自定義邏輯返回NO或者YES,如果沒有實(shí)現(xiàn)這個方法,則系統(tǒng)默認(rèn)返回就是YES
//正確性驗(yàn)證: validateValue(內(nèi)部驗(yàn)證)
- (BOOL)validateValue:(inout id _Nullable * _Nonnull)ioValue
forKey:(NSString *)inKey
error:(out NSError **)outError {
//...
return YES;
}
二、KVC與字典
<Foundation/NSKeyValueCoding.h>
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
三、KVC的消息傳遞
NSArray* lengthArr = [arr valueForKey:@"length"];
NSArray* lowercaseArr = [arr valueForKey:@"lowercaseString"];
# 給成員 length 發(fā)送消息(遍歷所有成員 長度)
# 給成員 lowercaseString 發(fā)送消息(字符串全部轉(zhuǎn)成小寫)
#pragma mark - KVC消息傳遞
- (void)arrayMessagePass{
NSArray *array = @[@"Alan",@"Xing",@"XZ",@"ZhaiAlan"];
NSArray *lenStr= [array valueForKeyPath:@"length"];
NSArray *lowStr= [array valueForKeyPath:@"lowercaseString"];
NSLog(@"---%@",lenStr);// 消息從array傳遞給了string
NSLog(@"---%@",lowStr);
}
輸出結(jié)果:
---( 4, 4, 2, 8 )
---( alan, xing, xz, zhaialan )
四、KVC容器操作
1. 聚合操作符 @avg、@count、@max、@min、@sum
平均數(shù)、數(shù)量、最大值、最小值、合值
float avg = [[students valueForKeyPath:@"@avg.height"] floatValue];
float max = [[students valueForKeyPath:@"@max.height"] floatValue];
float min = [[students valueForKeyPath:@"@min.height"] floatValue];
float sum = [[students valueForKeyPath:@"@sum.height"] floatValue];
int count = [[students valueForKeyPath:@"@count.height"] intValue];
2. 數(shù)組操作符
去重:@distinctUnionOfObjects
不去重:@unionOfObjects
NSArray* arr1 = [students valueForKeyPath:@"@distinctUnionOfObjects.height"];
NSArray* arr2 = [students valueForKeyPath:@"@unionOfObjects.height"];
3. 嵌套集合(array&set NSMutableArray )操作
@distinctUnionOfArrays 、@distinctUnionOfSets、@unionOfArrays
讀取集合中每個元素的鍵路徑指定的屬性,放在一個NSArray實(shí)例中,將數(shù)組進(jìn)行去重后返回
NSMutableArray* students1 = [NSMutableArray array];
NSMutableArray* students2 = [NSMutableArray array];
//嵌套數(shù)組
NSArray* nestArr = @[students1, students2];
NSArray* arr1 = [nestArr valueForKeyPath:@"@distinctUnionOfArrays.height"];
NSArray* arr2 = [nestArr valueForKeyPath:@"@unionOfArrays.height"];
4. 嵌套集合(array&set NSMutableSet)操作 @distinctUnionOfArrays 、@distinctUnionOfSets、@unionOfArrays
NSMutableSet* students1 = [NSMutableSet set];
NSMutableSet* students2 = [NSMutableSet set];
NSSet* nestSet = [NSSet setWithObjects:students1, students2, nil];
NSArray* arr1 = [nestSet valueForKeyPath:@"@distinctUnionOfSets.height"];
//不去重-沒有此方法(異常崩潰:this class does not implement the unionOfArrays operation.)
// NSArray* arr2 = [nestSet valueForKeyPath:@"@unionOfArrays.height"];
五、KVC集合代理對象
-
個人理解(一對多的關(guān)系:通過一個key,對應(yīng)多個方法)
? 對象的屬性可以是一對一的,也可以是一對多的。一對多的屬性要么是有序的(數(shù)組),要么是無序的(集合)。
? 屬性的一對多關(guān)系其實(shí)就是一種對容器類的映射。
? 不可變的有序容器屬性(NSArray)和無序容器屬性(NSSet)一般可以使用valueForKey:來獲取。
? 如果有一個名為numbers的數(shù)組屬性,我們可以使用valueForKey:@"numbers"來獲取,這個是沒問題的,但KVC還能使用更靈活的方式管理集合?!蔷褪牵杭洗韺ο?/p>(變量 variable)
-(void)valueForKey 有如下的搜索規(guī)則:
1、按順序搜索 getKey、key、isKey,第一個被找到的會用作返回。
2、countOf<Key>、objectIn<Key>AtIndex 與<key>AtIndexes其中之一,這個組合會使KVC返回一個代理數(shù)組。
3、countOf<Key>、enumeratorOf<Key>、memberOfVar。這個組合會使KVC返回一個代理集合。
4、名為_val、_isVar、var、isVar的實(shí)例變量。到這一步時,KVC會直接訪問實(shí)例變量,而這種訪問操作破壞了封裝性,我們應(yīng)該盡量避免,這可以通過重寫+(Bool)accessInstanceVariablesDirectly返回NO來避免這種行為。
6、KVC的應(yīng)用
6.1、動態(tài)地取值和賦值
6.2、用KVC來訪問和修改私有變量
對于類里的私有屬性,OC是無法直接訪問的,但是KVC是可以的。
6.3、Model和字典轉(zhuǎn)換
6.4、修改一些控件的內(nèi)部屬性:最常用的就是UITextField中的placeHolderText了。
6.5、操作集合
6.6、用KVC實(shí)現(xiàn)高階消息傳遞
[self.textField setValue:[UIColor blueColor] forKeyPath:@"_placeholderLabel.textColor"];
[self.textField setValue:[UIFont systemFontOfSize:14] forKeyPath:@"_placeholderLabel.font"];
[self.textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.backgroundColor"];
二、KVO
1、KVO原理分析(概念)
2、KVO基本使用
3、KVO自定義
4、KVO延伸(自動銷毀機(jī)制、Blocks、YY封裝KVO)
1、概念
全稱(Key-Value Observing)鍵值觀察
允許對象通知其他對象屬性的更改。它對應(yīng)用程序中模型 Model和控制器層 VC之間的通信特別有用。 (在OS X中,控制器層綁定技術(shù)嚴(yán)重依賴于鍵值觀察。)控制器對象通常觀察模型對象的屬性,視圖對象通過控制器觀察模型對象的屬性。然而,另外,模型對象可以觀察其他模型對象(通常用于確定從屬值何時改變)或甚至自身(再次確定從屬值何時改變)。
原理分析:
1. 動態(tài)生成一個繼承自原類的新類
2. 使用Person的分類,重寫新類的set方法
3. 修改isa指針的指向。將self的isa指針指向新類,也就是為了在調(diào)用setter方法的時候,會去新類中查找。
4. 將self的isa指針指向原類,為了獲得舊值,然后賦值新值。最后去通知觀察者值的改變,isa指針再指向新類(為了繼續(xù)觀察屬性,下次進(jìn)行通知)。
引用:
KVO原理以及自定義KVO
iOS探索KVO實(shí)現(xiàn)原理,重寫KVO
//設(shè)置舊值
change[NSKeyValueChangeOldKey] = oldValue;
//設(shè)置新值
change[NSKeyValueChangeNewKey] = newValue;
//調(diào)用observer里面的回調(diào)方法
objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),keyPath,observer,change,context);
//self的isa指針指向新類
object_setClass(self, newClass);
——————————————————————————
特點(diǎn):觀察屬性,一對一關(guān)系和多對多關(guān)系。
一個簡單的例子:假設(shè)Person對象與Account賬戶對象交互。 Person的實(shí)例要知道Account實(shí)例的某些方面何時發(fā)生變化,例如余額或利率。
- 一個對象的一個屬性、多個屬性;
- 多個對象的一個屬性、多個屬性;
ViewController VC --> Observing Model Property
View --> VC --> Observing Model Property
1.1、Runtime 實(shí)現(xiàn)KVO
KVO的實(shí)現(xiàn)依賴于 Objective-C 強(qiáng)大的 Runtime,當(dāng)觀察某對象 People 時,KVO 機(jī)制動態(tài)創(chuàng)建一個對象people當(dāng)前類的子類,并為這個新的子類重寫了被觀察屬性 keyPath 的 setter方法。setter 方法隨后負(fù)責(zé)通知觀察對象屬性的改變狀況。
Apple 使用了isa-swizzling 來實(shí)現(xiàn) KVO 。當(dāng)觀察對象people時,KVO機(jī)制動態(tài)創(chuàng)建一個新的名為:NSKVONotifying_People的新類,該類繼承自對象People 的本類,且 KVO 為 NSKVONotifying_People 重寫觀察屬性的 setter 方法,setter 方法會負(fù)責(zé)在調(diào)用原 setter 方法之前和之后,通知所有觀察對象屬性值的更改情況。
1.2、NSKVONotifying_People 類剖析
NSLog(@"self->isa: %@",self->isa);
NSLog(@"self class: %@",[self class]);
# 在建立KVO監(jiān)聽前,打印結(jié)果為:
self->isa: People
self class: People
# 在建立KVO監(jiān)聽之后,打印結(jié)果為:
self->isa: NSKVONotifying_People
self class: People
1.3、子類setter方法剖析
KVO 的鍵值觀察通知依賴于 NSObject 的兩個方法:
-
willChangeValueForKey:被觀察屬性發(fā)生改變之前被調(diào)用,通知即將改變、 -
didChangeValueForKey:被觀察屬性發(fā)生改變之后被調(diào)用,通知已經(jīng)變更。
在存取數(shù)值的前后分別調(diào)用 這2 個方法,且重寫觀察屬性的setter 方法這種繼承方式的注入是在運(yùn)行時而不是編譯時實(shí)現(xiàn)的。
手動調(diào)用KVO
KVO 為子類的觀察者屬性重寫調(diào)用存取方法的工作原理在代碼中相當(dāng)于:
- (void)setName:(NSString *)newName {
[self willChangeValueForKey:@"name"]; # KVO 在調(diào)用存取方法之前總調(diào)用
[super setValue:newName forKey:@"name"]; # 調(diào)用父類的存取方法
[self didChangeValueForKey:@"name"]; # KVO 在調(diào)用存取方法之后總調(diào)用
}
如果想控制當(dāng)前對象的自動調(diào)用過程,也就是由上面兩個方法發(fā)起的KVO調(diào)用,則可以重寫下面方法。方法返回YES則表示可以調(diào)用,如果返回NO則表示不可以調(diào)用。
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
BOOL automatic = NO;
if ([theKey isEqualToString:@"balance"]) {
automatic = NO;
}
else {
automatic = [super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}
分析觀察者的整個過程:
1、未被觀察的屬性,直接找到屬性的 setter/getter方法 賦值/取值
2、被觀察的屬性,會動態(tài)創(chuàng)建一個子類 NSKVONotifying_object,然后添加一些方法,重寫對應(yīng)屬性的 setter/getter 方法,觀察子類屬性的變化,并通知給子類
*isa --> subClass --> superClass --> mateClass ... --> rootClass
(先找子類方法,再找父類方法,再找元類方法,根類方法 根類的元類就是它自己,元類最后指向自己)
-
TZPerson:isa-->NSKVONotifiying_TZPerson-->supclass TZPerson - 添加觀察后:動態(tài)去創(chuàng)建一個新類,并添加一些方法/屬性,
繼承了原類(移除觀察者后,isa指針重新指向 TZPerson 原類)
重寫了子類:指針指向原類
- (Class) class {
return object_superClass(object_getClass(self));
}
方法調(diào)用過程:
- 即將改變:NSKeyValueWillChange
-[TZPerson setSteps:]
- 已經(jīng)改變:NSKeyValueDidChange
- 通知觀察者:NSKeyValueNotifyObserver
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
2、基本使用
使用三步驟:
- 添加觀察者
- 實(shí)現(xiàn)監(jiān)聽方法
- 移除觀察者

3、KVO自定義
- 動態(tài)創(chuàng)建一個類
// 1. 拼接子類名 NSKVONotifying_object
// 2. 創(chuàng)建并注冊類- class 創(chuàng)建并注冊類
- class 添加一些方法
- class (重寫屬性的 setter 方法)
- 方法反射,添加一個方法
- zm_setter
- 添加析構(gòu)方法 - dealloc
- return newClass;
- 修改isa的指向
- 關(guān)聯(lián)方法
4、KVO延伸(自動銷毀機(jī)制、Blocks、YY封裝KVO)
在YY_KVO 中使用到
---------------------------- "NSObject+YYAddForKVO.h" ----------------------------
# //實(shí)現(xiàn)監(jiān)聽
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (!self.block) return;
BOOL isPrior = [[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue];
if (isPrior) return;
# //使用中間類
NSKeyValueChange changeKind = [[change objectForKey:NSKeyValueChangeKindKey] integerValue];
if (changeKind != NSKeyValueChangeSetting) return;
id oldVal = [change objectForKey:NSKeyValueChangeOldKey];
if (oldVal == [NSNull null]) oldVal = nil;
id newVal = [change objectForKey:NSKeyValueChangeNewKey];
if (newVal == [NSNull null]) newVal = nil;
# //Block 回調(diào)監(jiān)聽結(jié)果
self.block(object, oldVal, newVal);
問題:
1、KVO是否會對一個 變量 進(jìn)行通知?
答:不會,只觀察對象的屬性。
三、通知/信號量
1、通知:NSNotification
目前分為四個推送:
1.用戶推送:NSNotificationCenter
2.本地推送:UILocalNotification(狀態(tài)欄提示:例如鬧鐘、掛號排隊(duì))
3.遠(yuǎn)程推送:RemoteNotifications
4.地理位置推送:必須配合CLLocation使用
過程:注冊、接收、處理
有以下特點(diǎn):
- 一對一 或 一對多
- 消息的
發(fā)送者告知接收者事件已經(jīng)發(fā)生或者將要發(fā)送,僅此而已,接收者不能影響發(fā)送者的行為。
#//1.1、添加:通知
[[NSNotificationCenter defaultCenter] postNotificationName:@"通知變化" object:nil];
#//1.2、接收:通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ChangNotificat:) name:@"通知變化" object:nil];
#//1.3、移除:通知
[[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:self];
# // 2、監(jiān)聽通知:鍵盤
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(NotificatClick:)
name:UIKeyboardWillHideNotification object:nil];
# /* 3、監(jiān)聽通知:輸入框
* UITextFieldTextDidBeginEditingNotification;
* UITextFieldTextDidEndEditingNotification;
* UITextFieldTextDidChangeNotification;
*/
[[NSNotificationCenter defaultCenter] addObserverForName:UITextFieldTextDidEndEditingNotification
object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notificate){
NSLog(@" textField ");
if ([notificate.object isEqual:self]){
}
}];
2、信號量:dispatch_semaphore
dispatch_semaphore 是信號量,但當(dāng)信號總量設(shè)為 1 時也可以當(dāng)作鎖來。在沒有等待情況出現(xiàn)時,它的性能比 pthread_mutex 還要高,但一旦有等待情況出現(xiàn)時,性能就會下降許多。相對于 OSSpinLock 來說,它的優(yōu)勢在于等待時不會消耗 CPU 資源。對磁盤緩存來說,它比較合適。
一般在GCD職工使用,我理解的dispatch_semaphore有兩個主要應(yīng)用 :
- 保持線程同步
- 為線程加鎖,等待執(zhí)行任務(wù)完成后,通知后面的任務(wù)執(zhí)行
四、代理協(xié)議:Delegate/Protocol
sender: 消息的發(fā)送者
receiver:消息的接收者
delegate:代理者
Delegate 代理
代理的目的是改變或傳遞控制鏈??梢詼p少框架復(fù)雜度。
允許一個類在某些特定時刻通知到其他類,而不需要獲取到那些類的指針。
可以減少框架復(fù)雜度。
消息的發(fā)送者(sender)告知接收者(receiver)某個事件將要發(fā)生,delegate同意,然后發(fā)送者響應(yīng)事件:發(fā)送者 委托 接收者 去做某件事。
delegate機(jī)制使得接收者可以改變發(fā)送者的行為。
通常發(fā)送者和接收者的關(guān)系是直接的 一對一的關(guān)系。
IBOutlet可以為weak,NSString為copy,Delegate一般為weak。
Protocol 小結(jié):
類對象遵守了Protocol,那么該類就有了Protocol里面聲明的方法。類必須實(shí)現(xiàn)@required方法、可選實(shí)現(xiàn)@optional方法,具體怎么實(shí)現(xiàn)由該類自行決定,Protocol不過問。
五、單例:Singleton
1、單例模式的三個要點(diǎn):
1) 某個類 只能有一個實(shí)例(另外創(chuàng)建必須crash拋出異常提示);
2) 它必須 自行創(chuàng)建這個實(shí)例;
3) 它必須自行 向整個系統(tǒng)提供這個實(shí)例。
2、單例模式的優(yōu)點(diǎn):
1.實(shí)例控制:Singleton 會阻止其他對象實(shí)例化其自己的 Singleton 對象的副本,從而 確保所有對象都訪問唯一實(shí)例。
2.靈活性:因?yàn)轭惪刂屏藢?shí)例化過程,所以類可以更加靈活修改實(shí)例化過程
一個共享的實(shí)例,該實(shí)例只會被創(chuàng)建一次。
**該方法有很多優(yōu)勢: **
1、線程安全
2、很好滿足靜態(tài)分析器要求
3、和自動引用計(jì)數(shù)(ARC)兼容
4、僅需要少量代碼
使用繼承單例 源類,便于管理,代碼如下:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
//Impl:初始微程序裝載(Initial Microprogram Loading)
//#if DEBUG
// #define SINGLETON_INSTANCE_LOG(x) NSLog(@"Singleton object <%s(%@)> has been created.",#x, [x class])
//#else
// #define SINGLETON_INSTANCE_LOG(x)
//#endif
// DEBUG 宏定義模式不用
#define SINGLETON_INSTANCE_LOG(x)
//1、繼承于 Singleton 的初始微程序裝載(實(shí)例化)
#define Singleton_Instance_method_Interface(ClassName) \
+ (ClassName *)instance;
#define Singleton_Instance_method_Impl(ClassName) \
+ (ClassName *)instance \
{ \
static ClassName *_g_##ClassName##_obj = nil; \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_g_##ClassName##_obj = [[self singletonAlloc] init]; \
SINGLETON_INSTANCE_LOG(_g_##ClassName##_obj); \
}); \
return _g_##ClassName##_obj; \
}
//2、不繼承于 Singleton 的初始微程序裝載(實(shí)例化)
#define Singleton_Instance_method_ImplAlloc(ClassName) \
+ (ClassName *)instance \
{ \
static ClassName *_g_##ClassName##_obj = nil; \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_g_##ClassName##_obj = [[self alloc] init]; \
SINGLETON_INSTANCE_LOG(_g_##ClassName##_obj); \
}); \
return _g_##ClassName##_obj; \
}
/**
* 我建議:
單例類都應(yīng)該繼承JKSingletonObject這個項(xiàng)目。
強(qiáng)迫每個人都使用+ singletonAlloc對團(tuán)隊(duì)有好處。
*/
@interface Singleton : NSObject
+ (id)singletonAlloc;
@end
#import "Singleton.h"
@implementation Singleton
/**
* 覆蓋這個方法是阻止其他程序員編碼“[[Singleton alloc]init]”。
你永遠(yuǎn)不應(yīng)該使用alloc自己。請寫+實(shí)例在自己的子類。
請參閱類方法+ Singleton.h實(shí)例。
*/
+ (id)alloc {
@throw [NSException exceptionWithName:@"單例模式的規(guī)則"
reason:@"禁止使用alloc自己創(chuàng)建這個對象,請使用+ singletonAlloc代替。"
userInfo:@{}];
return nil;
}
+ (id)singletonAlloc{
return [super alloc];
}
/**
* 原始方式:沒有使用,看看就行(另外創(chuàng)建,沒有拋出異常)
* @return 返回一個實(shí)例
*/
+ (Singleton *)singleton
{
static Singleton *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}