IOS 基礎(chǔ)面試一

1.1 談一談GCD和NSOperation的區(qū)別氮发?

  • 首先二者都是多線程相關(guān)的概念掰读,當(dāng)然在使用中也是根據(jù)不同情境進(jìn)行不同的選擇唧龄;
  • GCD是將任務(wù)添加到隊(duì)列中(串行/并發(fā)/主隊(duì)列)周循,并且制定任務(wù)執(zhí)行的函數(shù)(同步/異步)植兰,其性能最好,底層是C語(yǔ)言的API雇庙,也更輕量級(jí)谓形。iOS4.0以后推出的灶伊,針對(duì)多核處理器的并發(fā)技術(shù)疆前,只能設(shè)置某一個(gè)隊(duì)列的優(yōu)先級(jí),其高級(jí)功能有一次性執(zhí)行dispatch_once聘萨,延遲操作dispatch_after竹椒,調(diào)度組等等;
  • NSOperation把操作(異步)添加到隊(duì)列中(全局的并發(fā)隊(duì)列)米辐,是OC框架胸完,更加面向?qū)ο螅菍?duì)GCD的封裝翘贮,iOS2.0推出赊窥,蘋(píng)果推出GCD之后,對(duì)NSOperation的底層全部重寫(xiě)狸页,可以隨時(shí)取消已經(jīng)設(shè)定準(zhǔn)備要執(zhí)行的任務(wù)锨能,已經(jīng)執(zhí)行的除外,可以設(shè)置隊(duì)列中每一個(gè)操作的優(yōu)先級(jí)芍耘,其高級(jí)功能可以設(shè)置最大操作并發(fā)數(shù)址遇,繼續(xù)/暫停/全部取消,可以快隊(duì)列設(shè)置操作的依賴關(guān)系斋竞。

1.2 談?wù)劧嗑€程的應(yīng)用

通常耗時(shí)的操作都放在子線程處理倔约,然后到主線程更新UI,如

  • 我們要從數(shù)據(jù)庫(kù)提取數(shù)據(jù)還要將數(shù)據(jù)分組后顯示坝初,那么就會(huì)開(kāi)個(gè)子線程來(lái)處理浸剩,處理完成后才去刷新UI顯示。
  • 拍照后鳄袍,會(huì)在子線程處理圖片绢要,完成后才回到主線程來(lái)顯示圖片。拍照出來(lái)的圖片太大了畦木,因此要做處理袖扛。
  • 音頻、視頻處理會(huì)在子線程來(lái)操作
  • 文件較大時(shí),文件操作會(huì)在子線程中處理
  • 做客戶端與服務(wù)端數(shù)據(jù)同步時(shí)蛆封,會(huì)在后臺(tái)閑時(shí)自動(dòng)同步

2. 線程之間是如何通信的唇礁?

通過(guò)主線程和子線程切換的時(shí)候傳遞參數(shù)

1、-(void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array; 
2惨篱、-(void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait; 
3盏筐、-(void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array    
4、-(void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait 
5砸讳、-(void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg  

方法1和方法2提供了子線程到主線程的單向通信
方法3和方法4提供了當(dāng)前線程到任意線程之間的通信琢融,比較靈活
方法5其實(shí)是創(chuàng)建了一個(gè)子線程來(lái)接受selecter

3. 網(wǎng)絡(luò)圖片處理問(wèn)題怎么解決圖片重復(fù)下載問(wèn)題?(SDWebImage大概實(shí)現(xiàn)原理)

  • 這個(gè)就需要用到字典簿寂,以圖片的下載地址url為key漾抬,下載操作為value,所有的圖片大概分成三類:已經(jīng)下載好的常遂,正在下載的和將要下載的纳令;
  • 當(dāng)一張圖片將要進(jìn)行下載操作的時(shí)候,先判斷緩存中是否有相同的圖片克胳,如果有的話就返回平绩,沒(méi)有的話就去沙盒中找,有的話就拿出來(lái)用漠另,沒(méi)有的話再去以圖片的url為key去字典中找有沒(méi)有正在進(jìn)行的任務(wù)捏雌,最后去判斷等待的下載操作任務(wù)里面的字典有無(wú)相同key,如果沒(méi)有笆搓,就自己開(kāi)啟任務(wù)性湿,記錄一下,文件保存的名稱是url的md5值
  • 這里建立了兩個(gè)字典 :
    1.iconCache:保存緩存的圖片
    2.blockOperation用來(lái)保存下載任務(wù)
1501122-e719042afe2fa99e.png.jpeg
  • 每當(dāng)進(jìn)入或退出程序時(shí)砚作,會(huì)進(jìn)行圖片文件的管理:超過(guò)一星期的文件會(huì)被清除窘奏,如果設(shè)置了最大緩存,超過(guò)這個(gè)緩存就會(huì)刪除最舊的文件葫录,直到當(dāng)前緩存文件為最大緩存文件的一半大凶殴;

  • 一般app中大部分緩存都是圖片的情況下米同,可以直接調(diào)用clear方法進(jìn)行清除緩存骇扇,getSize()方法獲取當(dāng)前緩存大小。

4. 多線程安全的幾種解決方法面粮?

  • 1少孝、 只有在主線程刷新訪問(wèn)UI
  • 2、 如果要防止資源搶奪熬苍,需要用synchronize進(jìn)行加鎖保護(hù)
  • 3稍走、 如果是異步操作要保證線程安全等問(wèn)題袁翁,盡量使用GCD(有些函數(shù)默認(rèn)就是安全的)

5. 原子屬性

  • 原子屬性采用的是"多讀單寫(xiě)"機(jī)制的多線程策略;"多讀單寫(xiě)"縮小了鎖范圍,比互斥鎖的性能好
  • 規(guī)定只在主線程更新UI,就是因?yàn)槿绻诙嗑€程中更新,就需要給UI對(duì)象加鎖,防止資源搶占寫(xiě)入錯(cuò)誤,但是這樣會(huì)降低UI交互的性能,所以ios設(shè)計(jì)讓所有UI對(duì)象都是非線程安全的(不加鎖)

6. 代理的作用婿脸、block

  • 代理又叫委托粱胜,是一種設(shè)計(jì)模式(可以理解為java中回調(diào)監(jiān)聽(tīng)機(jī)制),代理是對(duì)象與對(duì)象之間的通信交互狐树,代理解除了對(duì)象之間的耦合性
  • 改變或傳遞控制鏈焙压,允許一個(gè)類在某些特定時(shí)刻通知到其他類,而不需要獲取到那些類的指針抑钟,可以減少框架復(fù)雜度
  • 代理的屬性常是assign的原因:防止循環(huán)引用涯曲,以致對(duì)象無(wú)法得到正確的釋放block底層是根據(jù)函數(shù)指針和結(jié)構(gòu)體結(jié)合實(shí)現(xiàn)的,block本身就是結(jié)構(gòu)體在塔,更加簡(jiǎn)潔幻件,不需要定義繁瑣的協(xié)議方法,但通信事件比較多的話心俗,建議使用Delegate
  • block就是一個(gè)數(shù)據(jù)類型傲武,存放一段代碼蓉驹,編譯的時(shí)候不會(huì)執(zhí)行城榛,只有用到的時(shí)候才會(huì)去執(zhí)行里面的代碼。聲明的時(shí)候使用copy是因?yàn)橐獜臈^(qū)拷貝到堆區(qū)态兴,在棧區(qū)會(huì)受到作用域的限制狠持,超出所在的函數(shù)就會(huì)被銷(xiāo)毀,就沒(méi)辦法進(jìn)行傳值回調(diào)等一系列操作了瞻润。應(yīng)注意循環(huán)引用喘垂,weak來(lái)修飾。如果一個(gè)變量是在block外部創(chuàng)建绍撞,需要在block內(nèi)部修改正勒,那么需要使用block修飾這個(gè)變量(block可以在ARC和MRC情況下使用,可以修飾對(duì)象和基本數(shù)據(jù)類型傻铣,weak只能在ARC下使用章贞,只能修飾對(duì)象,不能修飾基本數(shù)據(jù)類型)
  • 最常用的是使用block作為參數(shù)傳值非洲,不同情況下回調(diào)不同的代碼(如成功回調(diào)失敗回調(diào))

7. 談?wù)勀銓?duì)runTime運(yùn)行時(shí)機(jī)制的了解(注意哦鸭限,這個(gè)很重要的)

runtime是一套比較底層的純C語(yǔ)言API,屬于一個(gè)C語(yǔ)言庫(kù)两踏,包含了很多底層的C語(yǔ)言的API

  • 平時(shí)編寫(xiě)的OC代碼败京,在程序運(yùn)行過(guò)程中,其實(shí)都是轉(zhuǎn)成了runtime的C語(yǔ)言代碼梦染,runtime是OC的幕后工作者赡麦,底層語(yǔ)言,例如:
OC--> [[WPFPerson alloc] init]
runtime-->objc_msgSend(objc_msgSend("WPFPerson", "alloc"), "init")
  • 利用runtime可以實(shí)現(xiàn)一些非常底層的操作(用OC不好實(shí)現(xiàn))
  1. 在程序運(yùn)行過(guò)程中,動(dòng)態(tài)創(chuàng)建一個(gè)類(比如KVO底層實(shí)現(xiàn):檢測(cè)isa指針泛粹,發(fā)現(xiàn)是新建了一個(gè)類车荔,當(dāng)然Xcode7.0以前的版本才可以監(jiān)聽(tīng)到isa指針)
  2. 遍歷一個(gè)類的所有成員變量、方法戚扳,訪問(wèn)私有變量(先通過(guò)runtime的class_getInstanceVariable獲取成員變量忧便,再通過(guò)class_getIvar獲取它的值)
  3. 在程序運(yùn)行過(guò)程中,動(dòng)態(tài)為某個(gè)類添加屬性\方法帽借,修改屬性值\方法珠增,比如產(chǎn)品經(jīng)理需要跟蹤記錄APP中按鈕的點(diǎn)擊次數(shù)和頻率等數(shù)據(jù),可以通過(guò)繼承按鈕或者類別實(shí)現(xiàn)砍艾,但是帶來(lái)的問(wèn)題比如別人不一定去實(shí)例化你寫(xiě)的子類蒂教,或者其他類別也實(shí)現(xiàn)了點(diǎn)擊方法導(dǎo)致不確定會(huì)調(diào)用哪一個(gè),runtime可以這樣解決:在按鈕的分類里面脆荷,重寫(xiě)load方法凝垛,利用dispatch_once保證只執(zhí)行一次,新建監(jiān)控按鈕點(diǎn)擊的方法蜓谋,先用class_addMethod方法梦皮,判斷其返回的bool值,如果添加成功桃焕,就用class_replaceMethod將原來(lái)的方法移除剑肯,如果添加失敗,就用method_exchangeImplementations方法進(jìn)行替換
  4. 攔截并替換方法观堂,比如由于某種原因让网,我們要改變這個(gè)方法的實(shí)現(xiàn),但是又不能動(dòng)它的源碼(比如一些開(kāi)源庫(kù)出現(xiàn)問(wèn)題的時(shí)候师痕,這時(shí)候runtime就可以出場(chǎng)了)-->先增加一個(gè)tool類溃睹,然后寫(xiě)一個(gè)我們自己實(shí)現(xiàn)的方法-change,通過(guò)runtime的class_getInstanceMethod獲取兩個(gè)方法胰坟,在用class_replaceMethod方法進(jìn)行替換因篇。防止數(shù)組越界的方法:數(shù)組越界的時(shí)候報(bào)錯(cuò)的方法是add_object,做一個(gè)邏輯判斷腕铸,越界的時(shí)候通過(guò)class_replaceMethod交換掉add_object(相當(dāng)于重寫(xiě)了這個(gè)方法)
  • 相關(guān)應(yīng)用

1.NSCoding(歸檔和解檔)惜犀,如果一個(gè)模型有很多個(gè)屬性,那么需要對(duì)每個(gè)屬性都實(shí)現(xiàn)一遍encodeObject和decodeObjectForKey方法狠裹,十分麻煩虽界,但是如果使用class_copyIvarList獲取所有屬性,然后循環(huán)遍歷涛菠,使用[ivarName substringFromIndex:1]去掉成員變量下劃線

  1. 字典轉(zhuǎn)模型:像幾個(gè)出名的開(kāi)源庫(kù)JSONModel莉御、MJExtension等都是通過(guò)這種方式實(shí)現(xiàn)的(利用runtimeclass_copyIvarList獲取屬性數(shù)組撇吞,遍歷模型對(duì)象的所有成員屬性,根據(jù)屬性名找到字典中key值進(jìn)行賦值礁叔,當(dāng)然這種方法只能解決NSString牍颈、NSNumber等,如果含有NSArray或NSDictionary琅关,還要進(jìn)行第二步轉(zhuǎn)換煮岁,如果是字典數(shù)組,需要遍歷數(shù)組中的字典涣易,利用objectWithDict方法將字典轉(zhuǎn)化為模型画机,在將模型放到數(shù)組中,最后把這個(gè)模型數(shù)組賦值給之前的字典數(shù)組)
  • Method Swizzling:OC中調(diào)用方法事實(shí)上就是向?qū)ο蟀l(fā)送消息新症,而查找消息的唯一依據(jù)就是selector的名字步氏,因此可以使用runtime運(yùn)行時(shí)機(jī)制動(dòng)態(tài)交換方法。在+load方法里面調(diào)換徒爹,因?yàn)?code>method swizzling的影響范圍是全局的荚醒,所以應(yīng)該放在最保險(xiǎn)的地方來(lái)處理,+load方法能夠保證能在類初始化的時(shí)候一定能被調(diào)用隆嗅,可以保證統(tǒng)一性界阁,如果是在使用的時(shí)候才去調(diào)用,可能達(dá)不到全局處理的效果榛瓮;使用dispatch_once保證只交換一次铺董。
[objc_getClass("__NSArrayM") swizzleSelector:@selector(addObject:)
withSwizzledSelector:@selector(hyb_safeAddObject:)];

使用場(chǎng)景:addObject方法添加的值為nil的時(shí)候會(huì)崩潰。調(diào)用objectAtIndex:時(shí)出現(xiàn)崩潰提示empty數(shù)組問(wèn)題

8. 談?wù)勀銓?duì)Run Loop的理解

  • RunLoop是多線程的一個(gè)很重要的機(jī)制禀晓,就是一個(gè)線程一次只能執(zhí)行一個(gè)任務(wù),執(zhí)行完任務(wù)后就會(huì)退出線程坝锰。主線程會(huì)通過(guò)do-while死循環(huán)讓程序持續(xù)等待下一個(gè)任務(wù)不退出粹懒。通過(guò)mach_msg()讓runloop沒(méi)事時(shí)進(jìn)入trap狀態(tài),節(jié)省CPU資源顷级。非主線程通常來(lái)說(shuō)就是為了執(zhí)行某個(gè)任務(wù)而創(chuàng)建的凫乖,執(zhí)行完就會(huì)歸還資源,因此默認(rèn)不開(kāi)啟RunLoop
  • 實(shí)質(zhì)上弓颈,對(duì)于子線程的runloop是默認(rèn)不存在的帽芽,因?yàn)樘O(píng)果采用了懶加載的方式,如果沒(méi)有手動(dòng)調(diào)用[NSRunLoop currentRunLoop]的話翔冀,就不會(huì)去查詢當(dāng)前線程的RunLoop导街,也不會(huì)創(chuàng)建逾苫、加載
  • 當(dāng)然如果子線程處理完某個(gè)任務(wù)后不退出便脊,需要繼續(xù)等待接受事件,需要啟動(dòng)的時(shí)候也可以手動(dòng)啟動(dòng)踏幻,比如說(shuō)添加定時(shí)器的時(shí)候就要手動(dòng)開(kāi)始RunLoop
8.1 如何處理事件
  • 界面刷新: 當(dāng)UI改變( Frame變化、 UIView/CALayer 的繼承結(jié)構(gòu)變化等)時(shí)泽论,或手動(dòng)調(diào)用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后艾少,這個(gè) UIView/CALayer 就被標(biāo)記為待處理。 蘋(píng)果注冊(cè)了一個(gè)用來(lái)監(jiān)聽(tīng)BeforeWaiting和Exit的Observer翼悴,在它的回調(diào)函數(shù)里會(huì)遍歷所有待處理的 UIView/CAlayer 以執(zhí)行實(shí)際的繪制和調(diào)整缚够,并更新 UI 界面。
  • 手勢(shì)識(shí)別: 如果上一步的 _UIApplicationHandleEventQueue() 識(shí)別到是一個(gè)guesture手勢(shì)鹦赎,會(huì)調(diào)用Cancel方法將當(dāng)前的touchesBegin/Move/End 系列回調(diào)打斷潮瓶。隨后系統(tǒng)將對(duì)應(yīng)的 UIGestureRecognizer 標(biāo)記為待處理。 蘋(píng)果注冊(cè)了一個(gè) Observer 監(jiān)測(cè) BeforeWaiting (Loop即將進(jìn)入休眠) 事件钙姊,其回調(diào)函數(shù)為 _UIGestureRecognizerUpdateObserver()毯辅,其內(nèi)部會(huì)獲取所有剛被標(biāo)記為待處理的 GestureRecognizer,并執(zhí)行GestureRecognizer的回調(diào)煞额。 當(dāng)有 UIGestureRecognizer 的變化(創(chuàng)建/銷(xiāo)毀/狀態(tài)改變)時(shí)思恐,這個(gè)回調(diào)都會(huì)進(jìn)行相應(yīng)處理。
  • 網(wǎng)絡(luò)請(qǐng)求:最底層是CFSocket層膊毁,然后是CFNetwork將其封裝胀莹,然后是NSURLConnection對(duì)CFNetwork進(jìn)行面向?qū)ο蟮姆庋b,NSURLConnection是iOS7中新增的接口婚温。當(dāng)網(wǎng)絡(luò)開(kāi)始傳輸時(shí)描焰,NSURLConnection創(chuàng)建了兩個(gè)新線程:com.apple.NSURLConnectionLoader和com.apple.CFSocket.private。其中CFSocket線程是處理底層socket連接的栅螟。NSURLConnectionLoader這個(gè)線程內(nèi)部會(huì)使用RunLoop來(lái)接受底層socket的事件荆秦,并添加到上層的Delegate
8.2 應(yīng)用
  • 滑動(dòng)與圖片刷新:當(dāng)tableView的cell上有需要從網(wǎng)絡(luò)獲取的圖片的時(shí)候,滾動(dòng)tableView力图,異步線程回去加載圖片步绸,加載完成后主線程會(huì)設(shè)置cell的圖片,但是會(huì)造成卡頓吃媒∪拷椋可以設(shè)置圖片的任務(wù)在CFRunloopDefaultMode下進(jìn)行,當(dāng)滾動(dòng)tableView的時(shí)候赘那,Runloop切換到UITrackingRunLoopMode刑桑,不去設(shè)置圖片,而是當(dāng)停止的時(shí)候募舟,再去設(shè)置圖片祠斧。(在viewDidLoad中調(diào)用self.imageView performSelector@selector(setImage) withObject:...afterDelay:...inModes@[NSDefayltRunLoopMode]
  • 常駐子線程,保持子線程一直處理事件 為了保證線程長(zhǎng)期運(yùn)轉(zhuǎn)胃珍,可以在子線程中加入RunLoop梁肿,并且給Runloop設(shè)置item蜓陌,防止Runloop自動(dòng)退出

9. SQLite常用的SQL語(yǔ)句

  • 創(chuàng)建表:create table 表名(字段名 字段數(shù)據(jù)類型 是否為主鍵, 字段名 字段數(shù)據(jù)類型, 字段名 字段數(shù)據(jù)類型...)
  • 增:insert into 表名(字段1,字段2...) values(值1吩蔑,值2...)
  • 刪:delete from 表名 where 字段=值

10. 關(guān)于Socket钮热,談?wù)凾CP/IP 和 UDP的理解

  • Socket是一個(gè)用于傳輸網(wǎng)絡(luò)數(shù)據(jù)的工具,TCP/IP 和 UDP都是傳輸協(xié)議烛芬,用于定義網(wǎng)絡(luò)數(shù)據(jù)傳輸?shù)母袷剿砥冢瑢儆陂L(zhǎng)連接
  • TCP/IP 側(cè)重可靠傳輸,傳輸速度慢赘娄,不會(huì)丟失數(shù)據(jù)仆潮,安全,聊天和下載文件時(shí)用到
  • UDP:側(cè)重快速傳輸遣臼,傳輸速度快性置,容易丟失數(shù)據(jù)包,不安全揍堰。局域網(wǎng)游戲和網(wǎng)絡(luò)游戲鹏浅,視頻聊天的時(shí)候用到
  • TCP更安全是因?yàn)橛幸粋€(gè)三次握手:第一次握手(客戶端發(fā)送syn包到服務(wù)器,并進(jìn)入SYN_SEND狀態(tài)屏歹,等待服務(wù)器確認(rèn))隐砸,第二次握手(服務(wù)器收到syn包,必須確認(rèn)客戶的SYN包蝙眶,同時(shí)自己發(fā)送一個(gè)SYN+ACK包季希,此時(shí)服務(wù)器進(jìn)入SYN_RECV狀態(tài)),第三次握手(客戶端收到服務(wù)器的SYN+ACK包幽纷,向服務(wù)器發(fā)送確認(rèn)包ACK式塌,發(fā)送完畢后服務(wù)器和客戶端都進(jìn)入ESTABLISHED狀態(tài),完成三次握手)霹崎,三次握手之后才開(kāi)始正式傳輸數(shù)據(jù)珊搀。因此使用TCP協(xié)議的MSN比采用UDP的QQ傳輸速度慢,但并不代表UDP不安全尾菇,因?yàn)槌绦騿T可以手動(dòng)對(duì)UDP的數(shù)據(jù)收發(fā)進(jìn)行驗(yàn)證(比如發(fā)送方對(duì)每個(gè)數(shù)據(jù)包進(jìn)行編號(hào)然后由接收方進(jìn)行驗(yàn)證)
  • Http:超文本傳輸協(xié)議,用于定義網(wǎng)絡(luò)數(shù)據(jù)傳輸?shù)母袷?短鏈接)http1.0之前不支持短連接囚枪,1.1之后默認(rèn)就是長(zhǎng)連接派诬,只要在服務(wù)器和客戶端同時(shí)設(shè)置Connection為keep-alive即可
  • 長(zhǎng)連接是為了復(fù)用,長(zhǎng)連接指的是TCP連接链沼,也就是為了復(fù)用TCP連接默赂,也就是說(shuō)多個(gè)HTTP請(qǐng)求可以復(fù)用一個(gè)TCP連接,節(jié)省了很多TCP連接建立和斷開(kāi)的消耗
  • 比如請(qǐng)求了一個(gè)網(wǎng)頁(yè)括勺,這個(gè)網(wǎng)頁(yè)肯定還包含了CSS缆八、JS等一系列資源曲掰,如果是短連接的話,每次打開(kāi)一個(gè)網(wǎng)頁(yè)奈辰,基本要建立幾個(gè)甚至幾十個(gè)TCP連接栏妖,浪費(fèi)了大量資源
  • 長(zhǎng)連接不是永久連接,如果一段時(shí)間內(nèi)奖恰,具體的時(shí)間長(zhǎng)短吊趾,是可以在header當(dāng)中進(jìn)行設(shè)置的,也就是所謂的超時(shí)時(shí)間瑟啃,這個(gè)連接沒(méi)有HTTP請(qǐng)求發(fā)出的話论泛,那么這個(gè)長(zhǎng)連接就會(huì)被斷掉
  • socket連接是長(zhǎng)連接,客戶端與服務(wù)器保持通道蛹屿,雙方可以主動(dòng)發(fā)送數(shù)據(jù)屁奏,一般多用于即時(shí)通訊,游戲错负,默認(rèn)超時(shí)時(shí)間是30秒坟瓢,默認(rèn)大小是8k(一個(gè)數(shù)據(jù)包大小)

11. 談一談內(nèi)存管理

  • iOS的內(nèi)存管理分為 MRC 和 ARC湿颅,管理的是堆區(qū)動(dòng)態(tài)產(chǎn)生的對(duì)象载绿,基本數(shù)據(jù)類型就不是內(nèi)存管理的范圍
    內(nèi)存管理的核心概念是引用計(jì)數(shù)器:當(dāng)對(duì)象被alloc、copy油航、new的時(shí)候崭庸,引用計(jì)數(shù)器+1,當(dāng)被release的時(shí)候引用計(jì)數(shù)器—1谊囚,為0的時(shí)候就會(huì)被系統(tǒng)回收怕享,調(diào)用dealloc方法

  • 說(shuō)道內(nèi)存管理,就必須說(shuō)說(shuō)@property的內(nèi)存管理參數(shù):retain --> release 一次舊對(duì)象 retain 一次新對(duì)象 (適用于OC對(duì)象類型)
    copy --> release 一次舊對(duì)象 拷貝一個(gè)新對(duì)象出來(lái)

  • 如何避免內(nèi)存泄露 --> 使用Analyze進(jìn)行代碼的靜態(tài)分析
    當(dāng)然使用block的時(shí)候最應(yīng)該注意下循環(huán)引用镰踏,使用Leaks檢測(cè)內(nèi)存泄露函筋,顯示綠色的勾告知內(nèi)存處理的不錯(cuò),實(shí)際上內(nèi)存得不到釋放奠伪。一般我的方法是在控制器生命周期的viewDidAppear和dealloc方法里面打印日志[[self class] description]跌帐,如果沒(méi)有打印出來(lái),就說(shuō)明沒(méi)有被釋放绊率。使用**__weak typeof(self) weakSelf = self;解決谨敛。有一次我是直接使用成員變量,而不是屬性滤否,_age脸狸,我以為這樣沒(méi)有使用self就可以了,但是后來(lái)測(cè)試發(fā)現(xiàn)還是造成循環(huán)引用了藐俺,因?yàn)開(kāi)age是控制器的成員變量泥彤,也就是強(qiáng)引用了控制器吟吝,也要改成弱引用__block **weak __typeof(_currentModel) weakModel = _currentModel;

12. 常見(jiàn)的數(shù)據(jù)持久化有哪些

  • 偏好設(shè)置(preference),利用NSUserDefaults用來(lái)保存應(yīng)用程序設(shè)置和屬性爸黄、用戶保存的數(shù)據(jù)揭鳞。用戶再次打開(kāi)程序或開(kāi)機(jī)后這些數(shù)據(jù)仍然存在
  • NSUserDefaults可以存儲(chǔ)的數(shù)據(jù)類型包括:NSData炕贵、NSString、NSNumber鳖轰、NSDate蕴侣、NSArray、NSDictionary狞膘。如果要存儲(chǔ)其他類型挽封,需要先轉(zhuǎn)化為前面的類型,才能用NSUserDefault存儲(chǔ)
  • 偏好設(shè)置是專門(mén)用來(lái)保存應(yīng)用程序的配置信息的忆某,一般不要在偏好設(shè)置中保存其他數(shù)據(jù)
  • 偏好設(shè)置會(huì)將所有數(shù)據(jù)保存到同一個(gè)文件中褒繁。即preference目錄下的一個(gè)以此應(yīng)用包名來(lái)命名的plist文件燕差。
//1.獲得NSUserDefaults文件
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
//2.向文件中寫(xiě)入內(nèi)容
[userDefaults setObject:@"AAA" forKey:@"a"];
[userDefaults setBool:YES forKey:@"sex"];
[userDefaults setInteger:21 forKey:@"age"];
//2.1立即同步
[userDefaults synchronize];
//3.讀取文件
NSString *name = [userDefaults objectForKey:@"a"];
BOOL sex = [userDefaults boolForKey:@"sex"];
NSInteger age = [userDefaults integerForKey:@"age"];
NSLog(@"%@, %d, %ld", name, sex, age);
  • 歸檔(Archiver)徒探、解檔(unArchiver),利用NSKeyedArchiver實(shí)現(xiàn)歸檔、利用NSKeyedUnarchiver反接的那個(gè)
  • 歸檔及時(shí)將內(nèi)存中的對(duì)象寫(xiě)入到磁盤(pán)文件中稚字,歸檔也叫序列化,解檔就是講磁盤(pán)中文件中的對(duì)象讀取出來(lái)
  • 必須遵循NSCoding協(xié)議昌讲,只要遵循了NSCoding協(xié)議的對(duì)象都可以通過(guò)它實(shí)現(xiàn)序列化,兩個(gè)協(xié)議方法必須實(shí)現(xiàn)
// 反歸檔
 (id)initWithCoder:(NSCoder *)aDecoder {
    if ([super init]) { 
        self.avatar = [aDecoder decodeObjectForKey:@"avatar"];
        self.name = [aDecoder decodeObjectForKey:@"name"]; 
        self.age = [aDecoder decodeIntegerForKey:@"age"];
    }
    return self;
}

// 歸檔
- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:self.avatar forKey:@"avatar"];
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeInteger:self.age forKey:@"age"];
}
    • 歸檔鸠按,把對(duì)象歸檔時(shí)需要調(diào)用NSKeyedArchiver的工廠方法archiveRootObject: toFile: 方法
NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"];
Person *person = [[Person alloc] init];
person.avatar = self.avatarView.image;
person.name = self.nameField.text;
person.age = [self.ageField.text integerValue];
[NSKeyedArchiver archiveRootObject:person toFile:file];
  • 反歸檔
NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"];
Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
if (person) {
        self.avatarView.image = person.avatar;
        self.nameField.text = person.name;
        self.ageField.text = [NSString stringWithFormat:@"%ld", person.age];
}
這五種持久化操作不同點(diǎn)
  • 從存儲(chǔ)數(shù)據(jù)大小來(lái)看,歸檔瑟曲、偏好設(shè)置负拟、屬性列表三種方法適合存儲(chǔ)數(shù)據(jù)量較小的數(shù)據(jù)花吟,數(shù)據(jù)庫(kù)衅澈、CoreData方法適合存儲(chǔ)數(shù)據(jù)量較大的數(shù)據(jù)

  • 從加密性來(lái)看经备,其中歸檔會(huì)將數(shù)據(jù)進(jìn)行加密,而偏好設(shè)置是直接保存到屬性列表中,不會(huì)對(duì)數(shù)據(jù)進(jìn)行加密

  • 從存儲(chǔ)類型來(lái)看急但,屬性列表只能存放固定的七種類型(可在plist文件中看到),歸檔對(duì)存儲(chǔ)類型無(wú)限制

13. KVC 和 KVO

  • KVC(key-value-coding鍵值編碼镐躲,跟多情況下會(huì)簡(jiǎn)化程序代碼)的常見(jiàn)用法:
  • 給私有變量(該變量不對(duì)外開(kāi)放)賦值:[Person setValue: @"19" ForKeyPath:@"age"]
  • 字典轉(zhuǎn)模型:setValuesForKeyWithDictionary
  • 取出私有變量:[Person valueForKey:@"age"]
  • 沒(méi)有找到對(duì)應(yīng)的key會(huì)崩潰:重寫(xiě)setValueForUndefinedKey
  • KVC缺點(diǎn):一旦使用KVC,編譯器無(wú)法檢查出錯(cuò)誤,即不會(huì)對(duì)設(shè)置的鍵入录、鍵路徑進(jìn)行錯(cuò)誤檢查,且執(zhí)行效率低于自定義的setter和getter方法蚀同,因?yàn)槭褂肒VC鍵值編值拷恨,必須先解析字符串小泉,然后設(shè)置或訪問(wèn)對(duì)象的實(shí)例變量

  • 通過(guò)KVO(key-value-observing酸茴,典型的觀察者模式,被觀察的對(duì)象必須使用KVC鍵值編碼來(lái)修改它的實(shí)例變量酪穿,這樣才能被觀察者觀察到)監(jiān)聽(tīng)person對(duì)象中name屬性發(fā)生改變

  • 給監(jiān)聽(tīng)的屬性設(shè)置一個(gè)觀察者:
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
  • 當(dāng)person的name的值發(fā)生改變時(shí),就會(huì)執(zhí)行該方法
(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{ 
do something....
}
  • 當(dāng)一個(gè)類的屬性被觀察的時(shí)候,系統(tǒng)會(huì)通過(guò)runtime動(dòng)態(tài)的創(chuàng)建一個(gè)該類的派生類,并且會(huì)在這個(gè)類中重寫(xiě)基類被觀察的屬性的setter方法钮追,而且系統(tǒng)將這個(gè)類的isa指針指向了派生類(NSNotifying_類名),從而實(shí)現(xiàn)了給監(jiān)聽(tīng)的屬性賦值時(shí)調(diào)用的是派生類的setter方法。重寫(xiě)的setter方法會(huì)在調(diào)用原setter方法前后鞠绰,通知觀察對(duì)象值得改變屿笼。

14. @synthesize和@dynamic區(qū)別是什么

  • 這兩個(gè)關(guān)鍵字都是@property對(duì)應(yīng)的詞
  • @synthesize 語(yǔ)義是如果沒(méi)有手動(dòng)實(shí)現(xiàn)setter和getter方法,那么編譯器會(huì)自動(dòng)幫你加上這兩個(gè)方法
  • @dynamic告訴編譯器肝断,屬性的setter和getter由用戶自己實(shí)現(xiàn),不自動(dòng)生成(readOnly只實(shí)現(xiàn)getter即可),但是如果沒(méi)有自己實(shí)現(xiàn),編譯的餓時(shí)候不會(huì)報(bào)錯(cuò)井联,運(yùn)行的時(shí)候就會(huì)報(bào)錯(cuò),這就是所謂的動(dòng)態(tài)綁定

15. 談?wù)剷r(shí)間響應(yīng)鏈的一般順序

  • 一般來(lái)說(shuō),第一響應(yīng)者是個(gè)視圖對(duì)象或者其他子類對(duì)象蝗锥,當(dāng)其被觸摸以后事件便交由他處理,如果他不處理穴张,事件就會(huì)被傳遞給它的視圖控制器對(duì)象viewController(如果存在),然后是它的父視圖對(duì)象(superView)(如果存在)璧瞬,以此類推,直到頂層視圖瘟忱,接著沿著頂層視圖(topView)到窗口(UIWindow對(duì)象)再到程序(Application對(duì)象)。如果整個(gè)過(guò)程都沒(méi)有響應(yīng)這個(gè)時(shí)間棒拂,該事件就會(huì)被拋棄谜诫。一般情況下,在響應(yīng)鏈中只要有UI系那個(gè)處理事件,事件就會(huì)停止傳遞

16. post和get方式的區(qū)別

  • GET請(qǐng)求的數(shù)據(jù)會(huì)負(fù)載URL之后锋谐,即把數(shù)據(jù)放在HTTP協(xié)議頭中,以?區(qū)分URL和傳輸數(shù)據(jù),參數(shù)之間以&相連从藤,英文字母/數(shù)字懊蒸,原樣發(fā)送,如果是空格通危,轉(zhuǎn)化為+,如果是中文,把字符串用BASE64加密魄幕;POST就是把提交的數(shù)據(jù)放在HTTP包的包體中

  • GET一般用于提交少量數(shù)據(jù)(最多提交1k留储,瀏覽器限制)机久,POST用于提交大量數(shù)據(jù)(理論上無(wú)限制,收服務(wù)器限制)

  • GET提交的數(shù)據(jù)可以在瀏覽器歷史記錄中看到,安全性不好软棺,別人可以拿到賬號(hào)密碼

  • Get是向服務(wù)器發(fā)索取數(shù)據(jù)的一種請(qǐng)求,而POST是向服務(wù)器發(fā)提交數(shù)據(jù)的一種請(qǐng)求稀火,只是發(fā)送機(jī)制不同

  • GET不可以設(shè)置書(shū)簽沛慢,POST可以設(shè)置書(shū)簽

什么情況下用POST:
  • 請(qǐng)求的結(jié)果具有持續(xù)性副作用逾冬,如數(shù)據(jù)庫(kù)添加新的數(shù)據(jù)行
    若使用get方法,則表單上手機(jī)的數(shù)據(jù)可能讓URL過(guò)長(zhǎng)
    要傳送的數(shù)據(jù)不是采用7位的ASCII編碼
什么情況下用GET:
  • 請(qǐng)求是為了查找資源雕沉,HTML表單數(shù)據(jù)僅用來(lái)幫助搜索
    請(qǐng)求結(jié)果無(wú)持續(xù)副作用性的副作用
    手機(jī)的數(shù)據(jù)及HTML表單內(nèi)的輸入字段名稱的總長(zhǎng)不超過(guò)1024個(gè)字符

17. 深復(fù)制和淺復(fù)制

  • 非集合類對(duì)immutable對(duì)象進(jìn)行copy操作,是指針復(fù)制汗唱,mutableCopy操作時(shí)內(nèi)容復(fù)制
  • 非集合類對(duì)mutable對(duì)象進(jìn)行copy和mutableCopy都是內(nèi)容復(fù)制巡验。
  • 在集合類對(duì)象中框弛,對(duì)immutable對(duì)象進(jìn)行copy,是指針復(fù)制,mutableCopy是內(nèi)容復(fù)制
  • 在集合類對(duì)象中溅呢,對(duì)mutable對(duì)象進(jìn)行copy和mutableCopy都是內(nèi)容復(fù)制。但是:集合對(duì)象的內(nèi)容復(fù)制僅限于對(duì)象本身铣墨,對(duì)象元素仍然是指針復(fù)制孕蝉。
  • NSString *str = @"string"; str = @"newString"; 打印對(duì)象地址超埋,發(fā)現(xiàn)是發(fā)生變化的霍殴,需要把@"newStirng"當(dāng)做一個(gè)新的對(duì)象,將這段對(duì)象的內(nèi)存地址賦值給str

18. 關(guān)于項(xiàng)目中動(dòng)畫(huà)的使用

  • 序列幀動(dòng)畫(huà):self.imageView.animationImages = array;
  • [UIView animateWithDuration] + CGAffinetransform
  • 核心動(dòng)畫(huà)
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position.y"]; 
anim.fromValue toValue repeatCount [btn.layer addAnimation]
  • 關(guān)鍵幀動(dòng)畫(huà)CAKeyframeAnimation系吩,anim.values = array来庭,添加到layer上
  • 組動(dòng)畫(huà)CAAnimationGroup,將以上動(dòng)畫(huà)組合起來(lái)
    轉(zhuǎn)場(chǎng)動(dòng)畫(huà):CATransition月弛,設(shè)置durationtype,然后添加到layer上絮蒿。利用UIView 的類方法實(shí)現(xiàn)轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
[UIView transitionWithView: duration: options: animations:^{ } completion:nil];
  • UIDynamicAnimator仿真者 尊搬、 UISnapBehavior吸附行為,設(shè)置damping來(lái)調(diào)節(jié)震動(dòng)幅度 土涝、UIPushBehavior推動(dòng)行為 佛寿、 UICollisionBehavior碰撞邊緣檢測(cè)行為 、 UIAttachmentBehavior附著行為 、 UIGravityBehavior重力行為
    POPSpringAnimation
  • springBounciness[0,20]越大振幅越大冀泻。
  • springSpeed速度

19. 談?wù)勀銓?duì)MVVM的認(rèn)識(shí)

  • 說(shuō)到MVVM常侣,就必須先說(shuō)MVC胳施,MVC就是Model顯示數(shù)據(jù)舞肆,View呈現(xiàn)用戶界面椿胯,Controller調(diào)節(jié)二者之間的交互,但是這個(gè)結(jié)構(gòu)存在的問(wèn)題就是模型的代碼太少剃根,而隨著功能的增加控制器的代碼會(huì)越來(lái)越多哩盲,而且不方便測(cè)試(比如辦一個(gè)飯卡,默認(rèn)一百元)狈醉;
  • MVVM中廉油,viewview controller正式聯(lián)系在一起,我們將它視為一個(gè)組件苗傅,這兩個(gè)都不能直接引用model抒线,而是引用視圖模型view model,里面放置著用戶輸入驗(yàn)證邏輯渣慕,視圖顯示邏輯十兢,發(fā)送網(wǎng)絡(luò)請(qǐng)求等代碼,view model再去引用model
    這個(gè)的優(yōu)點(diǎn)呢摇庙,首先就是低耦合,view可以獨(dú)立于model變化和修改遥缕,一個(gè)viewModel可以綁定到不同的view上卫袒,可重用性单匣,也有利于獨(dú)立開(kāi)發(fā),開(kāi)發(fā)人員可以專注業(yè)務(wù)邏輯和數(shù)據(jù)的開(kāi)發(fā)转砖,設(shè)計(jì)人員可以專注于頁(yè)面設(shè)計(jì)
  • 實(shí)際用例
  • 樓市餐廳需要充餐卡姓赤,默認(rèn)是100元,在模型層中只洒,如果想保存這個(gè)值 --> let balanece = 100
  • 但是展示給用戶的時(shí)候泡垃,我們想呈現(xiàn)出“您的賬戶余額為:¥100”忠寻,在mvc中,這種代碼只能放到視圖控制器中,顯得很臃腫,如果放在模型當(dāng)中聂薪,會(huì)更丑耀找,因?yàn)橛性S多進(jìn)行格式化的代碼擠在其中
    如果添加視圖模型腻要,僅僅需要映射一下原始數(shù)據(jù)
struct AccountViewModel {
        let dispalyBalance: String
        init(mode: cardAccount) { 
            let formattedBalance = model.balance.currencyValue;
            displayBalance = "Your balance is \(formatterBalance)"
        }
}
  • 通過(guò)這種方式胀滚,視圖模型實(shí)際上會(huì)讀取數(shù)據(jù)模型,然后將其中的信息進(jìn)行格式化媳纬,從而準(zhǔn)備展現(xiàn)在視圖當(dāng)中素挽,很容易測(cè)試,直接把帶有賬戶信息的模型放進(jìn)去,然后測(cè)試顯示就可以了窗慎,在之前是特別復(fù)雜的
  • 在去年WWDC上扇丛,Andy(iOS4.1-8的UIKit框架維護(hù)者)演講中有一個(gè)關(guān)于zoetrope(西洋鏡)的例子
  • 大概意思就是每一幀都是靜態(tài)值,可以通過(guò)改變?nèi)蝿?wù)手部抬起的距離,或者任務(wù)頭部?jī)A斜的距離,來(lái)對(duì)字符進(jìn)行編碼曼振。每一幀都是靜態(tài)的木羹,但是當(dāng)把他們放在一起穷遂,然后一直看向一個(gè)中心的話,那么始終都有新的數(shù)據(jù)出現(xiàn),這樣就可以得到一個(gè)而美麗的屈芜、生動(dòng)的動(dòng)畫(huà)(類比Tom貓)
  • 我們可以使用相同的方式來(lái)實(shí)現(xiàn)值類型躬翁,視圖控制器會(huì)跟隨zoetrope的最后一個(gè)幀圖像--也就是最新的一塊活躍數(shù)據(jù)例嘱,然后將其展示給用戶腋腮。只要您的模型發(fā)生了更新嘿悬,視圖就會(huì)根據(jù)最新的信息進(jìn)行更改了
var viewModel = ViewModel(model: Account)

20.0 關(guān)于Swift與OC的不同

其實(shí)是個(gè)面試官八個(gè)不懂Swift钢拧,而且一般不懂得就問(wèn)個(gè)Swift與OC的不同。題主也只是在自學(xué)Swift卿嘲,等到3.0出了之后再深入研究沃疮,而且項(xiàng)目中可能也要開(kāi)始從混編逐漸向Swift靠攏了。

  • Swift是一門(mén)更加現(xiàn)代化的語(yǔ)言梅肤,但是目前還在成長(zhǎng)階段司蔬,更新改動(dòng)比較大,雖然說(shuō)其底層思想不變姨蝴,變的是API和接口炒辉,但是每次更新完Xcode看到自己的Swift項(xiàng)目還是有些淡淡的憂傷,而且目前Swift開(kāi)發(fā)都要轉(zhuǎn)成OC的runtime,包略大恕曲,因此題主認(rèn)為成熟項(xiàng)目最好還是采用OC

  • 先記住一句話:OC底層面向?qū)ο筇谖眩鳶wift底層更加面向協(xié)議

  • 我們已經(jīng)見(jiàn)識(shí)過(guò)Apple使用了大量協(xié)議咒精,比如在tableView當(dāng)中,我們可以通過(guò)協(xié)議來(lái)告訴Apple需要多少個(gè)表視圖單元格艇纺,而不是每時(shí)每刻都要繼承UITableViewController

  • 在這里以MVVM作為測(cè)試用例:比如現(xiàn)在需要建立一個(gè)類似設(shè)置界面的tableView袋毙,每個(gè)cell需要一個(gè)label和一個(gè)switch,自定義SwitchWithTextTableViewCell,在其內(nèi)部建立一個(gè)configure方法中對(duì)labeltitletitleFonttitleColor耿导,switchswitchOnswitchColor等進(jìn)行初始化雅采,但這種方式非常累贅骤星,比如添加一個(gè)副標(biāo)題潭袱,就需要額外添加三個(gè)屬性

  • 但是利用協(xié)議SwitchWithTextCellProtocol露筒,讓視圖模型實(shí)現(xiàn)這個(gè)協(xié)議,然后在這里設(shè)置所有的屬性

protocol SwitchWithTextCellProtocol {
      var title: String { get } 
      var titleFont: UIFont { get } 
      var titleColor: UIColor { get }
      var switchOn: Bool { get }
      var switchColor: UIColor { get } 
      func onSwitchTogglenOn(onL Bool)
}
  • 通過(guò)swift2.0重點(diǎn)餓協(xié)議擴(kuò)展敌卓,就可以通過(guò)默認(rèn)值來(lái)做一些處理了,如果對(duì)于大多數(shù)單元格來(lái)說(shuō)伶氢,可以確定某一種顏色的話趟径,就可以對(duì)其建立擴(kuò)展,然后設(shè)置顏色即可癣防,所有實(shí)現(xiàn)此協(xié)議的視圖就沒(méi)有必要再去設(shè)置這個(gè)顏色了

  • 現(xiàn)在蜗巧,我的configure方法里面只要實(shí)現(xiàn)此協(xié)議的值就可以了

// 這個(gè)方法只需要一個(gè)參數(shù),相比于之前的多個(gè)參數(shù)簡(jiǎn)便了很多
class SwitchWithTextTableViewCell: UITableViewCell { 
    func configure(withDelegate delagate: SwitchWithTextCellProtocol) { 
    // 在這里配置方法 
    }
}
  • 現(xiàn)在的視圖模型
struct MinionModeViewModel: SwitchWithTextCellProtocol { 
    var title = "excellent!!" 
    var switchOn = true 
    var switchColor: UIColor {
         return .yellowColor()
     }
    func onSwitchToggleOn(on: Bool) {
         if on { 
                 print("The Minions are here to stay!")
         } else { 
                 print("The Minions went out to play!") 
        }
   }
}
  • 現(xiàn)在蕾盯,cellForRowAtIndexPath()也變得非常簡(jiǎn)明了
let cell = tableView.dequeueReuseableCellWithIdentifier("SwitchWithTextTableViewCell", forIndexPath: indexPath) as! SwitchWithTextTableViewCell
cell.configure(withDelegate: MinionModeViewModel())
return cell
  • 再把模型放在視圖模型層級(jí)幕屹,一遍對(duì)其進(jìn)行跟蹤,再視圖模型中傳遞這些信息级遭,這樣單元格就可以生成了

  • 但是在這個(gè)基礎(chǔ)上望拖,還可以再做進(jìn)一步的深化,就是建立兩個(gè)協(xié)議挫鸽,一個(gè)作為實(shí)際編碼的數(shù)據(jù)源:比如標(biāo)題內(nèi)容之類的實(shí)際數(shù)據(jù);一個(gè)作為單元格委托:存儲(chǔ)顏色说敏、字體之類的并沒(méi)有包含實(shí)際數(shù)據(jù)的信息,也就是仿照Apple中UITableView等集合視圖之類的地方丢郊,按照這種思維去建立單元格存儲(chǔ)和單元格委托

protocol SwitchWithTextCellDataSource { 
    var title: String { get } 
    var switchOn: Bool { get }
}
protocol SwitchWithTextCellDelegate { 
    func onSwitchTogglenOn(on: Bool) 
    var switchColor: UIColor { get }
    var textColor: UIColor { get } 
    var font: UIFont { get }
}
  • 接下來(lái)盔沫,再讓configure方法同時(shí)接受這兩個(gè)協(xié)議。因?yàn)槲锌梢匀吭趨f(xié)議擴(kuò)展中使用默認(rèn)值進(jìn)行配置枫匾,比如說(shuō)字體架诞、顏色之類的信息,這樣在理論上就可以不用向里面?zhèn)鬟f任何東西進(jìn)去干茉,只需要?jiǎng)?chuàng)建一個(gè)模型就可以了
// SwitchWithTextTableViewCellfunc 
configure(withDataSource dataSource: SwitchWithTextCellDataSource, delegate: SwitchWithTextCellDelegate?) {
 // 在這里配置視圖
}
  • 然后就要開(kāi)始通過(guò)擴(kuò)展來(lái)改進(jìn)視圖模型了谴忧,使用一個(gè)實(shí)際數(shù)據(jù)源的代碼塊,然后給定要傳遞的視圖當(dāng)中的原始信息
struct MinionModeViewModel: SwiftWithTextCellDataSource { 
    var title = "Minion Mode!!"
    var switchOn = true
}
  • 接下來(lái)會(huì)在一個(gè)單獨(dú)的視圖模型的部分使用處理字體、顏色之類的委托俏蛮,然后在其中進(jìn)行相關(guān)的配置
extension MinionModeViewModel: SwitchWithTextCellDelegate { 
    var switchColor: UIColor {
        return .yellowColor() 
    }
    func onSwitchToggleOn(on: Bool) { 
        if on {
               print("The Minions are here to stay!")
        } else { 
              print("The Minions went out to play!") 
        } 
    }
}
  • 最終撑毛,表格視圖單元格變得非常簡(jiǎn)單
// SettingViewControllerlet 
viewModel = MinionModeViewModel()
cell.configure(withDataSource:viewModel, delegate: viewModel)
return cell
  • 僅僅需要?jiǎng)?chuàng)建視圖模型,然后將其傳遞到配置方法當(dāng)中纯命,最后返回單元格免胃,就可以了

20.1 Swift2.0中的 MinxinTrait

  • 在游戲開(kāi)發(fā)中通常會(huì)有一個(gè)很龐大的層級(jí)關(guān)系,以及一系列的繼承辣恋,比如各種怪亮垫,繼承在這里顯得十分有意義,但是隨著層級(jí)的擴(kuò)展伟骨,這個(gè)項(xiàng)目就會(huì)變得凌亂起來(lái)

  • 比如說(shuō)需要設(shè)計(jì)一個(gè)可以射擊的怪物饮潦,但這時(shí)候塔防頂部的大炮也會(huì)射擊,就需要把“射擊輔助類”提取出來(lái)携狭,但是如果一直這樣提取子類继蜡,代碼后面會(huì)一團(tuán)亂麻

  • 將這個(gè)代碼重構(gòu),不再去提取能夠射擊或者能夠加血的子類逛腿,而是將其提取為協(xié)議稀并,通過(guò)協(xié)議擴(kuò)展來(lái)實(shí)現(xiàn)這個(gè)功能,代碼更加簡(jiǎn)潔单默,更利于理解

// 一看這個(gè)對(duì)象的類型碘举,就知道他有哪些功能,而不是一個(gè)個(gè)去查找她的實(shí)現(xiàn)
class ZapMonster: GameObject, GunTraint, HealthTraint, MovementTraint { ...}
  • 雖然說(shuō)這種設(shè)計(jì)模式是游戲方面的搁廓,但是我們平時(shí)的代碼也可以參考這種設(shè)計(jì)模式:這樣就不需要讓實(shí)際的單元格實(shí)現(xiàn)這個(gè)協(xié)議了引颈,只需要將其根更廣泛的TextPresentable聯(lián)系在一起就可以了,這樣境蜕,任何擁有標(biāo)簽的視圖蝙场,而不僅僅是單元格,都可以實(shí)現(xiàn)這個(gè)協(xié)議來(lái)彎沉相關(guān)的功能粱年。這樣就可以說(shuō)這個(gè)標(biāo)簽有什么樣的溫恩李丰,什么樣的顏色,以及什么樣的字體
protocol TextPresentable { 
    var text: String { get } 
    var textColor: UIColor { get } 
    var font: UIFont { get }
}
protocol SwitchPresentable { 
    var switchOn: Bool { get } 
    var switchColor: UIColor { get } 
    func onSwitchToggleOn(on: Bool)
}
  • 這種情況下逼泣,比如需要一個(gè)圖片框趴泌,只要一個(gè)iamgeProtocol就可以了,設(shè)計(jì)師要求改所有標(biāo)簽的顏色的話一行代碼就可以搞定

  • 現(xiàn)在單元格的模樣

class SwitchWithTextTableViewCell<T where T: TextPresentable, T: SwitchPresentable>: UITableViewCell { 
    private var delegate: T? // T是視圖模型 
    func configure(withDelegate delegate: T) { 
        // 在這里配置視圖 
   }
}
  • 在這種情況下拉庶,它沒(méi)有實(shí)現(xiàn)這些協(xié)議嗜憔,但是會(huì)期待某種實(shí)現(xiàn)這些協(xié)議的東西傳遞進(jìn)去,因此我們使用了泛型氏仗,這個(gè)單元格期待了一個(gè)實(shí)現(xiàn)了TextPresentableProtocol的委托吉捶。就我們而言夺鲜,傳遞進(jìn)去的將是一個(gè)實(shí)現(xiàn)了這些協(xié)議的東西就可以了,現(xiàn)在要基于這些信息在單元格當(dāng)中配置所有的東西了呐舔,現(xiàn)在就可以基于這些信息在單元格中配置所有的東西了
extension MinionModeViewModel: TextPresentable { 
    var text: String { 
        return "Minion Mode" 
    } 
    var textColor: UIColor {
        return .blackColor() 
    } 
    var font: UIFont { 
        return .systemFontOfsize(17.0) 
    }
}
  • 我們的視圖模型將會(huì)有一個(gè)TextPresentable代碼币励,在其中可以配置文本、顏色珊拼、字體食呻,并且由于所有的這些協(xié)議擴(kuò)展中都已經(jīng)有默認(rèn)值了,甚至不需要視圖模型去實(shí)現(xiàn)這些具體的內(nèi)容

  • 最后澎现,視圖模型當(dāng)中的代碼就只需要dequeue相應(yīng)的單元格仅胞。然后通過(guò)視圖模型對(duì)其進(jìn)行配置,然后返回單元格即可

20.2 Swift2.2隨著iOS9.3一同登場(chǎng)剑辫,講講什么新調(diào)整干旧?

  • ++--deprecate掉了,2.2版本會(huì)有一個(gè)警告妹蔽,但是3.0必然會(huì)完全移除椎眯,使用+=1-=1 替代;
  • 傳統(tǒng)C 風(fēng)格的for循環(huán)被干掉胳岂,只能用 for in编整;
  • var 參數(shù)被廢除了
  • 使用var, 讓你在函數(shù)內(nèi)部修改參數(shù)
  • 使用inout旦万,可以讓你的改變延續(xù)到函數(shù)結(jié)束后
  • 允許更多的參數(shù)作為參數(shù)標(biāo)簽:for i 1.stride(to: 9, by: 2){print(i)} 從1開(kāi)始,每次加2镶蹋,但結(jié)果要一直小于9成艘,代碼的自解釋性更好;
  • 字符串作為 selector 也被deprecated掉了贺归,可以通過(guò)#selector(Swift方法名)來(lái)實(shí)現(xiàn) selector淆两,發(fā)生拼寫(xiě)錯(cuò)誤也會(huì)得到提醒
    還有一些沒(méi)記住。拂酣。反正我的demo中需要用到的地方這幾點(diǎn)比較多

21. 優(yōu)化tableViewCell高度

  • 一種是針對(duì)所有 Cell具有固定高度的情況秋冰,通過(guò):self.tableView.rowHeight = 88;指定了一個(gè)所有 cell都是 88 高度的UITableView,對(duì)于定高需求的表格婶熬,強(qiáng)烈建議使用這種(而非下面的)方式保證不必要的高度計(jì)算和調(diào)用剑勾。

  • 另一種方式就是實(shí)現(xiàn)UITableViewDelegate 中的:heightForRowAtIndexPath:需要注意的是,實(shí)現(xiàn)了這個(gè)方法后赵颅,rowHeight 的設(shè)置將無(wú)效虽另。所以,這個(gè)方法適用于具有多種 cell 高度的 UITableView饺谬。

  • iOS7之后出了了estimatedRowHeight捂刺,面對(duì)不同高度的cell,只要給一個(gè)預(yù)估的值就可以了,先給一個(gè)預(yù)估值族展,然后邊滑動(dòng)邊計(jì)算森缠,但是缺點(diǎn)就是

  • 設(shè)置估算高度以后,tableViewcontentSize.height是根據(jù)cell高度預(yù)估值和cell的個(gè)數(shù)來(lái)計(jì)算的仪缸,導(dǎo)致導(dǎo)航條處于很不穩(wěn)定的狀態(tài)贵涵,因?yàn)閏ontentSize.height會(huì)逐漸由預(yù)估高度變?yōu)閷?shí)際高度,很多情況下肉眼是可以看到導(dǎo)航條跳躍的
  • 如果是設(shè)計(jì)不好的上拉加載或下拉刷新腹殿,有可能使表格滑動(dòng)跳躍
  • 估算高度設(shè)計(jì)初衷是好的独悴,讓加載速度更快,但是損失了流暢性锣尉,與其損失流暢性刻炒,我寧愿讓用戶加載界面的時(shí)候多等那零點(diǎn)幾秒
  • iOS8 WWDC 中推出了 self-sizing cell 的概念,旨在讓 cell 自己負(fù)責(zé)自己的高度計(jì)算自沧,使用 frame layoutauto layout都可以享受到:
  • self.tableView.estimatedRowHeight = 213;self.tableView.rowHeight = UITableViewAutomaticDimension;如果不加上估算高度的設(shè)置坟奥,自動(dòng)算高就失效了
  • 這個(gè)自動(dòng)算高在 push 到下一個(gè)頁(yè)面或者轉(zhuǎn)屏?xí)r會(huì)出現(xiàn)高度特別詭異的情況,不過(guò)現(xiàn)在的版本修復(fù)了拇厢。
  • 相同的代碼在 iOS7 和 iOS8 上滑動(dòng)順暢程度完全不同爱谁,iOS8 莫名奇妙的卡。很大一部分原因是 iOS8 上的算高機(jī)制大不相同,從 WWDC 也倒是能找到點(diǎn)解釋孝偎,cell 被認(rèn)為隨時(shí)都可能改變高度(如從設(shè)置中調(diào)整動(dòng)態(tài)字體大蟹玫小),所以每次滑動(dòng)出來(lái)后都要重新計(jì)算高度衣盾。
  • dequeueReusableCellWithIdentifier:forIndexPath:相比不帶“forIndexPath” 的版本會(huì)多調(diào)用一次高度計(jì)算
  • iOS7 計(jì)算高度后有”緩存“機(jī)制寺旺,不會(huì)重復(fù)計(jì)算;而 iOS8 不論何時(shí)都會(huì)重新計(jì)算cell高度
  • 使用UITableView+FDTemplateLayoutCell(百度知道負(fù)責(zé)人孫源) 無(wú)疑是解決算高問(wèn)題的最佳實(shí)踐之一势决,既有 iOS8 self-sizing 功能簡(jiǎn)單的 API阻塑,又可以達(dá)到 iOS7 流暢的滑動(dòng)效果,還保持了最低支持 iOS6

  • FDTemplateLayoutCell 的高度預(yù)緩存是一個(gè)優(yōu)化功能果复,利用RunLoop空閑時(shí)間執(zhí)行預(yù)緩存任務(wù)計(jì)算陈莽,當(dāng)用戶正在滑動(dòng)列表時(shí)顯然不應(yīng)該執(zhí)行計(jì)算任務(wù)影響滑動(dòng)體驗(yàn)。

  • 當(dāng)用戶正在滑動(dòng) UIScrollView 時(shí)虽抄,RunLoop將切換到UITrackingRunLoopMode 接受滑動(dòng)手勢(shì)和處理滑動(dòng)事件(包括減速和彈簧效果)走搁,此時(shí),其他 Mode (除 NSRunLoopCommonModes 這個(gè)組合 Mode)下的事件將全部暫停執(zhí)行迈窟,來(lái)保證滑動(dòng)事件的優(yōu)先處理朱盐,這也是 iOS 滑動(dòng)順暢的重要原因
  • 注冊(cè) RunLoopObserver 可以觀測(cè)當(dāng)前 RunLoop的運(yùn)行狀態(tài),并在狀態(tài)機(jī)切換時(shí)收到通知:RunLoop開(kāi)始
    • RunLoop即將處理Timer
    • RunLoop即將處理Source
    • RunLoop即將進(jìn)入休眠狀態(tài)
    • RunLoop即將從休眠狀態(tài)被事件喚醒
    • RunLoop退出
  • 分解成多個(gè)RunLoop Source任務(wù)菠隆,假設(shè)列表有 20 個(gè) cell兵琳,加載后展示了前 5 個(gè)狂秘,那么開(kāi)啟估算后 table view只計(jì)算了這 5 個(gè)的高度,此時(shí)剩下 15 個(gè)就是“預(yù)緩存”的任務(wù)躯肌,而我們并不希望這 15 個(gè)計(jì)算任務(wù)在同一個(gè) RunLoop 迭代中同步執(zhí)行者春,這樣會(huì)卡頓 UI,所以應(yīng)該把它們分別分解到 15 個(gè) RunLoop 迭代中執(zhí)行清女,這時(shí)就需要手動(dòng)向RunLoop中添加Source 任務(wù)(由應(yīng)用發(fā)起和處理的是Source 0 任務(wù))

22. 列舉一下你常用的第三方庫(kù)

  • Xcode插件
  • Alcatraz:Xcode 插件管理工具
  • ColorSense-for-Xcode:代碼生成顏色預(yù)覽钱烟,可視化編輯
  • KSImageNamed-Xcode:引入圖片自動(dòng)提示,預(yù)覽
  • VVDocumenter-Xcode:規(guī)范化注釋
  • 項(xiàng)目中常用的第三方庫(kù)
  • AFNetworking:網(wǎng)絡(luò)庫(kù)嫡丙,通常會(huì)在AFN上面再封裝一層拴袭,主要封裝接口邏輯
  • SDWebImage:下載網(wǎng)絡(luò)圖片,定時(shí)清除緩存
  • Reachability:網(wǎng)絡(luò)狀態(tài)判斷曙博,AFN已經(jīng)有這個(gè)功能
  • WebViewJavaScriptBridge: Webview和cocoa之間消息傳遞
  • fmdb:SQLite的封裝拥刻,簡(jiǎn)單易用
  • DTCoreText:CoreText庫(kù),支持HTML
  • KissXML:XML解析父泳,支持讀取和修改般哼,基于libxml
  • ZXingObjC:二維碼,支持編碼解碼
  • GTMBase64:base64編解碼
  • GPIImage:圖像處理
  • JSONKit:json解析惠窄,性能最好
  • Mansonry 蒸眠、Snapkit(Swift)輔助自動(dòng)布局
  • MJRefresh:上拉加載,下拉刷新
  • MBProgressHUD杆融、 SVProgressHUD進(jìn)度圖楞卡,加載效果提示

23. 為什么AFN顯示圖片不如SDWebImage流暢?同樣是從網(wǎng)絡(luò)上下載圖片而不是從緩存取圖片脾歇?

  • 因?yàn)镾DWebImage有一個(gè)decoder
  • UIImage的imageWithData函數(shù)是每次畫(huà)圖的時(shí)候才將Data解壓成ARGB的圖像
  • 所以每次畫(huà)圖的時(shí)候蒋腮,會(huì)有一個(gè)解壓操作,這樣效率很低介劫,但是只有瞬時(shí)的內(nèi)存需求
  • 為了提高效率通過(guò)SDWebImageDecoder將包裝在Data的資源解壓徽惋,然后畫(huà)在另外一張圖片上案淋,這樣新的圖片就不再需要重復(fù)解壓了
  • 這是典型的空間換時(shí)間的做法

24. 關(guān)于新特性

iOS7新特性
  • 在iOS7當(dāng)中座韵,使用麥克風(fēng)也需要取得用戶同意了。如果用戶不允許app使用麥克風(fēng)的話踢京,那么需要使用麥克風(fēng)的app就不能接收不到任何聲音
  • [NSArray firstObject]的實(shí)現(xiàn)誉碴,iOS4之前只是一個(gè)私有的方法
  • UIImage.renderingMode著色(Tint Color),可以設(shè)置一個(gè)UIImage在渲染時(shí)是否使用當(dāng)前視圖的Tint Color瓣距。
  • UIScreenEdgePanGestureRecognizer可以從屏幕邊界即可檢測(cè)手勢(shì)
  • 使用Core Image來(lái)檢測(cè)眨眼以及微笑iOS給Core Image增加了兩種人臉檢測(cè)功能:CIDetectorEyeBlink以及CIDetectorSmile黔帕。這也就是說(shuō)你現(xiàn)在可以在照片中檢測(cè)微笑以及眨眼。
iOS8新特性
  • 當(dāng)使用iOS8定位的時(shí)候需要請(qǐng)求用戶授權(quán)蹈丸,且在info.plist里添加字段NSLocationAlwaysUsageDescription請(qǐng)求用戶授權(quán)的描述

  • size classes是為了解決storyboard只能訂制一種屏幕樣式的問(wèn)題成黄,它不再是具體的尺寸呐芥,而是抽象尺寸通過(guò)寬/高 的compact、any奋岁、regular 組成了九種組合包含了所有蘋(píng)果設(shè)備的尺寸思瘟。

  • iOS8中,字體是Helvetica闻伶,中文的字體有點(diǎn)類似于“華文細(xì)黑”滨攻。只是蘋(píng)果手機(jī)自帶渲染,所以看上去可能比普通的華文細(xì)黑要美觀蓝翰。iOS9中光绕,中文系統(tǒng)字體變?yōu)榱藢橹袊?guó)設(shè)計(jì)的“蘋(píng)方” 有點(diǎn)類似于一種word字體“幼圓”。字體有輕微的加粗效果畜份,并且最關(guān)鍵的是字體間隙變大了诞帐!

####### iOS9新特性

  • iOS9系統(tǒng)發(fā)送的網(wǎng)絡(luò)請(qǐng)求將統(tǒng)一使用HTTPs,將不再默認(rèn)使用HTTP等不安全的網(wǎng)絡(luò)協(xié)議漂坏,而默認(rèn)采用TLS 1.2景埃。服務(wù)器因此需要更新,以解析相關(guān)數(shù)據(jù)顶别。如不更新谷徙,可通過(guò)在info.plist中聲明,倒退回不安全的網(wǎng)絡(luò)請(qǐng)求驯绎。

  • 將允許出現(xiàn)這種場(chǎng)景:同一app中多個(gè)location manager:一些只能在前臺(tái)定位完慧,另一些可在后臺(tái)定位

  • bitcode的理解應(yīng)該是把程序編譯成的一種過(guò)渡代碼,然后蘋(píng)果再把這個(gè)過(guò)渡代碼編譯成可執(zhí)行的程序剩失。bitcode也允許蘋(píng)果在后期重新優(yōu)化我們程序的二進(jìn)制文件屈尼,有類似于App瘦身的思想。

  • stackView

  • Multasking:多任務(wù)特性拴孤,三種形式

  • 臨時(shí)調(diào)出的滑動(dòng)覆蓋:Slide Over
  • 視頻播放的畫(huà)中畫(huà)模式(Picture in Picture) (AVPlayerViewController默認(rèn)支持脾歧。MPMoviePlayerViewControllerdeprecated掉了,不支持)
  • iPad真正同時(shí)使用兩個(gè)App
  • UI Test:iOS9.0之前加入異步代碼測(cè)設(shè)和性能測(cè)試演熟,可以說(shuō)Xcode自帶的測(cè)試框架已經(jīng)能滿足絕大部分單元測(cè)試的需求了鞭执,但是這并不夠,因?yàn)殚_(kāi)發(fā)一個(gè)iOS app從來(lái)都是很注重UI和用戶體驗(yàn)的芒粹,之前UI測(cè)試使用KIF兄纺,Automating,iOS9.0的Xcode給出了自帶的XCUITest的一系列工具化漆,和大多數(shù)UI測(cè)試工具類似估脆,XCUI使用Accessbility標(biāo)記來(lái)確定view,但因?yàn)槭茿pple自家的東西座云,可以自動(dòng)記錄操作流程疙赠,所以只要書(shū)寫(xiě)最后的驗(yàn)證部分就好了付材,比其他UI測(cè)試工具方便多了

  • Swift2

  • APP Thinning:app為了后向兼容,都同時(shí)包含了32bit和64bit圃阳,在圖片資源2X和3X的一應(yīng)俱全伞租,下載的時(shí)候只需要當(dāng)前機(jī)型對(duì)應(yīng)的一套資源,但是卻要全部打包下載限佩,現(xiàn)在只需要升級(jí)iOS9葵诈,就可以省很多流量

  • 3D touch

  • 地圖顯示實(shí)時(shí)的交通狀況

  • 人工智能siri更加智能,幾個(gè)大城市的地鐵及火車(chē)站入口都有詳細(xì)的標(biāo)識(shí)

  • 手機(jī)電池的低功耗設(shè)置

  • Spootlight祟同,你的設(shè)備會(huì)向推薦最近通話過(guò)的聯(lián)系人作喘,使用過(guò)的APP以及你可能感興趣的去處、信息呈現(xiàn)更精彩

25. 我是怎樣用兩個(gè)imageView實(shí)現(xiàn)了無(wú)線輪播晕城!

  1. 建立一個(gè)scrollView泞坦,設(shè)置contentsize3*kWidthcontentOffSetkWidth
  • 接下來(lái)使用代理方法scrollViewDidScroll來(lái)監(jiān)聽(tīng)scrollview的滾動(dòng)砖顷,定義一個(gè)枚舉變量來(lái)記錄滾動(dòng)的方向
  • 使用KVO來(lái)監(jiān)聽(tīng)direction屬性值的改變-->[self addObserver:self forKeyPath:@"direction" options:NSKeyValueObservingOptionNew context:nil];
  • 通過(guò)observeValueForKeyPath判斷滾動(dòng)的方向贰锁,當(dāng)偏移量大于x,表示左移滤蝠,則將otherImageView加在右邊豌熄,偏移量小于x,表示右移物咳,則將otherImageView加在左邊锣险。同時(shí)判斷設(shè)置對(duì)應(yīng)的索引,圖片
  • 通過(guò)代理方法scrollViewDidEndDecelerating來(lái)監(jiān)聽(tīng)滾動(dòng)結(jié)束览闰,結(jié)束后芯肤,scrollview的偏移量為0或者2x,我們通過(guò)代碼再次將scrollview的偏移量設(shè)置為x压鉴,并將currImageView的圖片修改為otherImageView的圖片崖咨,那么我們看到的還是currImageView,只不過(guò)展示的是下一張圖片油吭,如圖击蹲,又變成了最初的效果
  • 然后設(shè)置自動(dòng)輪播,添加計(jì)時(shí)器上鞠,利用setContentOffset方法里面setContentOffset:animated:方法執(zhí)行完畢后不會(huì)調(diào)用scrollview的scrollViewDidEndDecelerating方法际邻,但是會(huì)調(diào)用scrollViewDidEndScrollingAnimation方法芯丧,因此我們要在該方法中調(diào)用pauseScroll(即監(jiān)聽(tīng)減速結(jié)束后由otherImageView切換到currImageView的方法)
  • 添加計(jì)時(shí)器:self.timer = [NSTimer timerWithTimeInterval:self.time target:self selector:@selector(nextPage) userInfo:nil repeats:YES];
  • scrollViewWillBeginDragging中停止計(jì)時(shí)器
  • scrollViewDidEndDragging中開(kāi)啟計(jì)時(shí)器
  • 判斷外界傳入的是圖片還是路徑芍阎,如果是圖片,直接加入圖片數(shù)組中缨恒,如果是路徑谴咸,先添加一個(gè)占位圖片轮听,然后根據(jù)路徑去下載圖片
  • 監(jiān)聽(tīng)圖片被點(diǎn)擊
  • 定義一個(gè)block屬性暴露給外界void(^imageClickBlock)(NSInteger index)
  • 設(shè)置currImageViewuserInteractionEnabled為YES
  • currImageView添加一個(gè)點(diǎn)擊的手勢(shì)
    -在手勢(shì)方法里調(diào)用block,并傳入圖片索引
  • NSTimer的兩種形式
  • scheduledTimerWithTimeInterval是創(chuàng)建一個(gè)定時(shí)器岭佳,并加入到當(dāng)前運(yùn)行循環(huán)[NSRunLoop currentRunLoop]
  • 其他兩個(gè)([NSTimer timerWithTimeInterval:3 target:self selector:@selector(doSomeThing1) userInfo:nil repeats:YES];血巍、[[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:5] interval:3 target:self selector:@selector(doSomeThing2) userInfo:nil repeats:YES];)只是創(chuàng)建定時(shí)器,并未添加到當(dāng)前運(yùn)行循環(huán)中珊随,所以如果是其他兩種方式創(chuàng)建的定時(shí)器則需要手動(dòng)添加到currentRunLoop
  • NSTimer是普通的定時(shí)器述寡,如果系統(tǒng)繁忙,刷新可能會(huì)被延遲叶洞。但是CADisplaylink實(shí)時(shí)刷新鲫凶,跟著屏幕的刷新頻率實(shí)時(shí)刷新,60次/s衩辟,與屏幕刷新頻率相同

26. tableView的優(yōu)化

iOS平臺(tái)因?yàn)閁IKit本身的特性螟炫,需要將所有的UI操作都放在主線程執(zhí)行,所以有時(shí)候就習(xí)慣將一些線程安全性不確定的邏輯艺晴,以及它線程結(jié)束后的匯總工作等等放到了主線程昼钻,所以主線程包含大量計(jì)算、IO封寞、繪制都有可能造成卡頓然评。

  • 可以通過(guò)監(jiān)控runLoop監(jiān)控監(jiān)控卡頓,調(diào)用方法主要就是在kCFRunLoopBeforeSourceskCFRunLoopBeforeWaiting之間,還有kCFRunLoopAfterWaiting之后,也就是如果我們發(fā)現(xiàn)這兩個(gè)時(shí)間內(nèi)耗時(shí)太長(zhǎng),那么就可以判定出此時(shí)主線程卡頓.

  • 使用到CFRunLoopObserverRef,通過(guò)它可以實(shí)時(shí)獲得這些狀態(tài)值的變化

  • 監(jiān)控后另外再開(kāi)啟一個(gè)線程,實(shí)時(shí)計(jì)算這兩個(gè)狀態(tài)區(qū)域之間的耗時(shí)是否到達(dá)某個(gè)閥值,便能揪出這些性能殺手.

  • 監(jiān)控到了卡頓現(xiàn)場(chǎng),當(dāng)然下一步便是記錄此時(shí)的函數(shù)調(diào)用信息,此處可以使用一個(gè)第三方Crash收集組件PLCrashReporter,它不僅可以收集Crash信息也可用于實(shí)時(shí)獲取各線程的調(diào)用堆棧

  • 當(dāng)檢測(cè)到卡頓時(shí),抓取堆棧信息,然后在客戶端做一些過(guò)濾處理,便可以上報(bào)到服務(wù)器,通過(guò)收集一定量的卡頓數(shù)據(jù)后經(jīng)過(guò)分析便能準(zhǔn)確定位需要優(yōu)化的邏輯

  • 設(shè)置正確的 reuseidentifer 以重用cell

  • 盡量將View 設(shè)置為不透明,包括 cell 本身(backgroundcolor默認(rèn)是透明的)狈究,圖層混合靠GPU去渲染,如果透明度設(shè)置為100%沾瓦,那么GPU就會(huì)忽略下面所有的layer,節(jié)約了很多不必要的運(yùn)算谦炒。模擬器上點(diǎn)擊“Debug”菜單贯莺,然后選擇“color Blended Layers”,會(huì)把所有區(qū)域分成綠色和紅色,綠色的好,紅色的性能差(經(jīng)過(guò)混合渲染的)宁改,當(dāng)然也有一些圖片雖然是不透明的缕探,但是也會(huì)顯示紅色,如果檢查代碼沒(méi)錯(cuò)的話还蹲,一般就是圖片自身的性質(zhì)問(wèn)題了爹耗,直接聯(lián)系美工或后臺(tái)解決就好了。除非必須要用GPU加載的谜喊,其他最好要用CPU加載潭兽,因?yàn)镃PU一般不會(huì)百分百加載,可以通過(guò)CoreGraphics畫(huà)出圓角

  • 有時(shí)候美工失誤斗遏,圖片大小給錯(cuò)了山卦,引起不必要的圖片縮放(可以找美工去改,當(dāng)然也可以異步去裁剪圖片然后緩存下來(lái))诵次,還是使用Instrument的Color Misaligned Images账蓉,黃色表示圖片需要縮放枚碗,紫色表示沒(méi)有像素對(duì)齊。當(dāng)然一般情況下圖片格式不會(huì)給錯(cuò)铸本,有些圖片格式是GPU不支持的肮雨,就還要?jiǎng)跓〤PU去進(jìn)行格式轉(zhuǎn)換。還有可以通過(guò)Color Offscreen-Rendered Yellow來(lái)檢測(cè)離屏渲染(就是把渲染結(jié)果臨時(shí)保存箱玷,等到用的時(shí)候再取出怨规,這樣相對(duì)于普通渲染更消耗內(nèi)存,使用maskToBounds锡足、設(shè)置shadow椅亚,重寫(xiě)drawRect方法都會(huì)導(dǎo)致離屏渲染)避免漸變,cornerRadius在默認(rèn)情況下舱污,這個(gè)屬性只會(huì)影響視圖的背景顏色和border呀舔,但是不會(huì)離屏繪制,不影響性能扩灯。不用clipsToBounds(過(guò)多調(diào)用GPU去離屏渲染)媚赖,而是讓后臺(tái)加載圖片并處理圓角,并將處理過(guò)的圖片賦值給UIImageView珠插。UIImageView的圓角通過(guò)直接截取圖片實(shí)現(xiàn)惧磺,圓角路徑直接用貝塞爾曲線UIBezierPath繪制(人為指定路徑之后就不會(huì)觸發(fā)離屏渲染),UIGraphicsBeginImageContextWithOptions捻撑。UIView的圓角可以使用CoreGraphics畫(huà)出圓角矩形磨隘,核心是CGContextAddArcToPoint 函數(shù)。它中間的四個(gè)參數(shù)表示曲線的起點(diǎn)和終點(diǎn)坐標(biāo)顾患,最后一個(gè)參數(shù)表示半徑番捂。調(diào)用了四次函數(shù)后,就可以畫(huà)出圓角矩形江解。最后再?gòu)漠?dāng)前的繪圖上下文中獲取圖片并返回设预,最后把這個(gè)圖片插入到視圖層級(jí)的底部。“Flash updated Regions”用于標(biāo)記發(fā)生重繪的區(qū)域

  • 如果 row 的高度不相同,那么將其緩存下來(lái)

  • 如果cell 顯示的內(nèi)容來(lái)自網(wǎng)絡(luò),那么確保這些內(nèi)容是通過(guò)異步下載

  • 使用 shadowPath 來(lái)設(shè)置陰影犁河,圖層最好不要使用陰影,陰影會(huì)導(dǎo)致離屏渲染(在進(jìn)入屏幕渲染之前,還看不到的時(shí)候會(huì)再渲染一次,盡量不要產(chǎn)生離屏渲染)

  • 減少 subview 的數(shù)量,不要去添加或移除view桨螺,要就顯示宾符,不要就隱藏

  • cellForRowAtIndexPath 中盡量做更少的操作,最好是在別的地方算好,這個(gè)方法里只做數(shù)據(jù)的顯示灭翔,如果需要做一些處理,那么最好做一次之后將結(jié)果儲(chǔ)存起來(lái).
    使用適當(dāng)?shù)臄?shù)據(jù)結(jié)構(gòu)來(lái)保存需要的信息,不同的結(jié)構(gòu)會(huì)帶來(lái)不同的操作代價(jià)

  • 使用,rowHeight , sectionFooterHeightsectionHeaderHeight 來(lái)設(shè)置一個(gè)恒定高度 , 而不是從代理(delegate)中獲取

  • cell做數(shù)據(jù)綁定的時(shí)候魏烫,最好在willDisPlayCell里面進(jìn)行,其他操作在cellForRowAtIndexPath,因?yàn)榍罢呤堑谝豁?yè)有多少條就執(zhí)行多少次则奥,后者是第一次加載有多少個(gè)cell就執(zhí)行多少次,而且調(diào)用后者的時(shí)候cell還沒(méi)顯示

  • 讀取文件,寫(xiě)入文件,最好是放到子線程,或先讀取好,在讓tableView去顯示

  • tableView滾動(dòng)的時(shí)候,不要去做動(dòng)畫(huà)(微信的聊天界面做的就很好,在滾動(dòng)的時(shí)候,動(dòng)態(tài)圖就不讓他動(dòng),滾動(dòng)停止的時(shí)候才動(dòng),不然可能會(huì)有點(diǎn)影響流暢度)狭园。在滾動(dòng)的時(shí)候加載圖片读处,停止拖拽后在減速過(guò)程中不加載圖片,減速停止后加載可見(jiàn)范圍內(nèi)圖片

27. 談?wù)剝?nèi)存的優(yōu)化和注意事項(xiàng)(使用Instrument工具的CoreAnimation唱矛、GPU Driver罚舱、I/O操作,檢查fps數(shù)值)

  • 重用問(wèn)題:比如UITableViewCell绎谦、UICollectionViewCell管闷、UITableViewHeaderFooterViews等設(shè)置正確的reuseIdentifier,充分重用

  • 懶加載控件窃肠、頁(yè)面:對(duì)于不是立刻使用的數(shù)據(jù)包个,都應(yīng)該使用延遲加載的方式,比如網(wǎng)絡(luò)連接失敗的提示界面冤留,可能一直都用不到

  • 使用Autorelease Pool:在某些循環(huán)創(chuàng)建臨時(shí)變量處理數(shù)據(jù)時(shí)碧囊,自動(dòng)釋放池以保證能及時(shí)釋放內(nèi)存

  • 不要使用太多的xib/storyboard:載入時(shí)會(huì)將其內(nèi)部的圖片在內(nèi)的所有資源載入內(nèi)存,即使未來(lái)很久才會(huì)需要使用纤怒,相對(duì)于純代碼寫(xiě)的延遲加載糯而,在性能和內(nèi)存上就差了很多

  • 數(shù)據(jù)緩存:對(duì)于cell的行高要緩存起來(lái),使用reloadData效率也極高泊窘,對(duì)于網(wǎng)絡(luò)數(shù)據(jù)熄驼,不需要每次都請(qǐng)求的,應(yīng)該緩存起來(lái)烘豹,可以寫(xiě)入數(shù)據(jù)庫(kù)瓜贾,也可以通過(guò)plist文件存儲(chǔ)

  • 選擇正確的數(shù)據(jù)結(jié)構(gòu):針對(duì)不同的業(yè)務(wù)場(chǎng)景選擇最合適的數(shù)據(jù)結(jié)構(gòu)是寫(xiě)出高效代碼的基礎(chǔ)

  • 數(shù)組:有序的一組值,使用索引查詢起來(lái)很快携悯,使用值查詢的很慢阐虚,插入/刪除 很慢
  • 字典:存儲(chǔ)鍵值對(duì)對(duì),用鍵查找比較快
  • 集合:無(wú)序的一組值蚌卤,用值來(lái)查找很快实束,插入/刪除很快
  • gzip/zip壓縮:當(dāng)從服務(wù)器下載相關(guān)附件時(shí),可以通過(guò) zip壓縮后再下載逊彭,使得內(nèi)存更小咸灿,下載速度也更快

  • 重大開(kāi)銷(xiāo)對(duì)象:一些objects的初始化很慢,比如NSDateFormatterNSCalendar侮叮,但是又無(wú)可避免的需要使用避矢,通常作為屬性存儲(chǔ)起來(lái),避免反復(fù)使用

  • 避免反復(fù)處理數(shù)據(jù):需要應(yīng)用需要從服務(wù)器加載數(shù)據(jù),常為JSON或者XML格式的數(shù)據(jù)审胸,在服務(wù)器端或者客戶端使用相同的數(shù)據(jù)結(jié)構(gòu)很重要

  • 選擇圖片時(shí)亥宿,要對(duì)圖片進(jìn)行壓縮處理,根據(jù)不同的情況選擇不同的圖片加載方式砂沛,-imageNamed:讀取到內(nèi)存后會(huì)緩存下來(lái)烫扼,適合圖片資源較小,使用很頻繁的圖片碍庵;-initWithContentsOfFiles:僅加載圖片而不緩存映企,適合較大的圖片。若是collectionView中使用大量圖片的時(shí)候静浴,可以用UIVIew.layer.contents=(__bridge id _Nullable)(model.clipedImage.CGImage)堰氓;這樣就更輕量級(jí)一些

  • 當(dāng)然有時(shí)候也會(huì)用到一些第三方,比如在使用UICollectionViewUITableView的時(shí)候苹享,Facebook有一個(gè)框架叫AsyncDisplayKit双絮,這個(gè)庫(kù)就可以很好地提升滾動(dòng)時(shí)流暢性以及圖片異步下載功能(不支持sb和autoLayout,需要手動(dòng)進(jìn)行約束設(shè)置)得问,AsyncDisplayKit用相關(guān)node類掷邦,替換了UIView和它的子類,而且是線程安全的。它可以異步解碼圖片椭赋,調(diào)整圖片大小以及對(duì)圖片和文本進(jìn)行渲染抚岗,把這些操作都放到子線程,滑動(dòng)的時(shí)候就流暢許多哪怔。我認(rèn)為這個(gè)庫(kù)最方便的就是實(shí)現(xiàn)圖片異步解碼宣蔚。UIImage顯示之前必須要先解碼完成,而且解碼還是同步的认境。尤其是在UICollectionView/UITableView中使用 prototype cell顯示大圖胚委,UIImage的同步解碼在滾動(dòng)的時(shí)候會(huì)有明顯的卡頓。另外一個(gè)很吸引人的點(diǎn)是AsyncDisplayKit可以把view層次結(jié)構(gòu)轉(zhuǎn)成layer叉信。因?yàn)閺?fù)雜的view層次結(jié)構(gòu)開(kāi)銷(xiāo)很大亩冬,如果不需要view特有的功能(例如點(diǎn)擊事件),就可以使用AsyncDisplayKitlayer backing特性從而獲得一些額外的提升硼身。當(dāng)然這個(gè)庫(kù)還處于開(kāi)發(fā)階段硅急,還有一些地方地方有待完善,比如不支持緩存佳遂,我要使用這個(gè)庫(kù)的時(shí)候一般是結(jié)合AlamofireAlamofireImage實(shí)現(xiàn)圖片的緩存

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末营袜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子丑罪,更是在濱河造成了極大的恐慌荚板,老刑警劉巖凤壁,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異跪另,居然都是意外死亡拧抖,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)免绿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)唧席,“玉大人,你說(shuō)我怎么就攤上這事针姿「み海” “怎么了厌衙?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵距淫,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我婶希,道長(zhǎng)榕暇,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任喻杈,我火速辦了婚禮彤枢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好氧映,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布厂僧。 她就那樣靜靜地躺著,像睡著了一般淤齐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天碘裕,我揣著相機(jī)與錄音,去河邊找鬼攒钳。 笑死帮孔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的不撑。 我是一名探鬼主播文兢,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼焕檬!你這毒婦竟也來(lái)了禽作?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤揩页,失蹤者是張志新(化名)和其女友劉穎旷偿,沒(méi)想到半個(gè)月后烹俗,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡萍程,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年幢妄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茫负。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蕉鸳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出忍法,到底是詐尸還是另有隱情潮尝,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布饿序,位于F島的核電站勉失,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏原探。R本人自食惡果不足惜乱凿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望咽弦。 院中可真熱鬧徒蟆,春花似錦、人聲如沸型型。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)闹蒜。三九已至寺枉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嫂用,已是汗流浹背型凳。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嘱函,地道東北人甘畅。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像往弓,于是被迫代替她去往敵國(guó)和親疏唾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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