OC 底層(KVC淮腾、KVO糟需、Delegate、Category谷朝、Extension洲押、通知)

目錄

1.KVC
2.KVO
3.通知
4.代理、委托圆凰、協(xié)議
5.Block杈帐、KVO、通知专钉、代理之間的區(qū)別
6.分類 Category 和類擴展 Extension
7.類方法挑童、實例方法、構(gòu)造方法

1.KVC

概念:KVC(Key-Value Coding)鍵值編碼跃须,是一種可以直接通過字符串類型的屬性名 key 來訪問或賦值某個類屬性的機制站叼,而不是通過調(diào)用 Setter、Getter 方法訪問菇民。這樣就可以在運行時動態(tài)地訪問和修改對象的屬性尽楔,而不是在編譯時確定投储,這也是 iOS 開發(fā)中的黑魔法之一。它是利用 NSKeyValueCoding 非正式協(xié)議實現(xiàn)的一種機制翔试,對象采用這種機制來提供對其屬性的間接訪問轻要。

說明:

1.寫下 KVC 代碼并點擊跟進 setValue 會發(fā)現(xiàn) NSKeyValueCoding 是在 Foundation 框架下,KVC 通過對 NSObject 的擴展來實現(xiàn)的垦缅,所有繼承了 NSObject 的類可以使用 KVC冲泥。

2.NSArray、NSDictionary壁涎、NSMutableDictionary凡恍、NSOrderedSet、NSSet 等也遵守 KVC 協(xié)議怔球,除少數(shù)類型(結(jié)構(gòu)體)以外都可以使用 KVC嚼酝。

3.KVC 既支持帶有對象值的屬性,也支持基本數(shù)據(jù)類型和結(jié)構(gòu)竟坛,基本數(shù)據(jù)類型會被自動封裝和解裝闽巩,比如 KVC 的 keyPath 可以是屬性、實例變量担汤、成員變量等涎跨。

相關(guān)鏈接:https://juejin.cn/post/6844904086744104968

1.KVC 常用 API 方法

1.setValue:(id)value forKey:(NSString *)key 和 setValue:(id)value forKeyPath:(NSString *)keyPath 的區(qū)別:key 是直接根據(jù)屬性的名字設(shè)置,如果按路徑找會報錯崭歧。keyPath 相當(dāng)于根據(jù)路徑去尋找屬性隅很,一層一層往下找。
2.(id)valueForKey:(NSString *)key 和 (id)valueForKeyPath:(NSString *)keyPath 的區(qū)別同理率碾。

//通過 Key 讀取和存儲
- (nullable id)valueForKey:(NSString *)key;//直接通過Key來取值
- (void)setValue:(nullable id)value forKey:(NSString *)key;//通過Key來設(shè)值
//通過 keyPath 讀取和存儲
- (nullable id)valueForKeyPath:(NSString *)keyPath;//通過KeyPath來取值           
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;//通過KeyPath來設(shè)值  
//默認(rèn)返回YES叔营,表示如果沒有找到Set<Key>方法的話,會按照_key所宰,_iskey绒尊,key,iskey的順序搜索成員仔粥,設(shè)置成NO就不這樣搜索
+ (BOOL)accessInstanceVariablesDirectly;

// KVC提供屬性值正確性驗證的API,它可以用來檢查set的值是否正確,為不正確的值做一個替換值或者拒絕設(shè)置新值并返回錯誤原因
- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;

// 這是集合操作的API,里面還有一系列這樣的API,如果屬性是一個NSMutableArray,那么可以用這個方法來返回
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;

// 如果Key不存在,且KVC無法搜索到任何和Key有關(guān)的字段或者屬性,則會調(diào)用這個方法垒酬,默認(rèn)是拋出異常
- (nullable id)valueForUndefinedKey:(NSString *)key;

// 和上一個方法一樣,但這個方法是設(shè)值
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;

//如果你在SetValue方法時給Value傳nil,則會調(diào)用這個方法
- (void)setNilValueForKey:(NSString *)key;

//輸入一組Key,返回該組Key對應(yīng)的Value,再轉(zhuǎn)成字典返回,用于將Model轉(zhuǎn)到字典
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;

2.KVC 賦(設(shè))值和取值流程 - 底層原理

賦值流程:

1.首先會去找類的 set 方法,如果找不到會去找 帶下劃線的 set 方法件炉。
2.如果都找不到,則會看 +(BOOL)accessInstanceVariablesDirectly 方法中的返回(默認(rèn)為 YES) 矮湘。
3.返回 YES 時:會按照 _key斟冕、_isKey、key缅阳、isKey 的順序找屬性賦值磕蛇,如果類中沒有上面的這些屬性則會調(diào)用 -(void)setValue:(id)value forUndefinedKey:(NSString *)key 方法(自己簡單實現(xiàn)一下比如打個 NSLog()景描,否則報錯);返回 NO 時:會直接調(diào)用 -(void)setValue:(id)value forUndefinedKey 方法 秀撇。

取值流程:

1.首先取值會按 getKey超棺、key、isKey呵燕、_key 的順序取棠绘。
2.找不到也會根據(jù) +(BOOL)accessInstanceVariablesDirectly 返回值。
3.返回 YES 時:會按照 _key再扭、_isKey氧苍、key、isKey 的順序找屬性取值泛范,如果類中沒有這些屬性則會調(diào)用 -(id)valueForUndefinedKey:(NSString *)key 方法(自己實現(xiàn)一下让虐,否則報錯);返回 NO 時:直接調(diào)用 -(id)valueForUndefinedKey罢荡。

KVC 賦(設(shè))值流程:首先會去找類的 set 方法赡突,如果找不到會去找?guī)聞澗€的 set 方法,如果都找不到則會看 +(BOOL)accessInstanceVariablesDirectly 方法中的返回区赵,默認(rèn)為YES惭缰。返回 YES 時會按照 _key、_isKey惧笛、key从媚、isKey 的順序找屬性賦值,如果類中沒有上面的這些屬性則會調(diào)用 -(void)setValue:(id)value forUndefinedKey:(NSString *)key 方法(自己實現(xiàn)一下患整,否則報錯)拜效,返回 NO 時會直接調(diào)用 -(void)setValue:(id)value forUndefinedKey 方法
KVC 取值流程:首先取值會按 getKey、key各谚、isKey紧憾、_key 的順序取,找不到也會根據(jù) +(BOOL)accessInstanceVariablesDirectly 返回值昌渤。返回 YES 時赴穗,會按照 _key、_isKey膀息、key般眉、isKey 的順序找屬性取值,如果類中沒有這些屬性則會調(diào)用 -(id)valueForUndefinedKey:(NSString *)key 方法(自己實現(xiàn)一下潜支,否則報錯)甸赃,返回 NO 時直接調(diào)用 -(id)valueForUndefinedKey
setValue:forKey: 的原理
valueForKey: 的原理
KVC 的底層實現(xiàn)

3.KVC 的作用

KVC 應(yīng)用場景:

動態(tài)地取值和設(shè)值
Model 和字典轉(zhuǎn)換
修改一些控件的內(nèi)部屬性
用 KVC 來訪問和修改私有變量
操作集合
用 KVC 實現(xiàn)高階消息傳遞
......

相關(guān)鏈接:https://juejin.cn/post/6844903602545229831

KVC 應(yīng)用場景
KVC 的作用

4.KVC 處理異常(nil、UndefinedKey)

KVC 中最常見的異常就是不小心使用了錯誤的 key 或者在設(shè)值中不小心傳遞了 nil 的值冗酿,KVC 中有專門的方法來處理這些異常埠对。

  • KVC 處理 nil 異常

通常情況下 KVC 不允許你要在調(diào)用 setValue:屬性值 forKey:(或者 keyPath)時對非對象傳遞一個 nil 的值络断。因為值類型是不能為 nil,如果你不小心傳了项玛,KVC 會調(diào)用 setNilValueForKey: 方法貌笨。這個方法默認(rèn)是拋出異常,所以一般而言最好還是重寫這個方法襟沮。

//重寫
- (void)setNilValueForKey:(NSString *)key {
    NSLog(@"不能將%@設(shè)成nil", key);
}
  • KVC 處理 UndefinedKey 異常

通常情況下 KVC 不允許你要在調(diào)用 setValue:屬性值 forKey:(或者 keyPath)時對不存在的 key 進行操作锥惋,不然會報錯 forUndefinedKey 發(fā)生崩潰,重寫 forUndefinedKey 方法避免崩潰臣嚣。

//重寫

- (id)valueForUndefinedKey:(NSString *)key {
    NSLog(@"出現(xiàn)異常净刮,該key不存在%@",key);
    return nil;
}

- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    NSLog(@"出現(xiàn)異常,該key不存在%@", key);
}

2.KVO

KVO(Key Value Observing)鍵值監(jiān)聽(鍵值觀察)硅则,可以用于監(jiān)聽某個對象屬性值的改變淹父,也可以監(jiān)聽集合對象的變化。KVO 和 NSNotification 都是 iOS 中觀察者模式的一種實現(xiàn)怎虫。

注意:

1.KVO 只能監(jiān)聽通過 set 方法修改的值暑认。
2.如果使用 KVO 監(jiān)聽某個對象的屬性,當(dāng)對象釋放之前一定要移除監(jiān)聽大审。
3.KVO 的定義都是對 NSObject 的擴展來實現(xiàn)的蘸际,Objective-C 中有個顯式的 NSKeyValueObserving 類別名,所以對于所有繼承了 NSObject 的類型都能使用KVO(一些純Swift類和結(jié)構(gòu)體是不支持 KVC 的徒扶,因為沒有繼承 NSObject)粮彤。
4.自動鍵值觀察是使用稱為 isa-swizzling 的技術(shù)實現(xiàn),該 isa 指針指向?qū)ο蟮念惤猓3忠粋€調(diào)度表导坟,該調(diào)度表主要包含指向類實現(xiàn)的方法的指針以及其他數(shù)據(jù)。當(dāng)觀察者注冊觀察對象的某屬性時圈澈,被觀察對象的 isa 指針被修改惫周,指向中間類而不是真正的類;因此 isa 指針的值不一定反映實例的實際類康栈。永遠(yuǎn)不要依賴 isa 指針來確定類成員身份递递,應(yīng)該使用該 class 方法來確定對象實例的類。
5.KVO 監(jiān)聽集合對象變化時啥么,需要通過 KVC 的 mutableArrayValueForKey: 等可變代理方法獲得集合代理對象登舞,并使用代理對象進行操作,當(dāng)代理對象的內(nèi)部對象發(fā)生改變時悬荣,會觸發(fā) KVO 的監(jiān)聽方法逊躁。集合對象包含 NSArray 和 NSSet。

相關(guān)鏈接:https://juejin.cn/post/6844904090569277447

KVO 舉例:要監(jiān)聽 Person 中的 age 屬性隅熙,我們就創(chuàng)建一個 observer 用來監(jiān)聽 age 的變化稽煤,當(dāng)我們 age 一旦發(fā)生改變,就會通知 observer囚戚。KVO 的作用:可以監(jiān)聽某個對象屬性的改變

1.KVO 的底層

KVO 的 keyPath 可以是屬性酵熙、實例變量、成員變量驰坊,KVO 的底層基于 runtime 機制實現(xiàn), 它的原理是修改 setter 方法,因此使用 KVO 必須調(diào)用 setter夺荒,若直接訪問屬性對象則沒有效果埂息。即當(dāng)一個類型為 ObjectA 的對象被添加了觀察后,系統(tǒng)會生成一個 NSKVONotifying_ObjectA 類舟扎,并將對象的 isa 指針指向新的類分飞,也就是說這個對象的類型發(fā)生了變化,這個類相比較于 ObjectA睹限,會重寫以下幾個方法:重寫 setter譬猫、重寫 class、重寫 dealloc羡疗、重寫 _isKVOA染服。

KVO 的內(nèi)部具體實現(xiàn)原理:

1.KVO 是基于 runtime 機制實現(xiàn)的,當(dāng)某個類的屬性對象第一次被觀察時叨恨,系統(tǒng)就會在運行期間動態(tài)地創(chuàng)建該類的一個派生類柳刮,在這個派生類中重寫基類的任何被觀察屬性的 setter 方法,派生類在被重寫的 setter 方法內(nèi)實現(xiàn)真正的通知機制痒钝。舉例:如果原類為 Person秉颗,那么生成的派生類名為 NSKVONotifying_Person。
2.每一個類中都有一個 isa 指針指向當(dāng)前類午乓,所有系統(tǒng)就是在當(dāng)一個類的對象第一次被觀察的時候站宗,系統(tǒng)就會偷偷將 isa 指針指向動態(tài)生成的派生類,從而在被監(jiān)聽屬性賦值時被執(zhí)行的是派生類的 setter 方法益愈。
3.鍵值觀察通知依賴于 NSObject 的兩個方法: willChangeValueForKey: 和 didChangevlueForKey: 梢灭;在一個被觀察屬性發(fā)生改變之前 willChangeValueForKey: 一定會被調(diào)用,這就會記錄舊的值蒸其。而當(dāng)改變發(fā)生后 didChangeValueForKey: 會被調(diào)用敏释,繼而 observeValueForKey:ofObject:change:context: 也會被調(diào)用。

總結(jié) KVO 的調(diào)用順序:調(diào)用 willChangeValueForKey: -> 調(diào)用原來的setter實現(xiàn) -> 調(diào)用didChangeValueForKey:(內(nèi)部會調(diào)用observer的observeValueForKeyPath:ofObject:change:context:方法)

注意:KVO 的這套實現(xiàn)機制中蘋果還偷偷重寫了 class 方法摸袁,讓我們誤認(rèn)為還是使用的當(dāng)前類钥顽,從而達到隱藏生成的派生類。

KVO 實現(xiàn)原理

底層原理-例子1:

底層原理-例子1
  • NSKVONotifying_ 類名中的方法
- (void)viewDidLoad {
    [super viewDidLoad];

    self.person = [[LZPerson alloc] init];
    [self.person addObserver:self forKeyPath:@"nickName" options:(NSKeyValueObservingOptionNew) context:NULL];

    unsigned int intCount;
    Method *methodList = class_copyMethodList(objc_getClass("NSKVONotifying_LZPerson"), &intCount);

    for (unsigned int intIndex=0; intIndex<intCount; intIndex++) {

        Method method = methodList[intIndex];
        NSLog(@"SEL:%@靠汁,IMP:%p",NSStringFromSelector(method_getName(method)), method_getImplementation(method));
    }
}

// 打印結(jié)果
SEL:setNickName:蜂大,IMP:0x18a5d8520
SEL:class闽铐,IMP:0x18a5d6fd4
SEL:dealloc,IMP:0x18a5d6d58
SEL:_isKVOA奶浦,IMP:0x18a5d6d50
底層原理-例子1

底層原理-例子2:

底層原理-例子2
底層原理-例子2
底層原理-例子2
底層原理-例子2
底層原理-例子2

2.KVO 的用法(監(jiān)聽過程)

KVO 使用三部曲:添加/注冊 KVO 監(jiān)聽兄墅、實現(xiàn)監(jiān)聽方法以接收屬性改變通知、 移除 KVO 監(jiān)聽澳叉。

1.調(diào)用方法 addObserver:forKeyPath:options:context: 給被觀察對象添加觀察者隙咸。

2.在觀察者類中實現(xiàn) observeValueForKeyPath:ofObject:change:context: 方法以接收屬性改變的通知消息。

3.當(dāng)觀察者不需要再監(jiān)聽時成洗,調(diào)用 removeObserver:forKeyPath: 方法將觀察者移除五督。需要注意的是,至少需要在觀察者銷毀之前瓶殃,調(diào)用此方法充包,否則可能會導(dǎo)致 Crash。

幾個參數(shù)解釋:

observer:觀察者碌燕,也就是 KVO 通知的訂閱者误证,訂閱著必須實現(xiàn)。

keyPath:描述將要觀察的屬性修壕,相對于被觀察者愈捅。

options:KVO 的一些屬性配置;有四個選項(NSKeyValueObservingOptionNew:change字典包括改變后的值慈鸠、NSKeyValueObservingOptionOld:change字典包括改變前的值蓝谨、NSKeyValueObservingOptionInitial:注冊后立刻觸發(fā)KVO通知、NSKeyValueObservingOptionPrior:值改變前是否也要通知(這個key決定了是否在改變前改變后通知兩次)

context: 上下文青团,這個會傳遞到訂閱著的函數(shù)中譬巫,用來區(qū)分消息,所以應(yīng)當(dāng)是不同的督笆。

KVO 實際場景舉例:https://juejin.cn/post/6844903972528979976

具體步驟如下

1.注冊觀察者

消息中的上下文指針 context 包含任意數(shù)據(jù)芦昔,這些數(shù)據(jù)將在相應(yīng)的更改通知中傳回給觀察者;可以指定 NULL 并完全依賴 keyPath 字符串來確定更改通知的來源娃肿,但這樣可能會導(dǎo)致父類由于不同原因也在觀察相同鍵路徑的對象時出現(xiàn)問題咕缎。

[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];

2.屬性變化通知

//在這里 change 這個字典保存了變更信息,具體是哪些信息取決于注冊時的 NSKeyValueObservingOptions
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if ([keyPath isEqualToString:@"name"]) {
        NSLog(@"%@",change);
    }
}

3.移除觀察者(要記得)

如果被觀察者是單例料扰,那么如果被觀察者所在界面銷毀時不移除觀察者會崩潰(被觀察者未釋放凭豪,值改變方法還要調(diào)用,但界面被釋放晒杈,這個方法找不到了所以崩潰)

[self.person removeObserver:self forKeyPath:@"name" context:NULL];

4.設(shè)置 context上下文嫂伞,區(qū)分通知來源

static void *PersonNickContext = &PersonNickContext;
static void *PersonNameContext = &PersonNameContext;

[self.person addObserver:self forKeyPath:@"nick" options:NSKeyValueObservingOptionNew context:PersonNickContext];
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:PersonNameContext];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if (context == PersonNickContext) {
        NSLog(@"nick:%@",change);
        return;
    }

    if (context == PersonNameContext){
        NSLog(@"name:%@",change);
        return;
    }
}

3.手動關(guān)閉 KVO、手動觸發(fā) KVO

  • +(BOOL)automaticallyNotifiesObserversForKey 手動關(guān)閉 KVO
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
    return NO;
}
  • willChangeValueForKey、didChangeValueForKey 手動觸發(fā) KVO
[LZPerson willChangeValueForKey:@"name"];
_name = name;
[LZPerson didChangeValueForKey:@"name"];
手動 KVO(禁用 KVO)舉例
手動 KVO(禁用 KVO)舉例

4.監(jiān)聽可變數(shù)組

self.person.dateArray = [NSMutableArray arrayWithCapacity:1];
[self.person addObserver:self forKeyPath:@"dateArray" options:(NSKeyValueObservingOptionNew) context:NULL];

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    // 這種寫法不能收到KVO通知,因為KVO基于KVC,訪問 集合對象 有三種不同的代理方法
    // if(self.person.dateArray.count == 0){
    //     [self.person.dateArray addObject:@"1"];
    // }
    // else{
    //     [self.person.dateArray removeObjectAtIndex:0];
    // }
    
    if(self.person.dateArray.count == 0){
        [[self.person mutableArrayValueForKey:@"dateArray"] addObject:@"1"];
    }
    else{
        [[self.person mutableArrayValueForKey:@"dateArray"] removeObjectAtIndex:0];
    }
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    NSLog(@"%@",change);
}

- (void)dealloc{
    [self.person removeObserver:self forKeyPath:@"dateArray"];
}
接著上面代碼說明
/* Possible values in the NSKeyValueChangeKindKey entry in change dictionaries. See the comments for -observeValueForKeyPath:ofObject:change:context: for more information.
*/
typedef NS_ENUM(NSUInteger, NSKeyValueChange) {
    NSKeyValueChangeSetting = 1,      //賦值
    NSKeyValueChangeInsertion = 2,    //插入
    NSKeyValueChangeRemoval = 3,      //移除
    NSKeyValueChangeReplacement = 4,  //替換
};

5.KVO 和線程

KVO和線程

6.其他說明

  • 通過 KVC 修改屬性會觸發(fā) KVO 么帖努?

會觸發(fā) KVO撰豺。

  • 直接修改成員變量會觸發(fā) KVO 么?

不會觸發(fā) KVO拼余,因為直接修改成員變量并沒有走 set 方法郑趁。

3.通知

iOS 中存在三種常見的事件通知方式:NSNofiticationCenter、KVO Notification 和 User Notifications姿搜,其中 User Notifications 是用戶通知也就是常說的推送,在這里只講解 NSNofiticationCenter捆憎。

相關(guān)鏈接:https://juejin.cn/post/6844903457984348167

1.NSNotification

NSNotification 是方便 NSNotificationCenter 廣播到其他對象時的封裝對象舅柜,簡單講即通知中心對通知調(diào)度表中的對象廣播時發(fā)送 NSNotification 對象。其中 NSNotification 對象包含:名稱躲惰、object致份、字典三個屬性。

名稱:是用來標(biāo)識通知的標(biāo)記础拨;
object:是要通知的對象可以為 nil氮块;
字典:用來存儲發(fā)送通知時附帶的信息也可以為 nil。

@interface NSNotification : NSObject <NSCopying, NSCoding>

@property (readonly, copy) NSNotificationName name;
@property (nullable, readonly, retain) id object;
@property (nullable, readonly, copy) NSDictionary *userInfo;

2.NSNotificationCenter

NSNotificationCenter 是類似一個廣播中心站诡宗,使用 defaultCenter 來獲取應(yīng)用中的通知中心滔蝉,它可以向應(yīng)用任何地方發(fā)送和接收通知。在通知中心注冊觀察者塔沃,發(fā)送者使用通知中心廣播時蝠引,以 NSNotification 的 name 和 object 來確定需要發(fā)送給哪個觀察者。為保證觀察者能接收到通知蛀柴,所以應(yīng)先向通知中心注冊觀察者螃概,接著再發(fā)送通知這樣才能在通知中心調(diào)度表中查找到相應(yīng)觀察者進行通知。

3.NSNotificationQueue

NSNotificationQueue 通知隊列鸽疾,用來管理多個通知的調(diào)用吊洼。通知隊列通常以先進先出(FIFO)順序維護通。NSNotificationQueue 就像一個緩沖池把一個個通知放進池子中制肮,使用特定方式通過 NSNotificationCenter 發(fā)送到相應(yīng)的觀察者冒窍,特定的方式即合并通知和異步通知。

4.NSNotificatinonCenter 實現(xiàn)原理

1.NSNotificatinonCenter 是使用觀察者模式來實現(xiàn)的用于跨層傳遞消息弄企,用來降低耦合度超燃。
2.NSNotificatinonCenter 用來管理通知,將觀察者注冊到 NSNotificatinonCenter 的通知調(diào)度表中拘领,然后發(fā)送通知時利用標(biāo)識符 name 和 object 識別出調(diào)度表中的觀察者意乓,然后調(diào)用相應(yīng)的觀察者的方法,即傳遞消息(在 Objective-C 中對象調(diào)用方法,就是傳遞消息届良,消息有 name 或者 selector笆凌,可以接受參數(shù),而且可能有返回值)士葫,如果是基于 block 創(chuàng)建的通知就調(diào)用 NSNotification 的 block乞而。

相關(guān)鏈接:https://juejin.cn/post/6844904080742023176

5.通知常見的使用方法

5.1簡單常用舉例(不帶參數(shù)):

1.注冊發(fā)送通知:[[NSNotificationCenter defaultCenter] postNotificationName:@"changeColor" object:self];
2.接收處添加觀察者: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeColor) name:@"changeColor" object:nil];
3.addObserver 處 remove 掉觀察者:- (void)dealloc{
[[NSNotificationCenter defaultCenter]removeObserver:self];
}

相關(guān)鏈接:http://t.zoukankan.com/-ios-p-10608973.html

  • 發(fā)送者

發(fā)送通知可使用以下方法發(fā)送通知:

- (void)postNotification:(NSNotification *)notification;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;
發(fā)送者
  • 觀察者

你可以使用以下兩種方式注冊觀察者:

- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;

- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_0);
    // The return value is retained by the system, and should be held onto by the caller in
    // order to remove the observer with removeObserver: later, to stop observation.
觀察者
  • 移除觀察者

在對象被釋放前需要移除掉觀察者,避免已經(jīng)被釋放的對象還接收到通知導(dǎo)致崩潰慢显。移除觀察者有兩種方式:

- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(nullable NSNotificationName)aName object:(nullable id)anObject;
__block __weak id<NSObject> observer = [[NSNotificationCenter defaultCenter] addObserverForName:kChangeNotifition object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) { NSLog(@"-[NSNotificationCenter addObserverForName:object:queue:usingBlock:]"); [[NSNotificationCenter defaultCenter] removeObserver:observer]; }];

5.2其他舉例(帶參數(shù)):

例子1:

[[NSNotificationCenter defaultCenter]postNotificationName:@"BBXOrderBillBackNotification" object:nil userInfo:@{@"index":@"3"}];//返回送客地圖刷新(訂單已經(jīng)支付情況)

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(BBXOrderBillBackNotificationAction:) name:@"BBXOrderBillBackNotification" object:nil];//從賬單頁面確認(rèn)完成直接回來(訂單已支付情況)爪模,不走賬單詳情(代支付)

//從賬單頁面確認(rèn)完成直接返回來刷新(不跳賬單詳情)
- (void)BBXOrderBillBackNotificationAction:(NSNotification *)sender {
    NSDictionary *dic  = sender.userInfo;
    NSString *index = dic[@"index"];
    if ([index isEqualToString:@"3"]) {
        [self refreshEvent:NO];//網(wǎng)絡(luò)請求,刷新界面
    }
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

例子2:

[[NSNotificationCenter defaultCenter] postNotificationName:@"GaodeNaviUpdateMapNaviPopupView" object:nil userInfo:@{@"orderViewModel":orderViewModel}];//通知訂單面板刷新

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(gaodeNaviUpdateMapNaviPopupView:) name:@"GaodeNaviUpdateMapNaviPopupView" object:nil];//導(dǎo)航中更換終點導(dǎo)航刷新訂單面板

//通知-高德導(dǎo)航中改變終點刷新訂單面板
- (void)gaodeNaviUpdateMapNaviPopupView:(NSNotification *)sender {
    NSDictionary *dic = sender.userInfo;
    OrderViewModel *orderViewModel = dic[@"orderViewModel"];
    [orderViewModel refreshPrice];
    self.mapNavigationPopupView.orderViewModel = orderViewModel;//更新數(shù)據(jù)
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
發(fā)送通知
添加觀察者
通知回調(diào)方法
移除通知

5.3通知其他寫法:

//普通寫法
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(p_showGuideView) name:@"showGuideView" object:nil];
//其他寫法
[[NSNotificationCenter defaultCenter] addObserverForName:@"updateSectionData" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
   [self reloadUI];
}];
通知其他寫法

4.代理荚藻、委托屋灌、協(xié)議

1.概念

通常所說的代理又稱委托代理,是 iOS 中常用的一種設(shè)計模式应狱,可以理解成一種代理機制共郭,這個代理機制由代理對象、委托者疾呻、協(xié)議三部分組成除嘹。

代理 Delegate:用代理對象代理被代理對象的行為(比如:找槍手代考),代理對象可以通過被代理對象的指針執(zhí)行被代理對象的行為岸蜗,與此同時代理對象可以對這些行為進行增強實現(xiàn)尉咕。很多iOS開發(fā)者認(rèn)為代理和委托是一個意思,其實二者是不同的兩種設(shè)計理念散吵。它是 iOS 開發(fā)中的一種重要的消息傳遞方式和設(shè)計模式龙考,例如 UITableView、UITextField矾睦、UISearchView 等都是用代理機制實現(xiàn)消息傳遞晦款。

委托:根據(jù)指定的協(xié)議,指定代理去完成什么功能枚冗。委托是一個對象保存另外一個對象的引用缓溅,被引用的對象實現(xiàn)了事先確定的協(xié)議,該協(xié)議用于將引用對象中的變化通知給被引用對象赁温。委托和委托方雙方的 property 聲明屬性都是 assign 而不是 retain坛怪,為了避免循環(huán)引用造成的內(nèi)存泄露。

協(xié)議 Protocol:一般被用來定義一套公用的方法接口股囊,別的類可以遵守這個協(xié)議使用這些方法袜匿,方法分為 required 和 optional 兩種,required 是必須實現(xiàn)的方法稚疹,optional 是可選的方法居灯,即用來指定代理雙方可以做什么,必須做什么。比如 tableViewdelegate 這個協(xié)議怪嫌,他定義了很多接口义锥,然后 VC 去遵循這個協(xié)議并去實現(xiàn)里面的接口,tableView 不能去實現(xiàn)里面的接口所以他委托 VC 代理去處理里面的接口岩灭。

總結(jié):

1.采用委托代理的好處在于:避免子類化帶來的過多的子類以及子類與父類的耦合拌倍、通過委托傳遞消息機制實現(xiàn)分層解耦。

2.委托代理機制是一種設(shè)計模式噪径,以 @protocol 形式體現(xiàn)柱恤,一般是一對一傳遞。設(shè)置代理屬性都使用 weak 以規(guī)避循環(huán)引用找爱,通常我們定義的指針默認(rèn)都是 __strong 類型的膨更,而屬性本質(zhì)上也是一個成員變量和 set、get 方法構(gòu)成的缴允,strong 類型的指針會造成強引用,必定會影響一個對象的生命周期珍德,這也就會形成循環(huán)引用练般,所以代理用 weak。

3.在我們寫項目時锈候,特別是主界面會隨著處理的邏輯越來越多它會越來越肥薄料,對于新項目來說 MVVM 設(shè)計模式是一種最好的選擇,但是對于一個已經(jīng)很復(fù)雜的項目來說泵琳,代理是很好的方式摄职,可以用代理給 ViewController 瘦身。

4.協(xié)議類似于 Java 和 C# 中的接口获列,一個類可以遵循多個協(xié)議谷市,一個協(xié)議也可以被多個類遵循(就像一個演員可以扮演多個角色,一個角色也可以由多個演員扮演)击孩。

5.協(xié)議的特點:協(xié)議是一組方法的集合迫悠,協(xié)議中只有方法的聲明,沒有方法的實現(xiàn)巩梢,因為這些方法是留給遵循該協(xié)議的類做出多態(tài)實現(xiàn)的方法创泄。毫無關(guān)系的類可以遵循相同的協(xié)議從而具有相同的行為,這是自然的括蝠,就如同超人鞠抑、鳥、飛機都會飛忌警,但是他們其實沒有什么必然的關(guān)聯(lián)搁拙,我們可以把飛這個行為定義到一個協(xié)議中。

相關(guān)鏈接:https://blog.csdn.net/qq_42376419/article/details/98884229
為什么代理屬性要用 weak 修飾:http://www.reibang.com/p/661a01405802

協(xié)議補充說明
協(xié)議補充說明
委托回調(diào)補充說明

2.三者的關(guān)系

通俗來說就是某個對象 A 把要做的事情委托給另一個對象 B 按照雙方約定的的協(xié)議去做,其中 A 稱作委托者感混,B 稱作是被委托者也就是代理端幼。

相關(guān)鏈接:
https://juejin.cn/post/6844903752554512391
http://www.cnblogs.com/36bian/p/5240517.html
http://blog.csdn.net/mad1989/article/details/8463460

  • 通俗的舉例說明:

三者角色比作:委托方 = 老板,代理方 = 員工弧满,協(xié)議 = 合同婆跑。那么委托方傳遞信息或者事件到代理方,代理方執(zhí)行相關(guān)操作庭呜。這個就可以理解成:老板把工作材料和工作內(nèi)容交給員工滑进,員工去干活。

代理對象募谎、委托者扶关、協(xié)議之間的關(guān)系
代理對象、委托者数冬、協(xié)議之間的關(guān)系
委托方通過某種方式把任務(wù)分派出去給代理方處理节槐,而兩者之間的聯(lián)系便是協(xié)議
代理對象、委托者拐纱、協(xié)議三者一起用的
代理對象和委托者對應(yīng)關(guān)系

3.工作分配

  • 委托需要做的工作有:

1.定義協(xié)議與方法
2.聲明委托變量
3.設(shè)置代理
4.通過委托變量調(diào)用委托方法

  • 代理需要做的工作有:

1.遵循協(xié)議
2.實現(xiàn)委托方法

實現(xiàn)代理需要注意什么
協(xié)議和代理的理解
  • 舉例說明

比如學(xué)生想要買一本專業(yè)書铜异,書店沒有這本書,自己又不直接去出版社秸架,于是學(xué)生就委托書店幫忙買書揍庄,書店就是學(xué)生的代理,學(xué)生就是委托者东抹。

委托者 - 學(xué)生 Student.h

#import <Foundation/Foundation.h>

//定義協(xié)議與方法
@protocol StudentBuyBookDelegate<NSObject>
-(void)buyBook:(NSString *)name price:(int)p;
@end

@interface Student : NSObject
//聲明委托變量
@property(nonatomic,retain)id<StudentBuyBookDelegate> stu_delegate;

-(void)wantBuy;

@end

委托者 - 學(xué)生 Student.m

#import "Student.h"

@implementation Student

-(void)wantBuy {
    
    NSLog(@"學(xué)生:我想買IOS開發(fā)的書");
    //通過委托變量調(diào)用委托方法
    [self.stu_delegate buyBook:@"IOS開發(fā)" price:50];
}

@end

代理 - 書店 BookShop.h

#import <Foundation/Foundation.h>
#import "Student.h"
//書店遵守StudentBuyBookDelegate的委托協(xié)議
@interface BookShop : NSObject<StudentBuyBookDelegate>

@end

代理 - 書店 BookShop.m

#import "BookShop.h"

@implementation BookShop

//書店實現(xiàn)協(xié)議的方法
-(void)buyBook:(NSString *)name price:(int)p {
    
    NSLog(@"我可以以%i元的價格把%@賣個你",p,name);
}

@end

在 ViewController.m 里面

Student *student =[[Student alloc]init];
    BookShop *bookshop = [[BookShop alloc]init];
    
    //學(xué)生設(shè)置代理蚂子,委托書店買書
    student.stu_delegate = bookshop;
    
    //判斷書店是否實現(xiàn)了協(xié)議,避免未實現(xiàn)帶來的崩潰
    if ([bookshop respondsToSelector:@selector(buyBook:price:)])
    {
        [student wantBuy];
    }

4.委托代理機制 - 實現(xiàn)原理

實現(xiàn)流程關(guān)系

5.協(xié)議&代理&委托簡單舉例

協(xié)議 protocol - demo:

代理 delegate - demo:

委托回調(diào) - demo:

5.Block缭黔、KVO食茎、通知、代理之間的區(qū)別

https://blog.csdn.net/dqjyong/article/details/7685933
http://www.reibang.com/p/f819abf40509
https://www.zybuluo.com/SanMao/note/125908

  • block 和代理的區(qū)別

相同點:block 和代理都是回調(diào)的方式馏谨,使用場景相同董瞻。

不同點:

1.block 集中代碼塊,而代理分散代碼塊田巴。所以 block 更適用于輕便钠糊、簡單的回調(diào),如網(wǎng)絡(luò)傳輸壹哺。 代理適用于公共接口較多的情況抄伍,這樣做也更易于解耦代碼架構(gòu)。

2.block 運行成本高管宵,block 需要將使用的數(shù)據(jù)從棧內(nèi)存拷貝到堆內(nèi)存截珍,如果是對象就是加計數(shù)攀甚,使用完或 block 置為 nil 后才消除。 代理只是保存了一個對象指針岗喉,直接回調(diào)秋度,并沒有額外消耗,相對 C 的函數(shù)指針钱床,只是多做了一個查表動作荚斯。

block 和代理實際場景選擇:比如 tableview 中 cell 自定義一個按鈕,這個按鈕的回調(diào)可以用 block 來實現(xiàn)查牌。比如bbx司機端地圖頁面中選中訂單氣泡彈起的訂單面板頁面可以通過代理來實現(xiàn)面板上面各個按鈕控件的回調(diào)如電話事期、滑條、刷新等纸颜,因為控件和需要回調(diào)操作比較多兽泣。

bbx司機端地圖頁面中選中訂單氣泡彈起的訂單面板頁面
  • 通知和代理的區(qū)別

1.效率:代理比通知高。
2.關(guān)聯(lián):代理是強關(guān)聯(lián)胁孙,委托和代理雙方互相知道唠倦。通知是弱關(guān)聯(lián),不需要知道是誰發(fā)涮较,也不需要知道是誰接收牵敷。
3.代理是一對一的關(guān)系,通知是一對多的關(guān)系法希。代理一般是行為需要別人來完成,通知是全局通知靶瘸。
4.代理要實現(xiàn)對多個類發(fā)出消息可以通過將代理者添加入集合類后遍歷苫亦,或通過消息轉(zhuǎn)發(fā)來實現(xiàn)。

其他說明:協(xié)議有控制鏈(has-a)的關(guān)系怨咪,通知沒有屋剑。分析下通知和代理的行為模式,簡單來說通知的話它可以一對多诗眨,一條消息可以發(fā)送給多個消息接受者唉匾;代理按我們的理解,到不是直接說不能一對多匠楚,比如我們知道的明星經(jīng)濟代理人巍膘,很多時候一個經(jīng)濟人負(fù)責(zé)好幾個明星的事務(wù),只是對于不同明星間代理的事物對象都是不一樣的芋簿,一一對應(yīng)峡懈,不可能說明天要處理A明星要一個發(fā)布會蠢壹,代理人發(fā)出處理發(fā)布會的消息后颈将,別稱B的發(fā)布會了敬鬓。但是通知就不一樣,他只關(guān)心發(fā)出通知涕滋,而不關(guān)心多少接收到感興趣要處理。因此控制鏈(has-a)從英語單詞大致可以看出萍倡,單一擁有和可控制的對應(yīng)關(guān)系楼咳。

通知和代理的區(qū)別
  • KVO和通知的區(qū)別

1.相同:都是一對多的關(guān)系。
2.不同:通知是需要被觀察者先主動發(fā)出通知雾狈,觀察者注冊監(jiān)聽再響應(yīng)廓潜,比 KVO 多了發(fā)送通知這一步。
3.監(jiān)聽范圍:KVO 只能用于監(jiān)聽對象屬性的變化箍邮,即監(jiān)聽一個值的變化茉帅。通知不局限于監(jiān)聽屬性的變化,還可以對多種多樣的狀態(tài)變化進行監(jiān)聽锭弊,監(jiān)聽范圍廣堪澎,使用更靈活,即可以監(jiān)聽任何你感興趣的東西味滞。
4.使用場景:KVO 的一般使用場景是監(jiān)聽數(shù)據(jù)變化樱蛤,通知是全局通知。
5.2.KVO 發(fā)出消息由系統(tǒng)控制剑鞍,通知由開發(fā)者控制昨凡。
6.KVO 自動記錄新舊值變化,通知只能記錄開發(fā)者傳遞的參數(shù)蚁署。

注意:KVO 和 通知 NSNotification 都是 iOS 中觀察者模式的一種實現(xiàn)便脊。

6.分類 Category 和類擴展 Extension

https://juejin.cn/post/6844904067987144711

6.1 分類 Category

Category

1.什么是 Category(分類)?

Category(分類)是 OC 2.0 添加的語言特性光戈,又叫類別等哪痰,能夠在不改變原來類內(nèi)容的基礎(chǔ)上為類增加一些方法,即主要作用是為已經(jīng)存在的類添加方法久妆。Category 可以做到在既不子類化晌杰,也不侵入一個類的源碼的情況下,為原有的類添加新的方法筷弦,從而實現(xiàn)擴展一個類或者分離一個類的目的肋演。在日常開發(fā)中我們常常使用 Category 為已有的類擴展功能。

說明:

1.繼承能為已有類增加新的方法烂琴,還能直接增加屬性爹殊,但繼承關(guān)系增加了不必要的代碼復(fù)雜度,在運行時奸绷,也無法與父類的原始方法進行區(qū)分边灭。所以我們可以優(yōu)先考慮使用自定義 Category。

2.Category 的特性是:可以在運行時階段動態(tài)地為已有類添加新行為健盒。 Category 是在運行時期間決定的绒瘦,而成員變量的內(nèi)存布局已經(jīng)在編譯階段確定好了称簿,如果在運行時階段添加成員變量的話,就會破壞原有類的內(nèi)存布局惰帽,從而造成可怕的后果憨降,所以 Category 無法添加成員變量。

3.Category 在運行期決議的该酗,除了為已經(jīng)存在的類添加方法之外 apple 還推薦了 category 的另外兩個使用場景:第一種:可以把類的實現(xiàn)分開在幾個不同的文件里面授药。這樣做有幾個顯而易見的好處,1)可以減少單個文件的體積呜魄;2)可以把不同的功能組織到不同的category里悔叽;3)可以由多個開發(fā)者共同完成一個類;4)可以按需加載想要的category 等等爵嗅。第二種:聲明私有方法娇澎。不過除了apple推薦的使用場景,廣大開發(fā)者腦洞大開睹晒,還衍生出了category的其他幾個使用場景:1).模擬多繼承趟庄,2).把framework的私有方法公開。

相關(guān)鏈接:
https://juejin.cn/post/6844903461260263431
http://tech.meituan.com/DiveIntoCategory.html

2. Category 的作用以及常見的應(yīng)用場景

  • 把類的不同實現(xiàn)方法分開到不同的文件里伪很。
  • 聲明私有方法戚啥。
  • 模擬多繼承。
  • 將 framework 私有方法公開化锉试。
Category 的作用以及常見的應(yīng)用場景
Category 的使用場合
Category 中都可以添加哪些內(nèi)容

3. Category 的簡單使用

  • 實例代碼(以文本框的占位符顏色來簡單講解)
然后在需要的控制器中去導(dǎo)入頭文件猫十,然后直接調(diào)用方法就可實現(xiàn)(打斷點處即是)
Category 的使用注意
Category 的優(yōu)缺點、特點呆盖、注意點

4. Category 的底層原理

https://juejin.cn/post/6844903896708562952

Category 的實現(xiàn)原理

5.關(guān)于 Category 的幾個提問

通過探索 Category 底層原理回答以下問題:

1.Category 是否可以添加方法拖云、屬性、成員變量絮短?Category 是否可以遵守 Protocol?

2.Category 的本質(zhì)是什么昨忆,在底層是怎么存儲的丁频?

3.Category 的實現(xiàn)原理是什么,Catagory 中的方法是如何調(diào)用到的邑贴?

4.Category 中是否有 Load 方法席里,load 方法是什么時候調(diào)用的?

5.Category 中 load拢驾、initialize 的區(qū)別奖磁?

相關(guān)鏈接:http://www.reibang.com/p/ecc9873a3d8e

  • 1.Category 能不能添加成員變量呢?如何給 Category 添加成員變量繁疤?

因為分類底層結(jié)構(gòu)的限制咖为,不能直接給 Category 添加成員變量秕狰,但是可以通過關(guān)聯(lián)對象間接實現(xiàn) Category 有成員變量的效果。傳送門:OC - Association 關(guān)聯(lián)對象

Category 不能添加成員變量
Category 不能添加成員變量
Category 不能添加成員變量
  • 2.為什么分類中屬性不會自動生成 setter躁染、getter 方法的實現(xiàn)鸣哀,不會生成成員變量,也不能添加成員變量吞彤?

因為類的內(nèi)存布局在編譯的時候會確定我衬,但是分類是在運行時才加載,在運行時Runtime會將分類的數(shù)據(jù)饰恕,合并到宿主類中挠羔。

  • 3.Category 能不能添加屬性(property)呢?
Category 允許添加屬性但無法使用
  • 4.Category 中有 +load 方法嗎埋嵌?+load 方法是什么時候調(diào)用的破加?+load 方法能繼承嗎?

分類中有 +load 方法莉恼;
+load 方法在 Runtime 加載類拌喉、分類的時候調(diào)用;
+load 方法可以繼承俐银,但是一般情況下不會手動去調(diào)用 +load 方法尿背,都是讓系統(tǒng)自動調(diào)用。

傳送門:OC - load 和 initialize

  • 5.Category 中 load捶惜、initialize 的區(qū)別田藐?

https://juejin.cn/post/7038890265028853791

Category 中 load 和 initialize 區(qū)別
Category 中 load 和 initialize 相關(guān)面試題
Category 中 load 和 initialize 小結(jié)
Category 中 load 和 initialize 小結(jié)
  • 6.為什么將以前的方法列表挪動到新的位置用 memmove 呢?

為了保證挪動數(shù)據(jù)的完整性吱七。而將分類的方法列表合并進來汽久,不用考慮被覆蓋的問題,所以用 memcpy 就好踊餐。

  • 7.為什么優(yōu)先調(diào)用最后編譯的分類的方法景醇?

attachCategories() 方法中,從所有未完成整合的分類取出分類的過程是倒序遍歷吝岭,最先訪問最后編譯的分類三痰。然后獲取該分類中的方法等列表,添加到二維數(shù)組中窜管,所以最后編譯的分類中的數(shù)據(jù)最先加到分類二維數(shù)組中散劫,最后插入到宿主類的方法列表前面。而消息傳遞過程中優(yōu)先查找宿主類中靠前的元素幕帆,找到同名方法就進行調(diào)用获搏,所以優(yōu)先調(diào)用最后編譯的分類的方法。

  • 8objc_class 結(jié)構(gòu)體中的 baseMethodList 和 methods 方法列表的區(qū)別失乾?

baseMethodList 基礎(chǔ)的方法列表是 ro 只讀的常熙,不可修改纬乍,可以看成是合并分類方法列表前的methods的拷貝;而 methods 是 rw 可讀寫的症概,將來運行時要合并分類方法列表蕾额。

6.2 擴展 Extension

Extension

1. 什么是 Extension(擴展)?

① Extension 有一種說法叫“匿名分類”彼城,因為它很像分類诅蝶,但沒有分類名,嚴(yán)格來說要叫類擴展募壕。
② Extension 的作用是將原來放在 .h 中的數(shù)據(jù)放到 .m 中去调炬,私有化,變成私有的聲明舱馅。
③ Extension 是在編譯的時候就將所有數(shù)據(jù)都合并到類中去了(編譯時決議)缰泡,而 Category 是在程序運行的時候通過 Runtime 機制將所有數(shù)據(jù)合并到類中去(運行時決議)。

說明:

1.Extension 有時候也被稱為匿名分類代嗤,看起來和 Category 有點相似但兩者實質(zhì)上是不同的東西棘钞。

2.Extension 是在編譯階段與該類同時編譯的,是類的一部分干毅。而且 Extension 中聲明的方法只能在該類的 @implementation 中實現(xiàn)宜猜,這也就意味著,你無法對系統(tǒng)的類(例如 NSString 類)使用 Extension硝逢。

3.Extension 不但可以聲明方法姨拥,還可以聲明成員變量,這是 Category 所做不到的渠鸽。

2. Extension 作用

① 聲明私有屬性
② 聲明私有方法
③ 聲明私有成員變量

3. Extension 特點

① 編譯時決議(在編譯的時候就將擴展的所有數(shù)據(jù)都合并到類中去了)
② 只以聲明的形式存在叫乌,多數(shù)情況下寄生于宿主類的 .m 中
③ 不能為系統(tǒng)類添加擴展

6.3 Category 與 Extension 區(qū)別

https://www.cnblogs.com/stevenwuzheng/p/8205321.html

Category 與 Extension 區(qū)別
Category 與 Extension 區(qū)別

7.類方法、實例方法徽缚、構(gòu)造方法

https://blog.csdn.net/lianai911/article/details/103400835
https://juejin.cn/post/6844903558744113160

類方法(靜態(tài)方法)

類方法:也稱靜態(tài)方法或者工廠方法憨奸,以 + 開頭,在 C++ 中指的是用 static 關(guān)鍵字修飾的方法凿试。類方法屬于整個類排宰,是屬于類本身的方法,不屬于類的某一個實例對象红省,不需要實例化類额各,用類名即可使用国觉,通過類方法將消息發(fā)送給類吧恃。在項目中工具類的封裝多用工廠方法調(diào)用,調(diào)用格式 [類名 類方法]麻诀。類方法也可以同樣傳參數(shù)進去痕寓,比如進行網(wǎng)絡(luò)請求時候傲醉,可以把網(wǎng)絡(luò)請求封裝,傳入不同的請求體即可呻率。

類方法優(yōu)點:
1.節(jié)約空間:因為調(diào)用類方法不需要創(chuàng)建對象硬毕,所以節(jié)約了空間。
2.提高效率:調(diào)用類方法不需要拐彎抹角礼仗,直接找到類吐咳。

注意:
1.類方法不可以使用實例變量,類方法可以使用 self元践,因為 self 不是實例變量韭脊。
2.在類方法中不能直接調(diào)用類的屬性,不可以調(diào)用實例方法单旁,但是類方法可以通過創(chuàng)建對象來訪問實例方法沪羔。
3.類方法創(chuàng)造的對象要不要用 release 釋放:不需要,因為這個對象被放到自動釋放池中象浑,在 ARC 中已經(jīng)不需要考慮這個問題了蔫饰。

類方法說明
類方法說明
類方法舉例

實例方法(對象方法)

實例方法:也稱對象方法,以 - 開頭愉豺,在 C++ 中指的是不用 static 關(guān)鍵字修飾的方法篓吁,它屬于類的某一個或某幾個實例對象,即類對象必須實例化后才可以使用的方法粒氧,調(diào)用方式 [對象名 對象方法]越除。

對象方法聲明
對象方法舉例

構(gòu)造方法

構(gòu)造方法:初始化對象的方法。一般情況下在 OC 當(dāng)中創(chuàng)建1個對象分為兩部分(new 做的事):1.alloc:分配內(nèi)存空間外盯,2.init:初始化對象摘盆。構(gòu)造方法分為系統(tǒng)自帶和自定義構(gòu)造方法,如果是系統(tǒng)自帶的構(gòu)造方法需要重寫父類中自帶的構(gòu)造方法比如 init饱苟。如果是自定義構(gòu)造方法孩擂,屬于對象方法那么以 - 號開頭,返回值一般為 id 或者 instancetype 類型箱熬,方法名一般以 init 開頭类垦。

構(gòu)造方法的作用是:

1.用作初始化對象的成員變量
2.把 C 語言指針初始化為 NULL
3.把 OC 對象初始化為 nil
4.把基本數(shù)據(jù)類型初始化為0

相關(guān)鏈接:
http://t.zoukankan.com/sleepingSun-p-5123931.html
http://www.reibang.com/p/482290b19b7a

  • 系統(tǒng)構(gòu)造方法舉例:
- (instancetype)init {
  self = [super init];
  if (self) {     

  }
  return self;
}
  • 自定義構(gòu)造方法舉例:
@property int age;//年齡
@property NSString *name;//姓名

//自定義構(gòu)造方法,在初始化的時候為屬性"年齡"和"姓名"賦值
- (instancetype)initWithAge:(int)age andName:(NSString *)name;

//實現(xiàn)自定義構(gòu)造函數(shù)城须,在初始化的時候為屬性賦值
- (id)initWithAge:(int)age andName:(NSString *)name {
  if (self = [super init]) {
    _age = age;
    _name = name;
  }
  return self;
}

iOS 類方法與實例方法區(qū)別

類方法與實例方法區(qū)別

https://blog.csdn.net/C_philadd/article/details/114526484

類方法與實例方法區(qū)別
  • 類方法

1.類方法以+號開頭蚤认。
2.類方法是屬于類的,只能由類來調(diào)用糕伐,直接[類名 類方法名]調(diào)用砰琢,對象不能調(diào)用。
3.類方法不能訪問實例變量(成員變量)。
4.類方法中不能直接調(diào)用對象方法陪汽,要想調(diào)用對象方法必須創(chuàng)建或傳入對象训唱。

  • 實例方法

1.對象方法以-號開頭。
2.對象方法是屬于對象的挚冤,只能用對象來調(diào)用况增,創(chuàng)建對象后[對象名 對象方法]調(diào)用,沒有對象該方法無法執(zhí)行训挡。
3.對象方法能訪問實例變量(成員變量)澳骤。
4.對象方法中可以調(diào)用當(dāng)前對象的對象方法,也可以調(diào)用其他對象的對象方法澜薄。
5.對象方法中不可以調(diào)用類方法宴凉。

  • 其他說明

1.當(dāng)不需要訪問成員變量的時候,盡量用類方法表悬,并且類方法執(zhí)行效率更高弥锄。
2.類方法和實例方法可同名:類方法存儲在元類里,元類的結(jié)構(gòu)里有存儲類方法列表的數(shù)據(jù)結(jié)構(gòu)蟆沫,所以類方法和對象方法可以同名籽暇,并且也遵循 OC 的消息轉(zhuǎn)發(fā)機制。
3.舉例說明:Person *p1 = [Person new]饭庞; [Person 方法名] 就是類方法戒悠,[p1 方法名] 就是對象方法。

構(gòu)造方法和實例方法的區(qū)別

1.iOS 中構(gòu)造方法是指和類同名舟山,用于構(gòu)造對象(即生成對象)的方法绸狐。而實例方法指的是在實例生成之后,實例調(diào)用的方法累盗。
2.構(gòu)造方法 -> 構(gòu)造實例 -> 實例產(chǎn)生 -> 調(diào)用實例方法寒矿。詳細(xì)來說就是:類調(diào)用構(gòu)造方法,來生成了一個實例若债,而這個實例產(chǎn)生了以后符相,才會調(diào)用實例方法來完成一些行為。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蠢琳,一起剝皮案震驚了整個濱河市啊终,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌傲须,老刑警劉巖蓝牲,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異泰讽,居然都是意外死亡例衍,警方通過查閱死者的電腦和手機散怖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肄渗,“玉大人,你說我怎么就攤上這事咬最◆岬眨” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵永乌,是天一觀的道長惑申。 經(jīng)常有香客問我,道長翅雏,這世上最難降的妖魔是什么圈驼? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮望几,結(jié)果婚禮上绩脆,老公的妹妹穿的比我還像新娘。我一直安慰自己橄抹,他們只是感情好靴迫,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著楼誓,像睡著了一般玉锌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上疟羹,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天主守,我揣著相機與錄音,去河邊找鬼榄融。 笑死参淫,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的愧杯。 我是一名探鬼主播黄刚,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼民效!你這毒婦竟也來了憔维?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤畏邢,失蹤者是張志新(化名)和其女友劉穎业扒,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舒萎,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡程储,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片章鲤。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡摊灭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出败徊,到底是詐尸還是另有隱情帚呼,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布皱蹦,位于F島的核電站煤杀,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏沪哺。R本人自食惡果不足惜沈自,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辜妓。 院中可真熱鬧枯途,春花似錦、人聲如沸籍滴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽异逐。三九已至捶索,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間灰瞻,已是汗流浹背腥例。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留酝润,地道東北人燎竖。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像要销,于是被迫代替她去往敵國和親构回。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

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