面試系列:
- iOS面試全解1:基礎(chǔ)/內(nèi)存管理/Block/GCD
- iOS面試全解2:Runloop
- iOS面試全解3:Runtime
-
iOS面試全解4:KVC/KVO冕房、通知/推送/信號(hào)量、Delegate/Protocol趁矾、Singleton(當(dāng)前位置)
一耙册、KVC
全稱是Key-value coding
,鍵值編碼毫捣。使用字符串來標(biāo)識(shí)屬性详拙,是間接訪問
對(duì)象的屬性, 而不是直接通過調(diào)用存取方法(Setter蔓同、Getter方法)訪問饶辙。可以在運(yùn)行時(shí)
動(dòng)態(tài)訪問和修改對(duì)象的屬性牌柄。
KVC的方法定義在: Foundation/NSKeyValueCoding
中畸悬。
特點(diǎn): 可以簡(jiǎn)化程序代碼侧甫。
KVC詳解
-
1珊佣、KVC原理
- KVC 訪問私有變量
- setter 原理分析
- getter 原理分析
- forKeyPath、 valueForKeyPath
- 異常處理: 賦值/取值披粟、正確性驗(yàn)證
2咒锻、KVC與字典
3、KVC的消息傳遞
4守屉、KVC容器操作
5惑艇、KVC集合代理對(duì)象
6、KVC的應(yīng)用
一、KVC原理
1滨巴、 KVC 訪問私有變量
1.1 能夠訪問私有成員變量(給對(duì)象的私有成員進(jìn)行:取值/賦值 )
1.2 對(duì)數(shù)值和結(jié)構(gòu)體型的屬性進(jìn)行的 打包/解包 處理
- valueForUndefinedKey
- setValue:forUndefinedKey:
myName 思灌、_myName
myAge、_myAge
KVC常用的方法
為對(duì)象的屬性:取值恭取、賦值
# 賦值方法
-(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 是對(duì)更“深層”的對(duì)象進(jìn)行訪問泰偿。如數(shù)組的某個(gè)元素,對(duì)象的某個(gè)屬性蜈垮。如:
[myModel setValue:@"beijing" forKeyPath:@"address.city"];
# 返回所有對(duì)象的name屬性值
NSArray *names = [array valueForKeyPath:@"name"];
// 通過keyPath取值
? key:?jiǎn)螌釉L問
? keyPath:可以多層訪問
? key:@"age"
? keyPath:@"age"
? keyPath:@"student.age"
? keyPath:@"person.student.age"
注意
:setter耗跛、getter:會(huì)按照 _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)拋出一個(gè)異常蜒茄,未定義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)拋出一個(gè)異常盘寡,未定義key
2.2 YES:繼續(xù)找相關(guān)變量
//1楚殿、_name
//2、_isName
//3竿痰、name
//4脆粥、isName
方法或成員都不存在,使用
valueForUndefinedKey:
方法影涉,拋出異常
4变隔、setValue:forKeyPath、 valueForKeyPath
尋找多級(jí)屬性(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)了就會(huì)根據(jù)實(shí)現(xiàn)方法里面的自定義邏輯返回NO或者YES,如果沒有實(shí)現(xiàn)這個(gè)方法肌厨,則系統(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ā)送消息(遍歷所有成員 長(zhǎng)度)
# 給成員 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
讀取集合中每個(gè)元素的鍵路徑指定的屬性,放在一個(gè)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集合代理對(duì)象
-
個(gè)人理解(一對(duì)多的關(guān)系:通過一個(gè)key帖渠,對(duì)應(yīng)多個(gè)方法)
? 對(duì)象的屬性可以是一對(duì)一的,也可以是一對(duì)多的竭宰。一對(duì)多的屬性要么是有序的(數(shù)組)空郊,要么是無序的(集合)。
? 屬性的一對(duì)多關(guān)系其實(shí)就是一種對(duì)容器類的映射切揭。
? 不可變的有序容器屬性(NSArray)和無序容器屬性(NSSet)一般可以使用valueForKey:來獲取狞甚。
? 如果有一個(gè)名為numbers的數(shù)組屬性,我們可以使用valueForKey:@"numbers"來獲取廓旬,這個(gè)是沒問題的哼审,但KVC還能使用更靈活的方式管理集合≡斜——那就是:集合代理對(duì)象(變量 variable)
-(void)valueForKey 有如下的搜索規(guī)則:
1涩盾、按順序搜索 getKey、key巩步、isKey旁赊,第一個(gè)被找到的會(huì)用作返回桦踊。
2椅野、countOf<Key>、objectIn<Key>AtIndex 與<key>AtIndexes其中之一,這個(gè)組合會(huì)使KVC返回一個(gè)代理數(shù)組竟闪。
3离福、countOf<Key>、enumeratorOf<Key>炼蛤、memberOfVar妖爷。這個(gè)組合會(huì)使KVC返回一個(gè)代理集合。
4理朋、名為_val絮识、_isVar、var嗽上、isVar的實(shí)例變量次舌。到這一步時(shí),KVC會(huì)直接訪問實(shí)例變量兽愤,而這種訪問操作破壞了封裝性彼念,我們應(yīng)該盡量避免,這可以通過重寫+(Bool)accessInstanceVariablesDirectly返回NO來避免這種行為浅萧。
6逐沙、KVC的應(yīng)用
6.1、動(dòng)態(tài)地取值和賦值
6.2洼畅、用KVC來訪問和修改私有變量
對(duì)于類里的私有屬性吩案,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延伸(自動(dòng)銷毀機(jī)制、Blocks何暇、YY封裝KVO)
1陶夜、概念
全稱(Key-Value Observing
)鍵值觀察
允許對(duì)象通知其他對(duì)象屬性的更改。它對(duì)應(yīng)用程序中模型 Model
和控制器層 VC
之間的通信特別有用裆站。 (在OS X中条辟,控制器層綁定技術(shù)嚴(yán)重依賴于鍵值觀察黔夭。)控制器對(duì)象通常觀察模型對(duì)象的屬性,視圖對(duì)象通過控制器觀察模型對(duì)象的屬性羽嫡。然而本姥,另外,模型對(duì)象可以觀察其他模型對(duì)象(通常用于確定從屬值何時(shí)改變)或甚至自身(再次確定從屬值何時(shí)改變)杭棵。
原理分析:
1. 動(dòng)態(tài)生成一個(gè)繼承自原類的新類
2. 使用Person的分類婚惫,重寫新類的set方法
3. 修改isa指針的指向。將self的isa指針指向新類,也就是為了在調(diào)用setter方法的時(shí)候,會(huì)去新類中查找魂爪。
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):觀察屬性,一對(duì)一關(guān)系和多對(duì)多關(guān)系浇衬。
一個(gè)簡(jiǎn)單的例子:假設(shè)Person對(duì)象與Account賬戶對(duì)象交互懒构。 Person的實(shí)例要知道Account實(shí)例的某些方面何時(shí)發(fā)生變化,例如余額或利率耘擂。
- 一個(gè)對(duì)象的一個(gè)屬性胆剧、多個(gè)屬性;
- 多個(gè)對(duì)象的一個(gè)屬性醉冤、多個(gè)屬性秩霍;
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)觀察某對(duì)象 People
時(shí)铃绒,KVO 機(jī)制動(dòng)態(tài)創(chuàng)建一個(gè)對(duì)象people當(dāng)前類的子類,并為這個(gè)新的子類重寫了被觀察屬性 keyPath
的 setter
方法螺捐。setter 方法隨后負(fù)責(zé)通知觀察對(duì)象屬性的改變狀況颠悬。
Apple 使用了isa-swizzling
來實(shí)現(xiàn) KVO 。當(dāng)觀察對(duì)象people時(shí)定血,KVO機(jī)制動(dòng)態(tài)創(chuàng)建一個(gè)新的名為:NSKVONotifying_People
的新類赔癌,該類繼承自對(duì)象People
的本類,且 KVO 為 NSKVONotifying_People 重寫觀察屬性的 setter 方法澜沟,setter 方法會(huì)負(fù)責(zé)在調(diào)用原 setter 方法之前和之后灾票,通知所有觀察對(duì)象屬性值的更改情況。
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 的兩個(gè)方法:
-
willChangeValueForKey:
被觀察屬性發(fā)生改變之前被調(diào)用班缰,通知即將改變贤壁、 -
didChangeValueForKey:
被觀察屬性發(fā)生改變之后被調(diào)用悼枢,通知已經(jīng)變更埠忘。
在存取數(shù)值的前后分別調(diào)用 這2 個(gè)方法,且重寫觀察屬性的setter 方法這種繼承方式的注入是在運(yùn)行時(shí)
而不是編譯時(shí)實(shí)現(xiàn)的馒索。
手動(dòng)調(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)前對(duì)象的自動(dòng)調(diào)用過程
莹妒,也就是由上面兩個(gè)方法發(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;
}
分析觀察者的整個(gè)過程:
1、未被觀察的屬性,直接找到屬性的 setter/getter
方法 賦值/取值
2享潜、被觀察的屬性,會(huì)動(dòng)態(tài)創(chuàng)建一個(gè)子類 NSKVONotifying_object
塞俱,然后添加一些方法奕剃,重寫
對(duì)應(yīng)屬性的 setter/getter 方法
,觀察子類屬性的變化器一,并通知
給子類
*isa
--> subClass
--> superClass
--> mateClass
... --> rootClass
(先找子類方法课锌,再找父類方法,再找元類方法祈秕,根類方法 根類的元類就是它自己渺贤,元類最后指向自己)
-
TZPerson:
isa
-->NSKVONotifiying_TZPerson
-->supclass TZPerson
- 添加觀察后:動(dòng)態(tài)去創(chuàng)建一個(gè)新類,并添加一些方法/屬性请毛,
繼承了原類
(移除觀察者后志鞍,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自定義
- 動(dòng)態(tài)創(chuàng)建一個(gè)類
// 1. 拼接子類名 NSKVONotifying_object
// 2. 創(chuàng)建并注冊(cè)類- class 創(chuàng)建并注冊(cè)類
- class 添加一些方法
- class (重寫屬性的 setter 方法)
- 方法反射述雾,添加一個(gè)方法
- zm_setter
- 添加析構(gòu)方法 - dealloc
- return newClass;
- 修改isa的指向
- 關(guān)聯(lián)方法
4、KVO延伸(自動(dòng)銷毀機(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是否會(huì)對(duì)一個(gè) 變量 進(jìn)行通知鳍征?
答:不會(huì)黍翎,只觀察對(duì)象的屬性。
三艳丛、通知/信號(hào)量
1匣掸、通知:NSNotification
目前分為四個(gè)推送:
1.用戶推送:NSNotificationCenter
2.本地推送:UILocalNotification(狀態(tài)欄提示:例如鬧鐘趟紊、掛號(hào)排隊(duì))
3.遠(yuǎn)程推送:RemoteNotifications
4.地理位置推送:必須配合CLLocation使用
過程:注冊(cè)、接收碰酝、處理
有以下特點(diǎn):
- 一對(duì)一 或 一對(duì)多
- 消息的
發(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纹磺、信號(hào)量:dispatch_semaphore
dispatch_semaphore
是信號(hào)量帖烘,但當(dāng)信號(hào)總量設(shè)為 1 時(shí)也可以當(dāng)作鎖來。在沒有等待情況出現(xiàn)時(shí)橄杨,它的性能比 pthread_mutex 還要高秘症,但一旦有等待情況出現(xiàn)時(shí),性能就會(huì)下降許多式矫。相對(duì)于 OSSpinLock 來說乡摹,它的優(yōu)勢(shì)在于等待時(shí)不會(huì)消耗 CPU 資源。對(duì)磁盤緩存來說衷佃,它比較合適趟卸。
一般在GCD職工使用,我理解的dispatch_semaphore有兩個(gè)主要應(yīng)用 :
- 保持線程同步
- 為線程加鎖氏义,等待執(zhí)行任務(wù)完成后锄列,通知后面的任務(wù)執(zhí)行
四、代理協(xié)議:Delegate/Protocol
sender: 消息的發(fā)送者
receiver:消息的接收者
delegate:代理者
Delegate 代理
代理的目的是改變或傳遞控制鏈惯悠×谟剩可以減少框架復(fù)雜度。
允許一個(gè)類在某些特定時(shí)刻通知到其他類克婶,而不需要獲取到那些類的指針筒严。
可以減少框架復(fù)雜度。
消息的發(fā)送者(sender
)告知接收者(receiver
)某個(gè)事件將要發(fā)生情萤,delegate
同意鸭蛙,然后發(fā)送者響應(yīng)事件:發(fā)送者 委托 接收者 去做某件事。
delegate機(jī)制使得接收者可以改變發(fā)送者的行為筋岛。
通常發(fā)送者和接收者的關(guān)系是直接的 一對(duì)一
的關(guān)系娶视。
IBOutlet可以為weak,NSString為copy,Delegate一般為weak肪获。
Protocol 小結(jié):
類對(duì)象遵守了Protocol
寝凌,那么該類就有了Protocol里面聲明的方法。類必須實(shí)現(xiàn)@required
方法孝赫、可選實(shí)現(xiàn)@optional
方法较木,具體怎么實(shí)現(xiàn)由該類自行決定,Protocol不過問青柄。
五伐债、單例:Singleton
1、單例模式的三個(gè)要點(diǎn):
1) 某個(gè)類 只能有一個(gè)實(shí)例(另外創(chuàng)建必須crash拋出異常提示)刹前;
2) 它必須 自行創(chuàng)建這個(gè)實(shí)例泳赋;
3) 它必須自行 向整個(gè)系統(tǒng)提供這個(gè)實(shí)例雌桑。
2喇喉、單例模式的優(yōu)點(diǎn):
1.實(shí)例控制:Singleton 會(huì)阻止其他對(duì)象實(shí)例化其自己的 Singleton 對(duì)象的副本,從而 確保所有對(duì)象都訪問唯一實(shí)例校坑。
2.靈活性:因?yàn)轭惪刂屏藢?shí)例化過程拣技,所以類可以更加靈活修改實(shí)例化過程
一個(gè)共享的實(shí)例,該實(shí)例只會(huì)被創(chuàng)建一次耍目。
**該方法有很多優(yōu)勢(shì): **
1膏斤、線程安全
2、很好滿足靜態(tài)分析器要求
3邪驮、和自動(dòng)引用計(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這個(gè)項(xiàng)目喻粹。
強(qiáng)迫每個(gè)人都使用+ singletonAlloc對(duì)團(tuán)隊(duì)有好處蟆融。
*/
@interface Singleton : NSObject
+ (id)singletonAlloc;
@end
#import "Singleton.h"
@implementation Singleton
/**
* 覆蓋這個(gè)方法是阻止其他程序員編碼“[[Singleton alloc]init]”。
你永遠(yuǎn)不應(yīng)該使用alloc自己守呜。請(qǐng)寫+實(shí)例在自己的子類型酥。
請(qǐng)參閱類方法+ Singleton.h實(shí)例。
*/
+ (id)alloc {
@throw [NSException exceptionWithName:@"單例模式的規(guī)則"
reason:@"禁止使用alloc自己創(chuàng)建這個(gè)對(duì)象,請(qǐng)使用+ singletonAlloc代替查乒。"
userInfo:@{}];
return nil;
}
+ (id)singletonAlloc{
return [super alloc];
}
/**
* 原始方式:沒有使用弥喉,看看就行(另外創(chuàng)建,沒有拋出異常)
* @return 返回一個(gè)實(shí)例
*/
+ (Singleton *)singleton
{
static Singleton *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}