1.分類相關(guān)
(1)特點:
- 運(yùn)行時決議
在編好分類文件之后莱睁,它并沒有將分類當(dāng)中對應(yīng)添加的內(nèi)容附加到相應(yīng)的宿主類中是己,實際上宿主類中還沒有分類中方法婿斥,而是在運(yùn)行時竖配,通runtime將分類中添加的內(nèi)容添加到宿主類中。 - 可以為系統(tǒng)類添加分類
(2)實戰(zhàn)
分類中可以添加哪些內(nèi)容斩熊?
- 實例方法
- 類方法
- 協(xié)議
- 屬性(我們在分類中定義一個屬性往枣,實際上只是生成了對應(yīng)的get方法和set方法,并沒有為我們在分類中添加實例變量)
你用分類都做了哪些事情粉渠?
- 聲明私有方法
- 分解體積龐大的類文件
- 把Framework的私有方法公開
(3)分類的結(jié)構(gòu)體
(4)分類加載調(diào)用棧
static void remethodizeClass(Class cls){
category_list *cats;
bool isMeta;
runtimeLock.assertWriting();
/*
我們只分析分類當(dāng)中實例方法添加的邏輯
因此在這里我們假設(shè) isMeta = NO
*/
isMeta = cls->isMetaClass();
// Re-methodizing: check for more categories
// 獲取cls中未完成整合的所有分類
if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
if (PrintConnecting) {
_objc_inform("CLASS: attaching categories to class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
attachCategories(cls, cats, true /*flush caches*/);
free(cats);
}}
static void attachCategories(Class cls, category_list *cats, bool flush_caches){
if (!cats) return;
if (PrintReplacedMethods) printReplacements(cls, cats);
/*
我們只分析分類當(dāng)中實例方法添加的邏輯分冈,
因此在這里我們假設(shè)isMeta = NO
*/
bool isMeta = cls->isMetaClass();
/*
二維數(shù)組 [[method_t, method_t,...],[method_t, method_t,...], [method_t, method_t,...],...]
*/
method_list_t **mlists = (method_list_t **)
malloc(cats->count * sizeof(*mlists));
property_list_t **proplists = (property_list_t **)
malloc(cats->count * sizeof(*proplists));
protocol_list_t **protolists = (protocol_list_t **)
malloc(cats->count * sizeof(*protolists));
// Count backwards through cats to get newest categories first
int mcount = 0;
int propcount = 0;
int protocount = 0;
int i = cats->count;
bool fromBundle = NO;
while (i--) {//這里是倒敘遍歷,最先訪問最后編譯的分類
// 獲取一個分類
auto& entry = cats->list[i];
// 獲取該分類的方法列表
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
//最后編譯的分類最先添加到分類數(shù)組中
mlists[mcount++] = mlist;
fromBundle |= entry.hi->isBundle();
}
//屬性列表添加規(guī)則 同方法列表添加規(guī)則
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
proplists[propcount++] = proplist;
}
protocol_list_t *protolist = entry.cat->protocols;
if (protolist) {
protolists[protocount++] = protolist;
}
}
auto rw = cls->data();
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
// 添加方法
rw->methods.attachLists(mlists, mcount);
free(mlists);
if (flush_caches && mcount > 0) flushCaches(cls);
// 添加屬性
rw->properties.attachLists(proplists, propcount);
free(proplists);
// 添加協(xié)議
rw->protocols.attachLists(protolists, protocount);
free(protolists);
}
如果兩個分類中都有一個同名的方法霸株,最后編譯的分類當(dāng)中的同名方法才會最終生效雕沉。
2.關(guān)聯(lián)對象
能否給分類添加"成員變量" ?
id objc_getAssociatedObject(id object,const void * key)
void objc_setAssociatedObject(id object,const void *key,id value,objc_AssociationPolicy policy)
void objc_removeAssociatedObjects(id object)
關(guān)聯(lián)對象的本質(zhì)
關(guān)聯(lián)對象由AssociationsManager
管理并在AssociationsHashMap
存儲,所有對象的關(guān)聯(lián)內(nèi)容都在同一個全局容器
中。
3.擴(kuò)展屬性
(1) 特點
- 編譯時決議
- 只以聲明的形式存在去件,多數(shù)情況下寄生于宿主類.m中坡椒。
- 不能為系統(tǒng)類添加擴(kuò)展。
(2) 一般用擴(kuò)展做什么尤溜?
- 聲明私有屬性
- 聲明私有方法
- 聲明私有成員變量
4.代理
準(zhǔn)確的說是一種軟件設(shè)計模式倔叼,iOS當(dāng)中以@protocol形式體現(xiàn),傳遞方式一對一靴跛。
工作流程
注意點
一般聲明為weak以規(guī)避循環(huán)引用缀雳。
5.通知
概念:
通知是使用觀察者模式
來實現(xiàn)的用于跨層傳遞消息的機(jī)制,傳遞方式為一對多
梢睛。
如何實現(xiàn)通知機(jī)制肥印?
6.KVO
- KVO是Objective-C對觀察者設(shè)計模式的一種實現(xiàn)
-
apple使用了isa混寫(isa-swizzling)來實現(xiàn)KVO
- 使用setter方法改變值kvo才會生效。
- 使用setValue:forkey:改變KVO才會生效
- 成員變量直接修改需求手動添加KVO才會生效绝葡。
#import <Foundation/Foundation.h>
@interface MObject : NSObject
@property (nonatomic,assign)int value;
- (void)increase;
@end
#import "MObject.h"
@implementation MObject
- (instancetype)init{
self = [super init];
if(self){
_value = 0;
}
return self;
}
- (void)increase{
_value = _value + 1;
}
@end
#import <Foundation/Foundation.h>
@interface MObserver : NSObject
@end
#import "MObserver.h"
#import "MObject.h"
@implementation MObserver
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if([object isKindOfClass:[MObject class]] && [keyPath isEqualToString:@"value"]){
NSNumber *valueNum = [change valueForKey:NSKeyValueChangeNewKey];
NSLog(@"value is %@",valueNum);
}
}
@end
打印結(jié)果
NSKVONotifying_A的setter實現(xiàn)
- (void)setValue:(id)value forKey:(NSString *)key{
[self willChangeValueForKey:@"keyPath"];
//調(diào)用父類實現(xiàn)深碱,也即原類的實現(xiàn)
[super setValue:value forKey:key];
[self didChangeValueForKey:@"keyPath"];
}
問題
- 通過kvc設(shè)置value能否生效?
2.通過成員變量直接賦值value能否生效藏畅?
7.KVC
蘋果系統(tǒng)為我們提供的一種鍵值編碼技術(shù)敷硅。
(1) -(id)valueForKey:(NSString *)key
a.Accessor Method
- <getKey>
- <key>
- <isKey>
b.Instance var
- _key
- _isKey
- key
- isKey
(2)-(void)setValue:(id)value forKey:(NSString *)key
8.屬性關(guān)鍵字
- 讀寫權(quán)限
- 原子性
- 引用技術(shù)
(1)讀寫權(quán)限
- readonly
- readwrite
(2)原子性
- atomic (賦值和獲取是線程安全的)
例如一個數(shù)組使用atomic關(guān)鍵字功咒,對數(shù)組進(jìn)行賦值和獲取是線程安全的,但是對數(shù)組進(jìn)行添加數(shù)據(jù)绞蹦,刪除數(shù)據(jù)力奋,不是線程安全的 - nonatomic
(3)引用計數(shù)
retain/strong
assign/unsafe_unretained
weak
-
copy
assign
- 修飾基本數(shù)據(jù)類型,如int , BOOL等
- 修飾對象類型時幽七,不改變其引用計數(shù)
- 會產(chǎn)生懸垂指針(assign所修飾的對象被釋放后景殷,assign指針仍然指向原對象內(nèi)存地址,這時通過assign繼續(xù)訪問對象澡屡,由于懸垂指針的存在猿挚,會導(dǎo)致程序內(nèi)存泄漏或者程序異常)
weak
- 不改變修飾對象的引用計數(shù)。
- 所指對象在被釋放之后驶鹉,weak指針會自動置為nilcopy
(a) 淺拷貝
特點:1.增加被拷貝對象的引用計數(shù) 2.并沒有發(fā)生新的內(nèi)存分配
淺拷貝就是對內(nèi)存地址的復(fù)制绩蜻,讓目標(biāo)對象指針和原對象指針指向同一片內(nèi)存空間。
(b)深拷貝
特點: 1.深拷貝不會增加被拷貝對象的引用計數(shù) 2.深拷貝產(chǎn)生了內(nèi)存分配
深拷貝讓目標(biāo)對象指針和源對象指針指向兩片內(nèi)容相同的內(nèi)存空間室埋。
(c)深拷貝vs淺拷貝- 是否開辟了新的內(nèi)存空間
- 是否影響了引用計數(shù)
copy關(guān)鍵字總結(jié)
:可變對象的copy和mutableCopy都是深拷貝办绝,不可變對象的copy是淺拷貝,mutableCopy是深拷貝词顾,copy方法返回的都是不可變對象八秃,
(d)注意點@property(copy)NSMutableArray*array 碱妆?
不論賦值過來的是NSMutableArray 還是NSArray肉盹,copy之后都是NSArray,這樣對array進(jìn)行添加元素或者是刪除元素會導(dǎo)致程序異常。
9.經(jīng)典題目
MRC下如何重寫retain修飾變量的setter方法疹尾?
@property(nonatomic,retain) id obj;
- (void)setObjc:(id)obj{
if(_obj != obj){
[_obj release];
[_obj retain];
}
}
請簡述分類的實現(xiàn)原理上忍?
分類的實現(xiàn)是由運(yùn)行時決議的, 不同分類中含有同名分類方法纳本,誰最終生效取決于誰最后參與編譯窍蓝,最后參與編譯的分類當(dāng)中的同名分類方法會最終生效,如果分類中添加的方法與宿主類方法名相同繁成,分類方法會覆蓋同名的宿主方法吓笙,這里說的覆蓋是指,由于消息傳遞過程中巾腕,優(yōu)先查找數(shù)組靠前的方法面睛,如果找到了一個同名方法就進(jìn)行調(diào)用,實際上宿主類的同名方法是仍然存在尊搬?
KVO實現(xiàn)原理?
能否為分類添加成員變量叁鉴?