2016.05.20 10:24java
塵封已久的學習基礎總結,最近公司項目不是很忙,終於抽空整理出來,現分享出來。node
一次性執行 dispatch_once
,延遲操做dispatch_after
(這裏是延遲推到線程中,而不是在線程中等待,所以好比設置延遲1秒執行,可是一秒後只是推到了線程中,不會馬上執行),調度組等,其高級功能有
dispatch_barrier_async
柵欄來控制異步操做的順序dispatch_apply
充分利用多核進行快速迭代遍歷dispatch_group_t
隊列組,添加到隊列組中的任務完成以後會調用dispatch_group_notify
函數,能夠實現相似A、B兩個耗時操做都完成以後,去主線程更新UI的操做maxConcurrentOperationCount
,繼續/暫停/所有取消,能夠快隊列設置操做的依賴關係,經過KVO監聽 NSOperation 對象的屬性,如 isCancelled、isFinished;對象可重用。
NSInvocationOperation
和NSBlockOperation
建立方法等這些基礎面試官每每默認你是會的NSOperationQueue
只有兩種隊列:主隊列、其餘隊列。其餘隊列包含了串行和併發NSOperation + NSOperationQueue
將任務加入到隊列[operation2 addDependency:operation1];
(operation2 依賴於operation1的完成,但這兩個任務要加入到同一個隊列中)一般耗時的操做都放在子線程處理,而後到主線程更新UI,如ios
這個就須要用到字典,以圖片的下載地址url爲key,下載操做爲value,全部的圖片大概分紅三類:已經下載好的,正在下載的和將要下載的;面試
當一張圖片將要進行下載操做的時候,先判斷緩存中是否有相同的圖片,若是有的話就返回,沒有的話就根據url的md5加密值去沙盒中找,有的話就拿出來用,沒有的話再去以圖片的url爲key去字典中找有沒有正在進行的任務,最後去判斷等待的下載操做任務裏面的字典有無相同key,若是沒有,就本身開啓任務,記錄一下,文件保存的名稱是url的md5值spring
這裏創建了兩個字典 : 1.iconCache:保存緩存的圖片 2.blockOperation 用來保存下載任務 數據庫
每當進入或退出程序時,會進行圖片文件的管理:超過一星期的文件會被清除,若是設置了最大緩存,超過這個緩存就會刪除最舊的文件,直到當前緩存文件爲最大緩存文件的一半大小;swift
通常app中大部分緩存都是圖片的狀況下,能夠直接調用clear方法進行清除緩存,getSize()方法獲取當前緩存大小。設計模式
如何處理事件數組
界面刷新: 當UI改變( Frame變化、 UIView/CALayer 的繼承結構變化等)時,或手動調用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法後,這個 UIView/CALayer 就被標記爲待處理。 蘋果註冊了一個用來監聽BeforeWaiting和Exit的Observer,在它的回調函數裏會遍歷全部待處理的 UIView/CAlayer 以執行實際的繪製和調整,並更新 UI 界面。瀏覽器
手勢識別: 若是上一步的 _UIApplicationHandleEventQueue() 識別到是一個guesture手勢,會調用Cancel方法將當前的touchesBegin/Move/End 系列回調打斷。隨後系統將對應的 UIGestureRecognizer 標記爲待處理。 蘋果註冊了一個 Observer 監測 BeforeWaiting (Loop即將進入休眠) 事件,其回調函數爲 _UIGestureRecognizerUpdateObserver(),其內部會獲取全部剛被標記爲待處理的 GestureRecognizer,並執行GestureRecognizer的回調。 當有 UIGestureRecognizer 的變化(建立/銷燬/狀態改變)時,這個回調都會進行相應處理。
網絡請求:最底層是CFSocket層,而後是CFNetwork將其封裝,而後是NSURLConnection對CFNetwork進行面向對象的封裝。當網絡開始傳輸時,NSURLConnection建立了兩個新線程:com.apple.NSURLConnectionLoader和com.apple.CFSocket.private。其中CFSocket線程是處理底層socket鏈接的。NSURLConnectionLoader這個線程內部會使用RunLoop來接受底層socket的事件,並添加到上層的Delegate
應用
滑動與圖片刷新:當tableView的cell上有須要從網絡獲取的圖片的時候,滾動tableView,異步線程回去加載圖片,加載完成後主線程會設置cell的圖片,可是會形成卡頓。能夠設置圖片的任務在CFRunloopDefaultMode下進行,當滾動tableView的時候,Runloop切換到UITrackingRunLoopMode,不去設置圖片,而是而是當中止的時候,再去設置圖片。(在viewDidLoad中調用self.imageView performSelector@selector(setImage) withObject:...afterDelay:...inModes@[NSDefayltRunLoopMode])
常駐子線程,保持子線程一直處理事件 爲了保證線程長期運轉,能夠在子線程中加入RunLoop,而且給Runloop設置item,防止Runloop自動退出
//1.得到NSUserDefaults文件
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
//2.向文件中寫入內容
[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);
複製代碼
// 反歸檔
- (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"];
}
複製代碼
* 歸檔,把對象歸檔時須要調用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];
}
複製代碼
屬性列表
數據庫:SQLite
Core Data 點擊查看大神講解
屬性列表
從存儲數據大小來看,歸檔、偏好設置、屬性列表三種方法適合存儲數據量較小的數據,數據庫、CoreData方法適合存儲數據量較大的數據
從加密性來看,其中歸檔會將數據進行加密,而偏好設置是直接保存到屬性列表中,不會對數據進行加密
從存儲類型來看,屬性列表只能存放固定的七種類型(可在plist文件中看到),歸檔對存儲類型無限制
KVC(key-value-coding鍵值編碼,跟多狀況下會簡化程序代碼)的常見用法:
KVC缺點:一旦使用KVC,編譯器沒法檢查出錯誤,即不會對設置的鍵、鍵路徑進行錯誤檢查,且執行效率低於自定義的setter和getter方法,由於使用KVC鍵值編值,必須先解析字符串,而後設置或訪問對象的實例變量
經過KVO(key-value-observing,典型的觀察者模式,被觀察的對象必須使用KVC鍵值編碼來修改它的實例變量,這樣才能被觀察者觀察到)監聽person對象中name屬性發生改變
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
複製代碼
* 當person的name的值發生改變時,就會執行該方法
複製代碼
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
do something....
}
複製代碼
GET請求的數據會負載URL以後,即把數據放在HTTP協議頭中,以?區分URL和傳輸數據,參數之間以&相連,英文字母/數字,原樣發送,若是是空格,轉化爲+,若是是中文,把字符串用BASE64加密;POST就是把提交的數據放在HTTP包的包體中
GET通常用於提交少許數據(最多提交1k,瀏覽器限制),POST用於提交大量數據(理論上無限制,收服務器限制)
GET 無反作用,POST 有反作用
GET提交的數據能夠在瀏覽器歷史記錄中看到,安全性很差,別人能夠拿到帳號密碼,POST不會
Get是向服務器發索取數據的一種請求,而POST是向服務器發提交數據的一種請求,只是發送機制不一樣
GET不能夠設置書籤,POST能夠設置書籤
POST支持更多編碼類型且不對數據類型限制
什麼狀況下用POST:
什麼狀況下用GET:
POST請求的url表示處理該封閉實體的資源,該資源多是個數據接收過程、某種協議的網關、或者接收註解的獨立實體。
PUT請求中的url表示請求中封閉的實體-用戶代理知道url的目標,而且服務器沒法將請求應用到其餘資源。若是服務器但願該請求應用到另外一個url,就必須發送一個301響應;用戶代理可經過本身的判斷來決定是否轉發該請求。
POST是用來提交數據的。提交的數據放在HTTP請求的正文裏,目的在於提交數據並用於服務器端的存儲,而不容許用戶過多的更改相應數據(主要是相對於在url 修改要麻煩不少)。
PUT操做是冪等的。所謂冪等是指無論進行多少次操做,結果都同樣。好比我用PUT修改一篇文章,而後在作一樣的操做,每次操做後的結果並無不一樣
POST操做既不是安全的,也不是冪等的,好比常見的POST重複加載問題:當咱們屢次發出一樣的POST請求後,其結果是建立出了若干的資源。
安全和冪等的意義在於:當操做沒有達到預期的目標時,咱們能夠不停的重試,而不會對資源產生反作用。從這個意義上說,POST操做每每是有害的,但不少時候咱們仍是不得不使用它。
還有一點須要注意的就是,建立操做可使用POST,也可使用PUT,區別在於POST 是做用在一個集合資源之上的,而PUT操做是做用在集合的一個具體資源之上的,再通俗點說,若是URL能夠在客戶端肯定,那麼就使用PUT,若是是在服務端肯定,那麼就使用POST,好比說不少資源使用數據庫自增主鍵做爲標識信息,而建立的資源的標識信息究竟是什麼只能由服務端提供,這個時候就必須使用POST。
實際上是個面試官八個不懂Swift,並且通常不懂得就問個Swift與OC的不一樣。。題主也只是在自學Swift,等到3.0出了以後再深刻研究,並且項目中可能也要開始從混編逐漸向Swift靠攏了。
Swift是一門更加現代化的語言,可是目前還在成長階段,更新改動比較大,雖說其底層思想不變,變的是API和接口,可是每次更新完Xcode看到本身的Swift項目仍是有些淡淡的憂傷,並且目前Swift開發都要轉成OC的runtime,包略大,所以題主認爲成熟項目最好仍是採用OC
先記住一句話:OC底層面向對象,而Swift底層更加面向協議
咱們已經見識過Apple使用了大量協議,好比在tableView當中,咱們能夠經過協議來告訴Apple須要多少個表視圖單元格,而不是每時每刻都要繼承UITableViewController
在這裏以MVVM做爲測試用例:好比如今須要創建一個相似設置界面的tableView,每一個cell須要一個label和一個switch,自定義SwitchWithTextTableViewCell,在其內部創建一個configure方法中對label的title,titleFont,titleColor,switch的switchOn和switchColor等進行初始化,但這種方式很是累贅,好比添加一個副標題,就須要額外添加三個屬性
可是利用協議SwitchWithTextCellProtocol,讓視圖模型實現這個協議,而後在這裏設置全部的屬性
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)
}
複製代碼
經過swift2.0重點餓協議擴展,就能夠經過默認值來作一些處理了,若是對於大多數單元格來講,能夠肯定某一種顏色的話,就能夠對其創建擴展,而後設置顏色便可,全部實現此協議的視圖就沒有必要再去設置這個顏色了
如今,個人configure方法裏面只要實現此協議的值就能夠了
// 這個方法只須要一個參數,相比於以前的多個參數簡便了不少
class SwitchWithTextTableViewCell: UITableViewCell {
func configure(withDelegate delagate: SwitchWithTextCellProtocol) {
// 在這裏配置方法
}
}
複製代碼
struct MinionModeViewController: 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!")
}
}
}
複製代碼
let cell = tableView.dequeueReuseableCellWithIdentifier("SwitchWithTextTableViewCell", forIndexPath: indexPath) as! SwitchWithTextTableViewCell
cell.configure(withDelegate: MinionModeViewModel())
return cell
複製代碼
再把模型放在視圖模型層級,一遍對其進行跟蹤,再視圖模型中傳遞這些信息,這樣單元格就能夠生成了
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 }
}
複製代碼
// SwitchWithTextTableViewCell
func configure(withDataSource dataSource: SwitchWithTextCellDataSource, delegate: SwitchWithTextCellDelegate?) {
// 在這裏配置視圖
}
複製代碼
struct MinionModeViewModel: SwiftWithTextCellDataSource {
var title = "Minion Mode!!"
var switchOn = true
}
複製代碼
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!")
}
}
}
複製代碼
// SettingViewController
let viewModel = MinionModeViewModel()
cell.configure(withDataSource:viewModel, delegate: viewModel)
return cell
複製代碼
僅僅須要建立視圖模型,而後將其傳遞到配置方法當中,最後返回單元格,就能夠了
在遊戲開發中一般會有一個很龐大的層級關係,以及一系列的繼承,好比各類怪,繼承在這裏顯得十分有意義,可是隨着層級的擴展,這個項目就會變得凌亂起來
好比說須要設計一個能夠射擊的怪物,但這時候塔防頂部的大炮也會射擊,就須要把「射擊輔助類」提取出來,可是若是一直這樣提取子類,代碼後面會一團亂麻
將這個代碼重構,再也不去提取可以射擊或者可以加血的子類,而是將其提取爲協議,經過協議擴展來實現這個功能,代碼更加簡潔,更利於理解
// 一看這個對象的類型,就知道他有哪些功能,而不是一個個去查找她的實現
class ZapMonster: GameObject, GunTraint, HealthTraint, MovementTraint {
...
}
複製代碼
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)
}
複製代碼
這種狀況下,好比須要一個圖片框,只要一個iamgeProtocol就能夠了,設計師要求改全部標籤的顏色的話一行代碼就能夠搞定
class SwitchWithTextTableViewCell<T where T: TextPresentable, T: SwitchPresentable>: UITableViewCell {
private var delegate: T?
// T是視圖模型
func configure(withDelegate delegate: T) {
// 在這裏配置視圖
}
}
複製代碼
在這種狀況下,它沒有實現這些協議,可是會期待某種實現這些協議的東西傳遞進去,所以咱們使用了泛型,這個單元格期待了一個實現了TextPresentableProtocol 的委託。就咱們而言,傳遞進去的將是一個實現了這些協議的東西就能夠了,如今要基於這些信息在單元格當中配置全部的東西了,如今就能夠基於浙西而信息在單元格中配置全部的東西了
extension MinionModeViewModel: TextPresentable {
var text: String { return "Minion Mode" }
var textColor: UIColor { return .blackColor() }
var font: UIFont { return .systemFontOfsize(17.0) }
}
複製代碼
咱們的視圖模型將會有一個TextPresentable代碼,在其中能夠配置文本、顏色、字體,而且因爲全部的這些協議擴展中都已經有默認值了,甚至不須要視圖模型去實現這些具體的內容
最後,視圖模型當中的代碼就只須要dequeue相應的單元格。而後經過視圖模型對其進行配置,而後返回單元格便可
一種是針對全部 Cell 具備固定高度的狀況,經過:self.tableView.rowHeight = 88; 指定了一個全部 cell 都是 88 高度的 UITableView,對於定高需求的表格,強烈建議使用這種(而非下面的)方式保證沒必要要的高度計算和調用。
另外一種方式就是實現 UITableViewDelegate 中的:heightForRowAtIndexPath:須要注意的是,實現了這個方法後,rowHeight 的設置將無效。因此,這個方法適用於具備多種 cell 高度的 UITableView。
iOS7以後出了了estimatedRowHeight,面對不一樣高度的cell,只要給一個預估的值就能夠了,先給一個預估值,而後邊滑動邊計算,可是缺點就是
iOS8 WWDC 中推出了 self-sizing cell 的概念,旨在讓 cell 本身負責本身的高度計算,使用 frame layout 和 auto layout 均可以享受到:
相同的代碼在 iOS7 和 iOS8 上滑動順暢程度徹底不一樣,iOS8 莫名奇妙的卡。很大一部分緣由是 iOS8 上的算高機制大不相同,從 WWDC 也卻是能找到點解釋,cell 被認爲隨時均可能改變高度(如從設置中調整動態字體大小),因此每次滑動出來後都要從新計算高度。
使用 UITableView+FDTemplateLayoutCell(百度知道負責人孫源) 無疑是解決算高問題的最佳實踐之一,既有 iOS8 self-sizing 功能簡單的 API,又能夠達到 iOS7 流暢的滑動效果,還保持了最低支持 iOS6
FDTemplateLayoutCell 的高度預緩存是一個優化功能,利用RunLoop空閒時間執行預緩存任務計算,當用戶正在滑動列表時顯然不該該執行計算任務影響滑動體驗。
iOS平臺由於UIKit自己的特性,須要將全部的UI操做都放在主線程執行,因此有時候就習慣將一些線程安全性不肯定的邏輯,以及它線程結束後的彙總工做等等放到了主線程,因此主線程包含大量計算、IO、繪製都有可能形成卡頓。
設置正確的 reuseidentifer 以重用 cell
儘可能將 View 設置爲不透明,包括 cell 自己(backgroundcolor默認是透明的),圖層混合靠GPU去渲染,若是透明度設置爲100%,那麼GPU就會忽略下面全部的layer,節約了不少沒必要要的運算。模擬器上點擊「Debug」菜單,而後選擇「color Blended Layers」,會把全部區域分紅綠色和紅色,綠色的好,紅色的性能差(通過混合渲染的),固然也有一些圖片雖然是不透明的,可是也會顯示紅色,若是檢查代碼沒錯的話,通常就是圖片自身的性質問題了,直接聯繫美工或後臺解決就行了。除非必需要用GPU加載的,其餘最好要用CPU加載,由於CPU通常不會百分百加載,能夠經過CoreGraphics畫出圓角
有時候美工失誤,圖片大小給錯了,引發沒必要要的圖片縮放(能夠找美工去改,固然也能夠異步去裁剪圖片而後緩存下來),仍是使用Instrument的Color Misaligned Images,黃色表示圖片須要縮放,紫色表示沒有像素對齊。固然通常狀況下圖片格式不會給錯,有些圖片格式是GPU不支持的,就還要勞煩CPU去進行格式轉換。還有能夠經過Color Offscreen-Rendered Yellow來檢測離屏渲染(就是把渲染結果臨時保存,等到用的時候再取出,這樣相對於普通渲染更消耗內存,使用maskToBounds、設置shadow,重寫drawRect方法都會致使離屏渲染) 避免漸變,cornerRadius在默認狀況下,這個屬性只會影響視圖的背景顏色和 border,可是不會離屏繪製,不影響性能。不用clipsToBounds(過多調用GPU去離屏渲染),而是讓後臺加載圖片並處理圓角,並將處理過的圖片賦值給UIImageView。UIImageView 的圓角經過直接截取圖片實現,圓角路徑直接用貝塞爾曲線UIBezierPath繪製(人爲指定路徑以後就不會觸發離屏渲染),UIGraphicsBeginImageContextWithOptions。UIView的圓角可使用CoreGraphics畫出圓角矩形,核心是CGContextAddArcToPoint 函數。它中間的四個參數表示曲線的起點和終點座標,最後一個參數表示半徑。調用了四次函數後,就能夠畫出圓角矩形。最後再從當前的繪圖上下文中獲取圖片並返回,最後把這個圖片插入到視圖層級的底部。 「Flash updated Regions」用於標記發生重繪的區域
若是 row 的高度不相同,那麼將其緩存下來
若是 cell 顯示的內容來自網絡,那麼確保這些內容是經過異步下載
使用 shadowPath 來設置陰影,圖層最好不要使用陰影,陰影會致使離屏渲染(在進入屏幕渲染以前,還看不到的時候會再渲染一次,儘可能不要產生離屏渲染)
減小 subview 的數量,不要去添加或移除view,要就顯示,不要就隱藏
在 cellForRowAtIndexPath 中儘可能作更少的操做,最好是在別的地方算好,這個方法裏只作數據的顯示,若是須要作一些處理,那麼最好作一次以後將結果儲存起來.
使用適當的數據結構來保存須要的信息,不一樣的結構會帶來不一樣的操做代價
使用,rowHeight , sectionFooterHeight 和 sectionHeaderHeight 來設置一個恆定高度 , 而不是從代理(delegate)中獲取
cell作數據綁定的時候,最好在willDisPlayCell裏面進行,其餘操做在cellForRowAtIndexPath,由於前者是第一頁有多少條就執行多少次,後者是第一次加載有多少個cell就執行多少次,並且調用後者的時候cell還沒顯示
讀取文件,寫入文件,最好是放到子線程,或先讀取好,在讓tableView去顯示
tableView滾動的時候,不要去作動畫(微信的聊天界面作的就很好,在滾動的時候,動態圖就不讓他動,滾動中止的時候才動,否則可能會有點影響流暢度)。在滾動的時候加載圖片,中止拖拽後在減速過程當中不加載圖片,減速中止後加載可見範圍內圖片
重用問題:好比UITableViewCell、UICollectionViewCell、UITableViewHeaderFooterViews等設置正確的reuseIdentifier,充分重用
懶加載控件、頁面:對於不是馬上使用的數據,都應該使用延遲加載的方式,好比網絡鏈接失敗的提示界面,可能一直都用不到
使用Autorelease Pool:在某些循環建立臨時變量處理數據時,自動釋放池以保證能及時釋放內存
不要使用太多的xib/storyboard:載入時會將其內部的圖片在內的全部資源載入內存,即便將來好久纔會須要使用,相對於純代碼寫的延遲加載,在性能和內存上就差了不少
數據緩存:對於cell的行高要緩存起來,使用reloadData效率也極高,對於網絡數據,不須要每次都請求的,應該緩存起來,能夠寫入數據庫,也能夠經過plist文件存儲
選擇正確的數據結構:針對不一樣的業務場景選擇最合適的數據結構是寫出高效代碼的基礎
gzip/zip壓縮:當從服務器下載相關附件時,能夠經過 zip壓縮後再下載,使得內存更小,下載速度也更快
重大開銷對象:一些objects的初始化很慢,好比NSDateFormatter和 NSCalendar,可是又無可避免的須要使用,一般做爲屬性存儲起來,避免反覆使用
避免反覆處理數據:須要應用須要從服務器加載數據,常爲JSON或者XML格式的數據,在服務器端或者客戶端使用相同的數據結構很重要
選擇圖片時,要對圖片進行壓縮處理,根據不一樣的狀況選擇不一樣的圖片加載方式,-imageNamed:讀取到內存後會緩存下來,適合圖片資源較小,使用很頻繁的圖片;-initWithContentsOfFiles:僅加載圖片而不緩存,適合較大的圖片。如果collectionView中使用大量圖片的時候,能夠用UIVIew.layer.contents=(__bridge id _Nullable)(model.clipedImage.CGImage);這樣就更輕量級一些
固然有時候也會用到一些第三方,好比在使用UICollectionView和UITableView的時候,Facebook有一個框架叫AsyncDisplayKit,這個庫就能夠很好地提高滾動時流暢性以及圖片異步下載功能(不支持sb和autoLayout,須要手動進行約束設置),AsyncDisplayKit用相關node類,替換了UIView和它的子類,並且是線程安全的。它能夠異步解碼圖片,調整圖片大小以及對圖片和文本進行渲染,把這些操做都放到子線程,滑動的時候就流暢許多。我認爲這個庫最方便的就是實現圖片異步解碼。UIImage顯示以前必需要先解碼完成,並且解碼仍是同步的。尤爲是在UICollectionView/UITableView 中使用 prototype cell顯示大圖,UIImage的同步解碼在滾動的時候會有明顯的卡頓。另一個很吸引人的點是AsyncDisplayKit能夠把view層次結構轉成layer。由於複雜的view層次結構開銷很大,若是不須要view特有的功能(例如點擊事件),就可使用AsyncDisplayKit 的layer backing特性從而得到一些額外的提高。固然這個庫還處於開發階段,還有一些地方地方有待完善,好比不支持緩存,我要使用這個庫的時候通常是結合Alamofire和AlamofireImage實現圖片的緩存
但願此文對您的求職或夯實基礎起到做用,感謝閱讀!