?一 、KVO與KVC
? ? ? ? KVC
?? ? ? ? https://blog.csdn.net/yuwuchaio/article/details/80701681
?? ? ? ? KVC 鍵值編碼? 通過字符串Key值去直接訪問對象屬性,通過KVC機(jī)制可以間接的訪問對象的屬性,KVC之所以能訪問對象屬性是因?yàn)樽駨牧薔SKeyValueCoding協(xié)議,所有的NSObject類都遵從了這個(gè)協(xié)議,通過KVC訪問對象的屬性
?? ? ? ? KVC內(nèi)部查詢key值的順序
?? ? ? ? 當(dāng)調(diào)用setVlaue:屬性值 forKey :@"name"的代碼的時(shí)候,底層的執(zhí)行機(jī)制如下:
?? ? ? ? 程序優(yōu)先調(diào)用set方法,代碼通過setter方法完成設(shè)置。
?? ? ? ? 如果程序沒有找到set方法,KVC機(jī)制會檢查 +(BOOL)accessInstanceVariablesDirectly方法有沒有返回YES,該方法默認(rèn)返回YES,如果你重寫了該方法返回NO,那么這一步KVC會執(zhí)行setValue:forUndefinedKey:方法.因此,如果你不想讓你的類被別人使用KVC的話,重寫 +(BOOL)accessInstanceVariablesDirectly方法,讓其返回NO即可,這樣的話,如果KVC沒有找到set屬性名時(shí),會直接調(diào)用setValue:forUndefinedKey:方法 ?一般開發(fā)者并不會重寫+(BOOL)accessInstanceVariablesDirectly方法,接下來KVC會搜索該類里面有沒有名為_的成員變量,無論是在類.h部分還是在類.m部分定義,也無論用了什么樣的訪問修飾符,只要存在_命名的變量,KVC都可以對該成員變量賦值.
?? ? ? ? 如果該類中既沒有set方法,也沒有_成員變量,KVC機(jī)制會搜索_is的成員變量
?? ? ? ? 如果該類還是沒有_和_is成員變量,KVC機(jī)制再會繼續(xù)搜索和is的成員變量(即沒有下劃線的成員變量),再給他們賦值.
?? ? ? ? 如果上面列出的方法或成員變量都不存在,系統(tǒng)將會執(zhí)行該對象的setValue: forUndefineKey:方法,默認(rèn)是拋出異常.
KVC如何處理異常
? ? ? ? ? KVC中最常見的異常時(shí)使用了錯(cuò)誤的key,或者傳遞了nil,KVC中有專門的方法來處理這些異常 通常在KVC操作Model時(shí),拋出異常的那兩個(gè)方法是需要重寫的,如果直接拋出異常導(dǎo)致app崩潰是不合理的 錯(cuò)誤的<key> 重寫setValue: forUndefinedKey: 方法 一般是直接打印出來即可,或自己做特殊處理 傳了<nil> 重寫setNilValueForKey:就可以了?
? ? ? ? ? -(void)setNilValueForKey:(NSString *)key{ NSLog(@"不能將%@設(shè)成nil",key);} ?
KVC的使用
1.動(dòng)態(tài)的取值和設(shè)值
2.用KVC來訪問和修改私有變量
3.Model和字典轉(zhuǎn)換
4.修改一些控件的內(nèi)部屬性
KVO
?????手動(dòng)觸發(fā)KVO
? 1.需要重寫關(guān)閉屬性的自動(dòng)觸發(fā)
?? ? + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
?? ? ? ? BOOL automatic = NO;
?? ? ? ? if ([theKey isEqualToString:@"name"]) {
?? ? ? ? automatic = NO;
?? ? ? ? }
?? ? ? ? else {
?? ? ? ? automatic = [super automaticallyNotifiesObserversForKey:theKey];
?? ? ? ? }
?? ? ? ? return automatic;
?? ? }
?2.[self.redView addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
? ? 添加這兩個(gè)方法
? ? [self.redView willChangeValueForKey:@"name"];
? ? [self.redView didChangeValueForKey:@"name"];
?此時(shí)就會自動(dòng)調(diào)用
?底層實(shí)現(xiàn)原理:
? ? 動(dòng)態(tài)的創(chuàng)建了一個(gè)redView的類,重寫了子類的set方法,在set方法中添加了willChangeValueForKey和didChangeValueForKey。
二、字典初始化傳入nil崩潰
因?yàn)橄到y(tǒng)檢測到傳入的key的count和value的count不一致,進(jìn)而導(dǎo)致崩潰,相當(dāng)于越界了
三、UIImage imageNamed 與 imageWithContentsOfFile的差別
[UIImage imageNamed:]只適合與UI界面中小的貼圖的讀取,而一些比較大的資源文件應(yīng)該盡量避免使用這個(gè)接口。
imageName的方式會在使用的時(shí)候系統(tǒng)會cache,程序員是無法處理cache的,這是由系統(tǒng)自動(dòng)處理的,對于重復(fù)加載的圖像,速度會提升很多,這樣反而用戶體驗(yàn)好。所以如果某張圖片需要在應(yīng)用中使用多次,或者重復(fù)引用,使用imageName的方式會更好
imageWithContentsOfFile的方式,在使用完成之后系統(tǒng)會釋放,不會緩存下來,所以也就沒有這樣的問題。一般也不會把所有的圖片都會緩存。有些圖片在應(yīng)用中只使用一兩次的,就可以用這樣的方式,比如新手引導(dǎo)界面的圖片等等,就適合這樣的方式。沒有明顯的界限。
四、iOS內(nèi)存管理
棧(stack):棧是編譯器自動(dòng)分配并釋放,用來存放函數(shù)的參數(shù),局部變量。
?堆(heap):堆一般是程序員自己分配和釋放,如果我們在使用的過程中,沒有釋放,那么等到程序完全結(jié)束,系統(tǒng)將會對堆中的內(nèi)容進(jìn)行回收。一般開發(fā)中的alloc就是存放在堆中的?
全局變量(靜態(tài)變量)(static):全局變量和靜態(tài)變量是單獨(dú)存放的,因?yàn)樗麄兊穆暶髦芷诤驼麄€(gè)程序的生命周期一致。釋放的時(shí)間是由整個(gè)程序結(jié)束后系統(tǒng)負(fù)責(zé)回收。?
文字常量區(qū):一般用來存放常量字符串,比如說String?*str?=?@"hello?world",他的釋放也和全局變量一致,在程序結(jié)束后進(jìn)行釋放。
?程序代碼區(qū):用來存放函數(shù)的二進(jìn)制內(nèi)容的區(qū)域。
內(nèi)存中存放的對象都是存放在堆上的,系統(tǒng)并不會釋放堆上的內(nèi)存,所以需要我們手動(dòng)去管理。
內(nèi)存管理分為ARC(自動(dòng)內(nèi)存管理)、MRC(手動(dòng)內(nèi)存管理)、自動(dòng)釋放池(autorelease pool)。
1.MRC時(shí)代,我們需要手動(dòng)的添加retain和relase,
2ARC時(shí)代,不需要手動(dòng)添加,系統(tǒng)會自動(dòng)幫我們插入retain和relase, ?
3自動(dòng)釋放池(autorelease pool)實(shí)際上只是把對release的調(diào)用延遲了,對于每一個(gè)Autorelease,系統(tǒng)只是把該Object放入了當(dāng)前的Autorelease pool中,當(dāng)該pool被釋放時(shí),該pool中的所有Object會被調(diào)用Release。實(shí)際上對于 [NSString stringWithFormat:1.0] 這類構(gòu)造函數(shù)返回的對象都是autorelease的。
手動(dòng)添加的autorelease pool會在大括號執(zhí)行完之后釋放池子中的對象。
每次初始化好的對象都是autorelease對象,ARC下不需要手動(dòng)加autorelease,MRC下需要手動(dòng)加autorelease,ARC系統(tǒng)會自動(dòng)幫我們插入retain和relase,MRC下需要手動(dòng)加。
每個(gè)runloop里面都會默認(rèn)有autorelease pool,當(dāng)runloop休眠或停止的時(shí)候,會釋放autorelease pool,釋放autorelease pool的時(shí)候會調(diào)用池子中的autorelease對象的relase方法,釋放對象。主線程中的autorelease pool是由若干個(gè)autorelease page組成,系統(tǒng)自動(dòng)把不同的對象放到不同的page中,當(dāng)runloop休眠的時(shí)候釋放對應(yīng)的page。
在實(shí)際中的使用場景其實(shí)很明確了, 在程序中中有大量臨時(shí)變量的時(shí)候最好手動(dòng)創(chuàng)建.
最常出現(xiàn)大量變量的時(shí)候顯然是循環(huán)/遍歷, 我們常用的for循環(huán), 以及enumerate其實(shí)跟autoreleasepool也有關(guān), for循環(huán)是不自動(dòng)創(chuàng)建autoreleasepool的, 而enumerate中已經(jīng)自動(dòng)創(chuàng)建了autoreleasepool, 值得注意的是高并發(fā)enumerate常常會出一些意外的問題, 例如對象被提前釋放, 所以建議高并發(fā)情況下使用for循環(huán)(性能高于enumerate), 再手動(dòng)添加autoreleasepool.
autorelease pool來避免頻繁申請/釋放內(nèi)存(就是pool的作用了)。這個(gè)應(yīng)該是相對比較好理解的。
main.m里面的自動(dòng)釋放池只有app退出時(shí)才會釋放。
autorelase與ralase的區(qū)別:realse后的對象會馬上釋放,autorelase相當(dāng)于是延遲對象的釋放,調(diào)用該方法后,對象不會被馬上釋放,只有當(dāng)前runloop運(yùn)行結(jié)束的時(shí)候才會釋放。
五?iOS深拷貝與淺拷貝
淺拷貝:淺拷貝并不拷貝對象本身,只是對指向?qū)ο蟮闹羔樳M(jìn)行拷貝
深拷貝:直接拷貝對象到內(nèi)存中一塊區(qū)域,然后把新對象的指針指向這塊內(nèi)存
在iOS中并不是所有對象都支持Copy和MutableCopy,遵循NSCopying協(xié)議的類可以發(fā)送Copy協(xié)議,遵循NSMutableCopying協(xié)議的類可以發(fā)送MutableCopy消息。如果一個(gè)對象沒有遵循這兩個(gè)協(xié)議而發(fā)送Copy或者M(jìn)utableCopy消息那么會發(fā)生異常。如果要遵循NSCopying協(xié)議,那么必須實(shí)現(xiàn)copyWithZone方法。如果要遵循NSMutableCopying協(xié)議那么必須實(shí)現(xiàn)mutableCopyWithZone方法。
系統(tǒng)非容器類(NSString)不可變對象調(diào)用Copy方法其實(shí)只是把當(dāng)前對象的指針指向了原對象的地址,而調(diào)用mutableCopy方法則是新分配了一塊內(nèi)存區(qū)域并把新對象的指針指向了這塊區(qū)域。對于可變對象來說調(diào)用Copy和MutableCopy方法都會重新分配一塊內(nèi)存。但是copy和mutableCopy的區(qū)別在于copy在復(fù)制對象的時(shí)候其實(shí)是返回了一個(gè)不可變對象,因此當(dāng)調(diào)用方法改變對象的時(shí)候會崩潰
對于容器類只有不可變的容器執(zhí)行copy操作才是淺拷貝,所以所有的容器類使用mutableCopy是深拷貝
六、首頁嵌套的手勢沖突
解決方式通過計(jì)算tableview的偏移量,來固定他的偏移量。
拖動(dòng)圖片放大的功能實(shí)現(xiàn) 可以在拖動(dòng)列表的時(shí)候,在代理方法中設(shè)置圖片的frame來實(shí)現(xiàn)
七 GCD 的使用
執(zhí)行方式分為同步異步 ?隊(duì)列分為串行隊(duì)列和并行隊(duì)列
同步執(zhí)行方法 ?任務(wù)按照順序執(zhí)行、不會開啟新的線程
異步執(zhí)行任務(wù) ? 任務(wù)可以并行處理,會開啟新的線程
串行隊(duì)列 ? 隊(duì)列是放任務(wù)的,隊(duì)列里面的任務(wù)按照順序執(zhí)行 ?主隊(duì)列是串行隊(duì)列,主隊(duì)列對應(yīng)著管理者主線程的那條隊(duì)列
并行隊(duì)列 ?多個(gè)任務(wù)同時(shí)執(zhí)行 ? 全局隊(duì)列是并行隊(duì)列
同步串行隊(duì)列 ? 只有當(dāng)串行隊(duì)列為主線程時(shí)會卡死當(dāng)前線程
異步串行隊(duì)列 ? ?如果是主隊(duì)列,不會開啟新線程 ?如果是自己創(chuàng)建的隊(duì)列,會開啟新線程,按照順序執(zhí)行任務(wù)
http://www.itdecent.cn/p/b89915a331ca
同步并行隊(duì)列 ? ? ?不會開啟子線程 ,串行執(zhí)行任務(wù)
異步并行隊(duì)列 ? 會開啟新的線程,并發(fā)的執(zhí)行任務(wù)
串行隊(duì)列 同步執(zhí)行與異步執(zhí)行的區(qū)別 ? 同步隊(duì)列?sync的任務(wù)需要立即執(zhí)行.任務(wù)按照順序執(zhí)行。異步隊(duì)列,只能保證async里面的任務(wù)是順序執(zhí)行,asyncblock外面的代碼與asyncblock里面的代碼不順序執(zhí)行
同步并行隊(duì)列 與 異步串行隊(duì)列的區(qū)別 ??同步并行隊(duì)列不會開啟子線程,異步串行隊(duì)列會開啟子線程。
調(diào)度組控制網(wǎng)絡(luò)請求 ? 需要配合dispatch_group_enter(group);dispatch_group_leave(group)
八 、iOS 分類(category)、類擴(kuò)展(extension)、協(xié)議(protocol)
分類 category
使用場景分析
1.擴(kuò)展已有的類
有大量的子類,需要添加公用方法,但又無法修改它們的父類的情形(如系統(tǒng)類)。
一般是大量的功能代碼已經(jīng)形成,使用子類需要添加新類的頭文件等。分類只能添加方法,不能添加屬性。(下文會提到如何添加屬性)
2.使用父類私有方法
已經(jīng)存在了大量的子類方法,但是又無法修改他們的父類,比如系統(tǒng)自帶的類添加類擴(kuò)展方法。在子類中聲明父類類別后,即可通過編譯。
category、extension異同點(diǎn)分析
1、分類(category)
① category只能添加“方法”,不能添加成員變量。
② 分類中可以訪問原來類中的成員變量,但是只能訪問@protect和@public屬性。
③ 添加方法加上前綴,添加方法會覆蓋父類的同名方法,可以防止意外覆蓋,也防止被別人覆蓋。
④ 分類中添加的成員變量,要通過getter、setter方法進(jìn)行添加。
類擴(kuò)展(extension)
① 類擴(kuò)展的屬性和方法都是私有的,也可以定義在.h中,這樣就是共有的,類擴(kuò)展中的方法是一定要實(shí)現(xiàn)的方法。Category沒有這個(gè)限制。
分類有.h和.m兩個(gè)文件 ?擴(kuò)展只有.h 方法的實(shí)現(xiàn)是放在原類中的
?九、消息傳遞
?1.void objc_msgSend(void /id self,SEL op/)
?當(dāng)調(diào)用方法[self class] 相當(dāng)于調(diào)用objc_msgSend(Self,@sel(class))
?void objc_msgSuperSend(void /struct objc_super *super,SEL op/),objc_super結(jié)構(gòu)體中接受者receiver就是當(dāng)前對象
?struct objc_super{
? id receiver;
?}
2.當(dāng)調(diào)用方法的時(shí)候,會先在緩存中查找是否有該方法,沒有的話通過isa指針去當(dāng)前類方法列表中查找,沒有的話再去父類查找,直到找到該方法,如果查找到根部(父類為nil比如NSObject)還沒有,就會去調(diào)用消息轉(zhuǎn)發(fā)機(jī)制
?消息轉(zhuǎn)發(fā)后調(diào)用的方法:
?+ (BOOL)resolveInstanceMethod:(SEL)sel
?- (id)forwardingTargetForSelector:(SEL)aSelector{
?- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
?- (void)forwardInvocation:(NSInvocation *)anInvocation
3.當(dāng)一個(gè)類的方法沒有實(shí)現(xiàn)的時(shí)候,可以通過runtime為方法添加實(shí)現(xiàn)