UIButton 與 UITableView 的層級結構
- 繼承結構,屬于內部的子控件結構
- UIButton為:UIButton > UIControl > UIView > UIResponder > NSObject
- UITableView為:UITableView > UIScrollView > UIView > UIResponder > NSObject
設置 ScrollView 的 contentSize 能在 ViewDidLoad 里設置嗎?為什么?
- 一般情況是可以在 ViewDidLoad 里設置,但是在 autolayout 下,系統(tǒng)會在 ViewDidAppear之前根據 subview 的 constraint 重新計算scrollview 的 contentsize。這就是為什么,在 Viewdidload 里面手動設置了 contentSize 沒用。因為在后面,會再重新計算一次,前面手動設置的值會被覆蓋掉。
- 解決辦法就是:
- 去除 autolayout 選項,自己手動設置 contentsize
- 如果要使用 autolayout,那么自己設置完 subview 的 constraint,然后讓系統(tǒng)自動根據 constraint 計算出 contentsize。要么就在 Viewdidappear 里自己手動設置 contentsize
簡述一下你對 UIView、UIWindow 和 CALayer 的理解
- UIView:屬于 UIKit.framework 框架,負責渲染矩形區(qū)域內容,為矩形區(qū)添加動畫,響應區(qū)域的觸摸事件,布局和管理一個或多個子視圖。
- UIWindow:屬于 UIKit.framework 框架,是一個特殊的 UIView,通常在一個程序只會有一個 UIWindow,但可以手動創(chuàng)建多個 UIWindow,同時加到程序里去。UIWindow 在程序中主要起3個作用:
- 作為容器,包含 app 所要顯示的所有視圖
- 傳遞觸摸消息到程序的 View 和其他對象
- 與 UIViewController 協(xié)同工作,方便完成設備方向旋轉的支持
- CALayer:屬于 QuartzCore.framework,是用來繪制內容的,對內容進行動畫處理依賴與 UIView 來進行顯示,不能處理用戶事件。
- UIView 和 CALayer 是相互依賴的,UIView 依賴 CALayer 提供內容,CALayer 依賴 UIView 的容器顯示繪制內容。
- UIViewController:每個視圖控制器都有一個自帶的視圖,并且負責這個視圖相關的一切事務。方便管理視圖中的子視圖,負責 Model 與 View 之間的通信,檢測設備旋轉以及內存警告;是所有視圖控制類的積累,定義了控制器的基本功能
** frame 和 bounds 有什么不同?**
- frame 指的是:該 View 在父View 坐標系統(tǒng)中的位置和大小(參照是父類的坐標系統(tǒng))
- bounds 指的是:該 View 在本身坐標系統(tǒng)中的位置和大?。▍⒄拯c事本身坐標系統(tǒng))
關于視圖的聲明周期的問題
- 首先判斷控制器是否有視圖,如果沒有通過 loadview 方法創(chuàng)建:通過 storyboard 或者代碼
- 隨后調用 Viewdidload,可以進行下一步的初始化操作,只會被調用一次
- 在視圖顯示之前調用 Viewwillappear,該函數(shù)可多次調用
- 在布局變化后,調用 Viewwill、didLayoutSubviews 處理相關信息
響應者鏈條?
- 事件響應鏈。包括點擊事件,畫面刷新等。在視圖棧內從上至下,或者從下至上傳播,可以說點事件的分發(fā),傳遞以及處理。
- 響應者鏈
- UIResponder 類是 UIKit 中一個用于處理事件響應的基類。窗口上的所有事件觸發(fā),都由該類響應(即事件處理入口)。所以,窗口上的 View 及其控制器都是派生于該類的,例如 UIView、UIViewController
- 調用 UIResponder 類提供的方法或者屬性,我們就可以捕捉到窗口上的所有響應事件,并進行處理。
- 響應者鏈條是由多個響應者對象連起來的鏈條,其中響應者對象是能處理事件的對象,所有的 View 和 ViewController 都是響應者對象,利用響應者鏈條能讓多個控件處理同一個觸摸事件。
- 事件傳遞機制
- 如果當前 View 不能處理當前事件,那么事件將會沿著響應鏈進行傳遞,直到遇到能處理該事件的響應者。
UITableView 的重用機制?
- 查看 UITableView 頭文件,會找到 NSMutableArray visiableCells,和 NSMutableArray reusableTableCell 兩個結構
- visiableCells內保存當前顯示的 cells,reusableTableCell保存可重用的 cells
- tableView 顯示之初,reusableTableCell為空,那么 [tableView dequeueReusabelCellWithIdentifier:cellIdentifier]返回 nil;
- 開始的 cell 都是通過 init 來創(chuàng)建,而且 cellForRowAtIndexPath 只是調用最大顯示 cell 數(shù)的次數(shù)。比如:有100條數(shù)據,iPhone 一屏最多顯示10個 Cell
- 程序最開始顯示 tableview 的情況是:
- 用init 創(chuàng)建10個 cell,并給 cell 指定同樣的重用標識(當然,可以為不同顯示類型的 cell指定不同標識)。并且10個 cell 全部都加入到 visiableCells 數(shù)組,reusableTableCells 為空。
- 向下拖動 tableview,當 cell 完全移除屏幕,并且 cell11(他也是 alloc 出來的,原因用上)完全顯示出來的時候。cell11加入到 visiableCells,cell11移除 visiableCells,cell11 加入到 reusableTableCells。
- 接著向下拖動 tableview,因為 reusableTableCells 中已經有值,所以當需要顯示新的 cell,cellForRowAtIndexPath 再次調用的時候,[tableView dequeueReusabelCellWithIdentifier:cellIdentifier]返回 cell。cell1加入到 visiableCells,cell1移除 reusableTableCells;cell2移除 visiableCells,cell2加入到 reusableTableCells。之后再需要顯示的 cell 就可以正常重用了。
- 注意:配置 cell 的時候一定要注意,對取出的重用 cell 做重新賦值,不要遺留老數(shù)據。
UITableView 的性能優(yōu)化?滑動的時候有種卡的感覺是為什么?怎么解決?
在使用第三方應用時,卻經常遇到性能上的問題,普遍表現(xiàn)在滑動時比較卡,特別是 cell 中包含圖片的情況時。
-
實際上針對性的優(yōu)化就可以解決 tableview 滑動的時候卡頓的問題:
- 使用不透明的視圖。不透明視圖可以提高渲染的速度??梢詫?cell 及其子視圖的 opaque 屬性設置為 YES(默認值)。
- 不要重復創(chuàng)建不必要的 cell。UITableView 只需要一屏幕的 UITableViewCell 的對象即可。因此在 cell 不見的時,可以將其緩存起來,而在需要時繼續(xù)使用它即可。注意:cell 被重用時,需要調用 setNeedsDisplayInRect:或 setNeedsDisplay 方法重繪cell。
- 減少動畫效果的使用,最好不要使用 insertRowsAtIndexPath:withRowAnimation:方法,而是直接調用 reloadData 方法。
- 減少視圖的數(shù)目。cell 包含了 textLabel、detailTextLabel 和 imageView 等 View,而你還可以自定義一些視圖放在它contextView 里,創(chuàng)建它會消耗較多的資源,并且也影響渲染的性能。
- cell 包含圖片,且數(shù)目較多,使用自定義 cell 速度比默認的要快。繼承 UITableViewCell,重寫 drawRect 方法:不過這樣以來,你會發(fā)現(xiàn)選中一行后,這個 cell 就變藍了,其中的內容就被遮擋住了。最簡單的辦法就是將 cell 的 selectionStyle 屬性設置為 None,這樣就不會高亮了。不需要與用戶交互的時候,使用 CALayer,將內容繪制到 layer 上,然后對 cell 的 contextView.layer 調用 addSubLayer方法。這個例子中,layer 并不會顯著影響性能,但如果 layer 透明,或者有圓角、變形等效果,就會影響繪制速度了。解決辦法可以使用預渲染圖像。
- (void)drawRect:(CGRect)rect { if (image) { [image drawAtPoint:imagePoint]; self.image = nil; } else { [placeHolder drawAtPoint:imagePoint]; } [text drawInRect:textRect withFont:font lineBreakModel:UILineBreakModelTailTruncation]; }
- 不要做多余的繪制工作。 在實現(xiàn) drawRect:的時候,它對 rect 參數(shù)就是需要繪制的區(qū)域,這個區(qū)域以外的不需要進行繪制。
- 預渲染圖像。你會發(fā)現(xiàn)即使做到了上述幾點,當新的圖像出現(xiàn)時,仍然會有短暫的停頓現(xiàn)象。解決的辦法就是在圖形上下文中畫,導出 UIimage 對象,然后再繪制到屏幕。(頭像圓角,或者其他變形的時候,用圖形上下文能提高性能。)異步繪制。
- 不要阻塞主線程。tableView 在更新數(shù)據的時候,整個界面卡主不動,完全不響應應用的用戶請求。常見的是網絡請求,等待時間長。解決方案:使用多線程,讓子線程去執(zhí)行這些函數(shù)或方法。
- 注意:當下載線程數(shù)超過2時,會顯著影響主線程的性能所以在不需要響應用戶請求的時,下載線程數(shù)可以增加到5,不建議再加了,以加快下載速度。如果用戶正在交互,應把線程數(shù)量控制在2個以內。
- 提前計算并緩存好高度。因為 heightForRowAtIndexPath 調用非常頻繁
- 選中正確的數(shù)據結構:學會選中對業(yè)務場景最適合的數(shù)組結構是寫出高效代碼的基礎。如果,數(shù)組:有序的一組值。使用索引來查詢比較快,使用值查詢很慢,插入、刪除很慢。字典:存儲鍵值對,用鍵查找比較快。集合:無序的一組值,用值來查詢很快,插入、刪除很快。
- gzip、zip 壓縮:當從服務器下載相關附件時,可以通過 gzip、zip 壓縮后再下載,使得內存更小,下載速度更快。
tableview 的 cell 如何嵌套 collectionView?
- 思路用網易新聞類似,用自定義的繼承 UITableViewCell 的類,在 initWithFrame 的構造方法中,初始化自定義的繼承自 UICollectionView
下拉和上拉的原理
- 以 tableView 的上拉刷新為例:
- 為了進行無縫閱讀,通過 tableView 的代理方法,willDisplayCell 判斷是否是最后一行
- 如果是最后一行,在顯示最后一行的同時,判斷當前是否存在上拉刷新
- 如果沒有上拉刷新,就進行加載數(shù)據,啟動小菊花
- 以 tableView 下拉刷新為例
- 判斷當前的上拉刷新視圖是否有動畫
- 如果沒有動畫,就不是上拉刷新
- 然后下拉刷新加載數(shù)據
- 加載完畢數(shù)據關閉數(shù)據
如何實現(xiàn) cell 的動態(tài)行高?
- 如果希望每條數(shù)據顯示自身的高,必須設置兩個屬性,1.預估行高,2.自定義行高
- 設置預估行高 _tableView.estimatedRowHeight = 200;
- 設置自定義行高 _tableView.rowHeight = UITableViewAutomaticDimension;
- 如果讓自定義行高有效,必須讓容器視圖有一個自下而上的約束
談談 webview
- iOS 開發(fā)中 webview 和 native code 的配合使用的一些經驗和技巧。
- webview 與運維成本低,更新幾乎不依賴 App 的版本,但在交互和性能上與 native code 有很大的差距。
- native code 與之對應。
- h5確實給 web 帶入了一個新時代。這個時代是什么?web App。也就是說,只有脫離 native 的這個前提,在瀏覽器的環(huán)境下,h5的意義才鞥呢顯現(xiàn)
awakeFormNib 與 viewdidload 區(qū)別
- awakeFromNib:當.nib 文件被加載的時候,會發(fā)送一個 awakeFromNib 消息到 nib 文件中的沒個對象,每個對象都可以定義自己的awakeFromNib函數(shù)來響應這個消息,執(zhí)行必要操作。也就是說 nib 文件創(chuàng)建 View 對象執(zhí)行awakeFromNib
- viewdiidload:當 View 對象被加載到內存就會執(zhí)行 Viewdidload,不管是通過 nib 還是代碼形式,創(chuàng)建對象就會執(zhí)行 viewdidload
layoutSubview 何時調用?
- 初始化 init 方法時不會觸發(fā)
- 滾動 UIScrollView 觸發(fā)
- 旋轉屏幕觸發(fā)
- 改變 View 的值觸發(fā),前提是 frame 改變了
- 改變 UIView 的大小觸發(fā)
viewcontroller 的 didReceiveMemoryWarning在什么時候調用,默認操作是什么?
- 引用程序收到來自系統(tǒng)的內存警告的時候,調用didReceiveMemoryWarning方法
- 默認做法:控制器上的 View 不再窗口上顯示時,調用 ViewWillUnload,直接銷毀View,并調用ViewDidUnload
如何實現(xiàn)瀑漂流,流水布局
- 使用 UICollectionView
- 使用自定義的 FlowLayout
- 需要在 layoutAttributeForElementsInRect 中設置自定布局的 item 的 frame
- 在 prepareLayout 中計算布局
- 遍歷數(shù)據內容,根據索引取出對應的 attributes(使用 LayoutAttributesForCellWithIndexPath),根據九宮格算法設置布局
- 細節(jié)1:實際布局,重寫shouldInvalidateLayoutForBoundsChange(bounds 改變從新布局,scrollView 的 contextOffset>bounds)
- 細節(jié)2:計算設置 itemSize(保證內容顯示完整,UICollectionView 的 context size 是根據 itemsize 計算的)根據列最大高度、對應列數(shù)量求出,最大高度累加得到
- 細節(jié)3:追加 item 到最短列,避免底部參數(shù)不齊
實現(xiàn)突破輪播圖
- scrollView 只需要設置三個 imageView 即可,并且默認顯示中間的 imageView
- 根據 scrollView 的移動情況,迅速變化三個 imageView 中的圖片數(shù)據
- imageView 更新完畢后,偷偷把 scrollView 拉回到中間的imageView 位置,這樣視覺效果上就實現(xiàn)了無限循環(huán)的效果。
load initialize 方法的區(qū)別
- (void)load;
- 當類對象被加入項目時,runtime 會向每個類發(fā)送 load 消息
- load 方法繪制每個類甚至分類被引入時僅調用一次,調用的順序:父類優(yōu)先于子類,子類優(yōu)先于分類。
- load 方法不會被類自動繼承
- (void)load;
- (void)initialize;
- 也是在第一次使用這個類的時候會調用這個方法。
- (void)initialize;
如何播放 gif 圖片,有什么優(yōu)化方案??
- UIImageView 用來顯示圖片,使用 UIImageView 中的動畫數(shù)組來實現(xiàn)動畫效果
- 用 UIWebView 來顯示動態(tài)圖片
- 第三方框架
事件傳遞與響應的過程?
- 在產生一個事件時,系統(tǒng)會將該事件加入到一個由 UIApplication 管理的事件隊列里去,UIApplication 會從事件隊列中取出最前面的事件,將它傳遞給先發(fā)送事件給應用程序的主窗口。
- 主窗口會調用 hitTest 方法尋找最適合的視圖控件,找到后會調用控件的 touches 方法來做具體的事情。
- 當調用 touches 方法,它的默認做法,就會將事件順著響應者鏈條上往上傳遞,傳遞給上一個響應者,接著就會調用上一個響應者的 touches 方法。
有哪些常見的 crash 場景?
- 訪問了僵尸對象
- 訪問了不存在的方法
- 數(shù)組越界
- 在定時器下一次回調前將定時器釋放掉
LLDB(GDB)常用的調試命令?
- p 輸出基本類型
- po 輸出 oc 對象
- expr 可以在調試中執(zhí)行表達式,并打印結果
如果一個函數(shù)10中有7次正確,3次錯誤,問題可能出現(xiàn)在什么地方?
- 從函數(shù)的所有條件分支類分析可能的錯誤。
- 檢測函數(shù)參數(shù)是否存在異常。
如何對 iOS 設備進行性能測試?
- profile->instruments->Time Profile 進行性能測試
- App 使用過程中,接聽電話。
- App 使用過程中,收到推送。
- 設備充電時,App 的影響以及流暢度。
- 設備在不同電量(低于10%、50%、95%)時,App 的響應以及流暢度
- 意外斷電,App 數(shù)據丟失情況。
- 網絡變化時,App 是如何響應的。
- 多點觸摸的情況。
- 跟其他 App 之間進行交互時的響應。
- 殺死進程在重新打開的反饋
- iOS 系統(tǒng)語言環(huán)境的變化
AFNetworking 斷點續(xù)傳
- 斷點續(xù)傳的主要思路:
- 檢查服務器的文件信息
- 檢查本地文件
- 如果比服務器小,斷點續(xù)傳,利用 HTTP 請求頭的 Range實現(xiàn)斷點續(xù)傳
- 如果比服務器大,重新下載
- 如何一樣,下載完成
簡單描述一下客戶端的緩存機制?
- 緩存可以分為:內存數(shù)據緩存、數(shù)據庫緩存、文件緩存
- 每次想獲取數(shù)據的時候
- 先檢測內存中有無緩存數(shù)據
- 在檢測本地有無緩存數(shù)據(數(shù)據庫、文件)
- 最終發(fā)送網絡請求
- 將服務器返回的網絡數(shù)據進行緩存(內存、數(shù)據庫、文件)
什么時候序列化和反序列化,用來做什么
- 序列化把對象轉化為字節(jié)序列的過程
- 反序列化把直接序列恢復成對象
- 把文件寫到文件或者數(shù)據庫中,并讀取出來
iOS 中常用的數(shù)據存儲方式有哪些?
- 數(shù)據存儲方法:UserDefault、KeyChain、File、DB
- file:Plist、Archiver、Stream
- DB:Core Data、FFDB