OC相關(二)

25. KVO的實現(xiàn)原理,如果手動實現(xiàn)KVO?

(1) KVO 是基于 runtime 機制實現(xiàn)的
(2) 當一個對象 (假設是person對象,對應的類為 JLperson) 的屬性值age發(fā)生改變時,系統(tǒng)會自動生成一個繼承自JLperson的類NSKVONotifying_JLPerson,在這個類的 setAge 方法里面調(diào)用

 [super setAge:age]
 [self willChangeValueForKey:@"age"];
 [self didChangeValueForKey:@"age"];

三個方法,而willChangeValueForKey:didChangeValueForKey:兩個方法內(nèi)部會主動調(diào)用-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
方法,在該方法中可以拿到屬性改變前后的值。
(3) 最后把這個JLperson對象的 isa 指針 ( isa 指針告訴 Runtime 系統(tǒng)這個對象的類是什么 ) 指向這個新創(chuàng)建的子類NSKVONotifying_JLPerson,對象就神奇的變成了新創(chuàng)建的子類NSKVONotifying_JLPerson的實例。

26.談下iOS開發(fā)中知道的哪些鎖?

相關文章:

  • @synchronized
  • NSLock 對象鎖(個人理解是互斥鎖)
  • NSRecursiveLock 遞歸鎖
  • NSConditionLock 條件鎖
  • pthread_mutex 互斥鎖(C語言)
  • dispatch_semaphore 信號量實現(xiàn)加鎖(GCD)
  • OSSpinLock (暫不建議使用,原因參見這里
  • 哪個性能最差?
  • SD和AFN使用的哪個?
    @synchronized用的最多,其次就是信號量和NSLock,其余的都很少用。
  • 一般開發(fā)中你最常用哪個? 哪個鎖apple存在問題又是什么問題?
    NSLock。
    OSSpinLock。
  • OSSpinLock為什么不安全了
    如果一個低優(yōu)先級的線程獲得鎖并訪問共享資源,這時一個高優(yōu)先級的線程也嘗試獲得這個鎖,它會處于 spin lock 的忙等狀態(tài)從而占用大量 CPU。此時低優(yōu)先級線程無法與高優(yōu)先級線程爭奪 CPU 時間,從而導致任務遲遲完不成、無法釋放 lock。這并不只是理論上的問題,libobjc 已經(jīng)遇到了很多次這個問題了,于是蘋果的工程師停用了 OSSpinLock。
  • 什么情況下會有死鎖?
  • 在一個串行隊列里做同步。如:
  • 在一個遞歸里用互斥鎖。使用鎖最容易犯的一個錯誤就是在遞歸或循環(huán)中造成死鎖

    如下代碼中,因為在線程1中的遞歸block中,鎖會被多次的lock,所以自己也被阻塞了。
    死鎖代碼
  • 此處將NSLock換成NSRecursiveLock,便可解決問題。NSRecursiveLock類定義的鎖可以在同一線程多次lock,而不會造成死鎖。遞歸鎖會跟蹤它被多少次lock。每次成功的lock都必須平衡調(diào)用unlock操作。只有所有的鎖住和解鎖操作都平衡的時候,鎖才真正被釋放給其他線程獲得。
    改成遞歸鎖就不會死鎖了
  • 互斥鎖有哪些?互斥鎖和自旋鎖的區(qū)別是啥?
  • 互斥鎖:如果一個線程無法獲取互斥量,該線程會被直接掛起,不再消耗CPU時間,當其他線程釋放互斥量后,操作系統(tǒng)會激活被掛起的線程?;コ怄i會使得線程阻塞,阻塞的過程又分兩個階段,第一階段是會先空轉(zhuǎn),可以理解成跑一個 while 循環(huán),不斷地去申請加鎖,在空轉(zhuǎn)一定時間之后,線程會進入 waiting 狀態(tài),此時線程就不占用CPU資源了,等鎖可用的時候,這個線程會立即被喚醒。
  • 自旋鎖: 如果一個線程需要獲取自旋鎖,該鎖已經(jīng)被其他線程占用,該線程不會被掛起,而是不斷消耗CPU時間,一直試圖獲取自旋鎖。
  • 總結:最大的區(qū)別,互斥鎖是掛起等待,等待時不占用CPU,自旋鎖會不停地試圖獲取鎖,會一直占用CPU。
26. 串行隊列和同步鎖兩者在保護線程安全上的性能對比。
27. iOS下如何實現(xiàn)指定線程數(shù)目的線程池?
28. 數(shù)據(jù)庫建表的時候索引有什么用?

在數(shù)據(jù)量大的時候,快速提高查找速度。

//在表上創(chuàng)建一個簡單的索引。允許使用重復的值。
CREATE INDEX index_name ON table_name (column_name);
////在表上創(chuàng)建一個唯一的索引。唯一的索引意味著兩個行不能擁有相同的索引值。
CREATE UNIQUE INDEX index_name ON table_name (column_name);

注意事項:

  • 第一,對于那些在查詢中很少使用或者參考的列不應該創(chuàng)建索引。這是因為,既然這些列很少使用到,因此有索引或者無索引,并不能提高查詢速度。相反,由于增加了索引,反而降低了系統(tǒng)的維護速度和增大了空間需求。

  • 第二,對于那些只有很少數(shù)據(jù)值的列也不應該增加索引。這是因為,由于這些列的取值很少,例如人事表的性別列,在查詢的結果中,結果集的數(shù)據(jù)行占了表中數(shù)據(jù)行的很大比例,即需要在表中搜索的數(shù)據(jù)行的比例很大。增加索引,并不能明顯加快檢索速度。

  • 第三,對于那些定義為text, image和bit數(shù)據(jù)類型的列不應該增加索引。這是因為,這些列的數(shù)據(jù)量要么相當大,要么取值很少。

  • 第四,當修改性能遠遠大于檢索性能時,不應該創(chuàng)建索引。這是因為,修改性能和檢索性能是互相矛盾的。當增加索引時,會提高檢索性能,但是會降低修改性能。當減少索引時,會提高修改性能,降低檢索性能。因此,當修改性能遠遠大于檢索性能時,不應該創(chuàng)建索引。

  • 優(yōu)點:
    第一,通過創(chuàng)建唯一性索引,可以保證數(shù)據(jù)庫表中每一行數(shù)據(jù)的唯一性。
    第二,可以大大加快數(shù)據(jù)的檢索速度,這也是創(chuàng)建索引的最主要的原因。
    第三,可以加速表和表之間的連接,特別是在實現(xiàn)數(shù)據(jù)的參考完整性方面特別有意義。
    第四,在使用分組和排序子句進行數(shù)據(jù)檢索時,同樣可以顯著減少查詢中分組和排序的時間。
    第五,通過使用索引,可以在查詢的過程中,使用優(yōu)化隱藏器,提高系統(tǒng)的性能。

  • 缺點:雖然,索引有許多優(yōu)點,但是,為表中的每一個列都增加索引,是非常不明智的。這是因為,增加索引也有許多不利的一個方面, 缺點:
    第一,創(chuàng)建索引和維護索引要耗費時間,這種時間隨著數(shù)據(jù)量的增加而增加。
    第二,索引需要占物理空間,除了數(shù)據(jù)表占數(shù)據(jù)空間之外,每一個索引還要占一定的物理空間,如果要建立聚簇索引,那么需要的空間
    就會更大。
    第三,當對表中的數(shù)據(jù)進行增加、刪除和修改的時候,索引也要動態(tài)的維護,這樣就降低了數(shù)據(jù)的維護速度。

29. 介紹下iOS設備獲取唯一設備號的歷史變遷。

iOS5之后,udid被廢棄。
iOS6之后,可以用idfa,不過idfa用戶可以在設置—通用—隱私里還原。
iOS7之后,udid被徹底限制。有段時間可以用open udid替代,將其存在剪切板中。但是有缺點,如果用戶完全刪除了open udid sdk 的app,然后重啟設備,就會重新生成新的open udid。
后來open udid停止更新了。
idfa或UUID保存在keychain里。(UUID每次生成都會變)。

30. 如何使用runtime hook一個class的某個方法,又如何hook某個instance的方法?

消息轉(zhuǎn)發(fā)里有專門的方法。

31. UIView、CoreAnimation和CoreGraphics的關系。
  • CoreGraphics:核心圖形庫,平時使用最頻繁的point,size,rect等這些圖形,都定義在這個框架中,類名以CG開頭的都屬于CoreGraphics框架,它提供的都是C語言的函數(shù)接口,是可以在iOS和macOS通用的。

  • CoreAnimation:核心動畫,其實就是QuartzCore框架。

  • CoreGraphics 和 CoreAnimation 的聯(lián)系:它們都是跨 iOS 和 macOS 使用的,這點區(qū)別于UIKit,并且CoreAnimation中大量用到CoreGraphics中的類,原因是顯然的,實現(xiàn)動畫自然要用到圖形庫中的東西。

  • 為什么 CAxxx 用的時候好多都要“.CGXXXX”呢?比如:

layer.backgroundColor = [UIColor redColor].CGColor;  

首先,圖層layer的類型是CALayer,它是CoreAnimation中的類。前面說CoreAnimation是跨平臺的,為了跨平臺的特性,它的backgroundColor屬性就不能使用UIColor類型了,因為UIKit只能使用于iOS,而CoreGraphics框架是跨平臺的,所以CALayer類的backgroundColor屬性就使用了CGColor類型。所以使用時在賦值前要先進行轉(zhuǎn)換,將UIKit中的東西轉(zhuǎn)換為CoreGraphics中的類型。

32.通過[UIImage imageNamed:]生成的對象什么時候被釋放?

用UIImage加載本地圖像最常用的是下面三種:

  1. imageNamed方法
[UIImage imageNamed:ImageName];
  1. imageWithContentsOfFile方法
NSString *thumbnailFile = [NSString stringWithFormat:@"%@/%@.png", [[NSBundle mainBundle] resourcePath], fileName];
UIImage *thumbnail = [UIImage imageWithContentsOfFile:thumbnailFile];
  1. initWithContentsFile方法
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath]
  • 第一種方法為常見方法,利用它可以方便加載資源圖片。用imageNamed的方式加載時,會把圖像數(shù)據(jù)根據(jù)它的名字緩存在系統(tǒng)內(nèi)存中,以提高imageNamed方法獲得相同圖片的image對象的性能。即使生成的對象被 autoReleasePool釋放了,這份緩存也不釋放。而且沒有明確的釋放方法。如果圖像比較大,或者圖像比較多,用這種方式會消耗很大的內(nèi)存。優(yōu)點是加載圖片速度快,缺點是耗內(nèi)存大,適用于小圖片并且會被頻繁讀取的圖片。

  • 第二種方法加載的圖片是不會緩存的。得到的對象時autoRelease的,當autoReleasePool釋放時才釋放。優(yōu)點是圖片數(shù)據(jù)不緩存,節(jié)省內(nèi)存,缺點是加載圖片速度相對慢,每次讀取圖片都要從路徑下尋找并解析圖片數(shù)據(jù)。適用于會用到的圖片數(shù)據(jù)不大,且不經(jīng)常用到的。

  • 第三種方法要手動release掉。不系統(tǒng)緩存。release后立即釋放,一般用在封面等圖比較大的地方。適用于大圖片,且不經(jīng)常要用的圖片。

  • 優(yōu)化方案

這兩種方法都有各自的優(yōu)缺點,那么有什么辦法能夠把其優(yōu)點結合起來呢,答案是hook;
首先hook imageNamed方法,然后在我們自己的方法中進行如下判斷:
定義一個本地緩存圖片的最大size
判斷本地Bundle中是否存在該圖片,不存在直接返回到原imageNamed方法,存在則繼續(xù)
從緩存中直接讀取,如果存在該圖片,直接返回,否則繼續(xù)
判斷該圖片是否大于size,如果大于則調(diào)用原imageWithContentsOfFile方法返回UIImage
如果該圖片不大于size則返回,并存入緩存
接下來hookimageWithContentsOfFile方法,步驟也是類似,就不多做闡述,直接看代碼;
https://elliotsomething.github.io/2016/03/01/iOS-%E4%B9%8B-imageNamed%E5%92%8CimageWithContentsOfFile%E4%BC%98%E5%8C%96/

33. 如何終止正在運行的工作線程?
  • NSOperationQueue:
[self.blockOperation cancel];
[self.invocationOperation cancel];
[self.queue cancelAllOperations];

注:設置優(yōu)先級只針對同一隊列中的操作,而且在必須start或者加入隊列之前設置才有效果。操作的執(zhí)行優(yōu)先級還取決于其依賴關系和添加隊列的順序,如果其依賴的操作未執(zhí)行完畢或者和其相同優(yōu)先級的操作在其之前添加到隊列,那該操作的執(zhí)行順序要延后。

  • 暫停和繼續(xù): 可以通過以下方法暫停和繼續(xù)操作隊列。
[self.queue setSuspended:YES]; //暫停
[self.queue setSuspended:NO];  //繼續(xù)

注:暫停一個操作隊列不會導致正在執(zhí)行的操作任務中途暫停,只是簡單地阻止調(diào)度新操作任務執(zhí)行。

  • GCD:
  • 法1:通過dispatch_block_cancel
    不過這個方法只能iOS8以后才能用,而且也是只能停止還未開始執(zhí)行的任務。
  • 法2:用于標記block是否需要取消,通過if判斷是否需要return。
34. 分析下SDWebImage (q3:內(nèi)部做Decoder的原因 (典型的空間換時間)).
35.你認為開發(fā)中那些導致crash?crash的收集和定位bug的方式談下
  • 數(shù)組越界。
  • 野指針。
  • 調(diào)用了沒有的方法,判斷錯了類調(diào)用了錯誤的方法。
  • 必須實現(xiàn)的代理方法未實現(xiàn)。
  • 通知、觀察者未移除。
36. Autorelease的原理?ARC的工作原理。
  • Autorelease的原理:
  • 是由AutoreleasePoolPage結構 以雙向鏈表的方式實現(xiàn)的
  • 當對象調(diào)用 autorelease 方法時,會將對象通過objc_autoreleasepoolPush函數(shù)加入 AutoreleasePoolPage 的棧中
  • 當自動釋放池釋放時,調(diào)用 objc_autoreleasePoolPop 方法會向棧中的對象發(fā)送 release 消息。
37. Runloop的原理,它是怎么休眠的?

就是一個do-while循環(huán),很多地方底層都是依賴Runloop,如定時器,自動釋放池等。
沒有任務的超過一定時間,就會自動進入休眠。

38. 如果頁面 A 跳轉(zhuǎn)到 頁面 B,A 的 viewDidDisappear 方法和 B 的 viewDidAppear 方法哪個先調(diào)用?

viewDidAppear

39. 怎么判斷一個cell是否顯示在屏幕上?
40.倒計時如何實現(xiàn) ?
41. 熟悉 CocoaPods 么?能大概講一下工作原理么?
42. 子線程里創(chuàng)建對象時是否需要創(chuàng)建自動釋放池。

需要。
默認主線的運行循環(huán)(runloop)是開啟的,子線程的運行循環(huán)(runloop)默認是不開啟的,也就意味著子線程中不會創(chuàng)建autoreleasepool,所以需要我們自己在子線程中創(chuàng)建一個自動釋放池。(子線程里面使用的類方法都是autorelease,就會沒有池子可釋放,也就意味著后面沒有辦法進行釋放,造成內(nèi)存泄漏。)----在主線程中如果產(chǎn)生事件那么runloop才回去創(chuàng)建autoreleasepool,通過這個道理我們就知道為什么子線程中不會創(chuàng)建自動釋放池了,因為子線程的runloop默認是關閉的,所以他不會自動創(chuàng)建autoreleasepool,需要我們手動添加。

43.談談常用的設計模式。

單例模式
工廠模式
觀察者模式
代理模式

44.KVC的實現(xiàn)原理。KVC是如何通過key找到value的?
setValue:forKey:的搜索方式
  1. 首先搜索setKey:方法。(key指成員變量名,首字母大寫)。
  2. 若未找到setter方法,若類的accessInstanceVariablesDirectly方法返回YES(默認返回YES),則按_key、key_isKey的順序搜索成員名。
  3. 如果沒有找到成員變量,調(diào)用setValue:forUnderfinedKey:方法(然后應該是拋出異常)。
valueForKey:的搜索方式:
  1. 首先按getKey、keyisKey、_key的順序查找getter方法,找到直接調(diào)用。如果是BOOL、int等內(nèi)建值類型,會做NSNumber的轉(zhuǎn)換。
  2. 上面的getter沒找到,查找countOfKey、objectInKeyAtIndex:、keyAtIndexes格式的方法。如果 countOfKey和另外兩個方法中的其中一個找到,那么就會返回一個NSArray對象。
  3. 還沒找到,查找countOfKey:、enumeratorOfKey:、memberOfKey:格式的方法。如果這三個方法都找到,那么就返回一個NSSet對象。
  4. 再沒找到,調(diào)用valueForUndefinedKey:方法。
  • 注:有的博客說iskey、_isKey也會被調(diào)用,但經(jīng)過我的代碼嘗試這倆并不行。
setValue:forKeyPath:

該方法能利用運算符一層一層往內(nèi)部訪問屬性,如:

NSString *avg= [persons valueForKeyPath:@"Person.@avg.age"];

實現(xiàn)原理就是用Runtime動態(tài)的去做上面的事,比如setValue:forKey:就是先動態(tài)的去找有沒有setter方法,沒有的話就動態(tài)的找有沒有相關的成員變量,再沒有的話就拋出異常。

  • KVC的keyPath中的集合運算符如何使用?
  1. 必須用在集合對象上或普通對象的集合屬性上
  2. 簡單集合運算符有@avg, @count , @max , @min ,@sum,
  3. 格式 @"@sum.age"@"集合屬性[.@max.age](mailto:.@max.age)"
45. Block的本質(zhì)是什么?

簡單地說就是把函數(shù)當成一個參數(shù)進行傳值,本質(zhì)跟NSObject一樣,都是一些結構體定義的,其中也都有isa指針。

46. 如何在異步下載時候, 取消下載, 保證流量不浪費

NSOperation,有cancel方法。(答案可能不對)

47.id、NSObject *、id<NSObject>、instancetype的區(qū)別。
  • id: 所有的OC對象都可以用它來表示,但是不一定是NSObject類,如NSProxy;另外id修飾的變量不遵守<NSObject>協(xié)議。
  • id可以調(diào)用任意selector而編譯器和IDE不會報錯,而NSObject必須調(diào)用NSObject類聲明的方法。
  • instancetype 可以返回和方法所在類相同類型的對象,id只能返回未知類型的對象。初始化方法返回參數(shù)類型時,盡量用 instancetype。
  • instancetype只能作為返回值,id可以作為參數(shù)。
48. Block和函數(shù)指針的區(qū)別。
49. 如何優(yōu)化開機速度?
  • main函數(shù)前優(yōu)化:
  1. 刪除沒用到的framework、類、靜態(tài)變量、圖片等。
  2. 刪減沒有被調(diào)用到或者已經(jīng)廢棄的方法。
  3. 盡量不要在load方法里增加方法,可以放到initialize里。
  • main函數(shù)后優(yōu)化:
  1. 不使用xib、sb,直接視用代碼加載首頁視圖。
  2. NSUserDefaults實際上是在Library文件夾下會生產(chǎn)一個plist文件,如果文件太大的話一次能讀取到內(nèi)存中可能很耗時,不要在這里保存圖片。
  3. 每次用NSLog方式打印會隱式的創(chuàng)建一個Calendar,因此需要刪減啟動時各業(yè)務方打的log,或者僅僅針對內(nèi)測版輸出log。
  4. 首次加載的控制器的viewDidLoad以及viewWillAppear方法里盡量少做或延時加載。
  5. 壓縮資源圖片。
50. App內(nèi)存你是如何分析的?
51. 怎么完成后期檢測, 優(yōu)化。

個人理解:

  • 頻繁打開和關閉SQLite,導致內(nèi)存不斷的增長。
    SQLite的數(shù)據(jù)庫本質(zhì)上來講就是一個磁盤上的文件,頻繁打開和關閉是很耗時和浪費資源的,可以設置SQLite的長連接方式;避免頻繁的打開和關閉數(shù)據(jù)庫。
  • 要大量創(chuàng)建局部變量的時候,可以創(chuàng)建內(nèi)嵌的autorelease pool來及時釋放內(nèi)存。
52. @property 的本質(zhì)是什么?ivar、getter、setter 是如何生成并添加到這個類中的。

本質(zhì)就是:@property = ivar + getter + setter;
自動合成,通過@synthesize自動合成了成員變量和getter和setter方法。再往本質(zhì)里說,類本身都是一些結構體,里面有成員變量列表、方法列表等一系列參數(shù),自動合成的過程就是往成員列表里、和方法列表里增加相應的內(nèi)容。

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

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

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