iOS全解4:KVC讲仰、KVO慕趴、通知/推送/信號(hào)量、Delegate/Protocol鄙陡、Singleton

面試系列:

一耙册、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關(guān)系圖.png


KVC詳解

  • 1珊佣、KVC原理

    1. KVC 訪問私有變量
    2. setter 原理分析
    3. getter 原理分析
    4. forKeyPath、 valueForKeyPath
    5. 異常處理: 賦值/取值披粟、正確性驗(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 原理分析:(賦值過程順序如下)
  1. 先找相關(guān)方法
    //1惠猿、-(void) setName
    //2羔砾、-(void) _setName
    //3、-(void) setIsName
    //與 -(void) _setIsName 無關(guān)

  2. 若沒有相關(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

  3. 方法或成員變量都不存在:
    使用setValue:forUndefinedKey:方法腹缩,拋出異常屿聋。


3、getter 原理分析(取值過程順序如下)
  1. 先找相關(guān)方法
    //1藏鹊、- getName
    //2润讥、- name

  2. 若沒有相關(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

  3. 方法或成員都不存在,使用 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è)新的子類重寫了被觀察屬性 keyPathsetter方法螺捐。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、基本使用

使用三步驟:

  1. 添加觀察者
  2. 實(shí)現(xiàn)監(jiān)聽方法
  3. 移除觀察者
KVO使用三步驟.png
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)用 :

  1. 保持線程同步
  2. 為線程加鎖氏义,等待執(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;
}




最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末玛迄,一起剝皮案震驚了整個(gè)濱河市由境,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌憔晒,老刑警劉巖藻肄,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蔑舞,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡嘹屯,警方通過查閱死者的電腦和手機(jī)攻询,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來州弟,“玉大人钧栖,你說我怎么就攤上這事∑畔瑁” “怎么了拯杠?”我有些...
    開封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)啃奴。 經(jīng)常有香客問我潭陪,道長(zhǎng),這世上最難降的妖魔是什么最蕾? 我笑而不...
    開封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任依溯,我火速辦了婚禮,結(jié)果婚禮上瘟则,老公的妹妹穿的比我還像新娘黎炉。我一直安慰自己,他們只是感情好醋拧,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開白布慷嗜。 她就那樣靜靜地躺著,像睡著了一般丹壕。 火紅的嫁衣襯著肌膚如雪庆械。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天雀费,我揣著相機(jī)與錄音干奢,去河邊找鬼。 笑死盏袄,一個(gè)胖子當(dāng)著我的面吹牛忿峻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辕羽,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼逛尚,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了刁愿?” 一聲冷哼從身側(cè)響起绰寞,我...
    開封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后滤钱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體觉壶,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年件缸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了铜靶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡他炊,死狀恐怖争剿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情痊末,我是刑警寧澤蚕苇,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站凿叠,受9級(jí)特大地震影響涩笤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜幔嫂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一辆它、第九天 我趴在偏房一處隱蔽的房頂上張望誊薄。 院中可真熱鬧履恩,春花似錦、人聲如沸呢蔫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽片吊。三九已至绽昏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間俏脊,已是汗流浹背全谤。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留爷贫,地道東北人认然。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像漫萄,于是被迫代替她去往敵國和親卷员。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • 原創(chuàng):知識(shí)點(diǎn)總結(jié)性文章創(chuàng)作不易腾务,請(qǐng)珍惜毕骡,之后會(huì)持續(xù)更新,不斷完善個(gè)人比較喜歡做筆記和寫總結(jié),畢竟好記性不如爛筆頭哈...
    時(shí)光啊混蛋_97boy閱讀 680評(píng)論 0 2
  • KVC定義 KVC(Key-value coding)鍵值編碼未巫,就是指iOS的開發(fā)中窿撬,可以允許開發(fā)者通過Key名直...
    SheIsMySin_72e7閱讀 377評(píng)論 0 0
  • KVC KVC定義 KVC(Key-value coding)鍵值編碼,就是指iOS的開發(fā)中叙凡,可以允許開發(fā)者通過K...
    jackyshan閱讀 51,855評(píng)論 9 200
  • KVC KVC定義 KVC(Key-value coding)鍵值編碼尤仍,就是指iOS的開發(fā)中,可以允許開發(fā)者通過K...
    戀空K閱讀 721評(píng)論 0 2
  • 1. KVC 1.0 KVC的使用 LGStudent.h LGPerson.h 我們?cè)谄綍r(shí)一般使用LGPerso...
    Jeffery_zc閱讀 456評(píng)論 1 1