iOS全解4:KVC、KVO、通知/推送/信號量、Delegate/Protocol、Singleton

面試系列:

一、KVC

全稱是Key-value coding,鍵值編碼。使用字符串來標(biāo)識屬性,是間接訪問對象的屬性, 而不是直接通過調(diào)用存取方法(Setter、Getter方法)訪問。可以在運(yùn)行時動態(tài)訪問和修改對象的屬性。
KVC的方法定義在: Foundation/NSKeyValueCoding中。

特點(diǎ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集合代理對象

  • 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 原理分析:(賦值過程順序如下)
  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)拋出一個異常,未定義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)拋出一個異常,未定義key
    2.2 YES:繼續(xù)找相關(guān)變量
    //1、_name
    //2、_isName
    //3、name
    //4、isName

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

使用三步驟:

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

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




最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 原創(chuàng):知識點(diǎn)總結(jié)性文章創(chuàng)作不易,請珍惜,之后會持續(xù)更新,不斷完善個人比較喜歡做筆記和寫總結(jié),畢竟好記性不如爛筆頭哈...
    時光啊混蛋_97boy閱讀 907評論 0 2
  • KVC定義 KVC(Key-value coding)鍵值編碼,就是指iOS的開發(fā)中,可以允許開發(fā)者通過Key名直...
    SheIsMySin_72e7閱讀 423評論 0 0
  • KVC KVC定義 KVC(Key-value coding)鍵值編碼,就是指iOS的開發(fā)中,可以允許開發(fā)者通過K...
    jackyshan閱讀 52,318評論 9 198
  • KVC KVC定義 KVC(Key-value coding)鍵值編碼,就是指iOS的開發(fā)中,可以允許開發(fā)者通過K...
    戀空K閱讀 832評論 0 2
  • 1. KVC 1.0 KVC的使用 LGStudent.h LGPerson.h 我們在平時一般使用LGPerso...
    Jeffery_zc閱讀 616評論 1 1

友情鏈接更多精彩內(nèi)容