iOS 基礎知識(三)

image

目錄

一. GCD和OperationQueue
二. CADisplayLink、NSTimer使用注意
三. 內(nèi)存布局
四. Tagged Pointer
五. copy和mutableCopy
六. OC對象的內(nèi)存管理
七. AutoreleasePool自動釋放池
八. 圖片的解壓縮到渲染過程
九. 應用卡頓的原因以及優(yōu)化
十. APP的啟動

一. GCD和NSOperationQueue

GCD 可用于多核的并行運算妓湘;
GCD 會自動利用更多的 CPU 內(nèi)核(比如雙核温兼、四核)甘晤;
GCD 會自動管理線程的生命周期(創(chuàng)建線程柠辞、調(diào)度任務鲸阔、銷毀線程)订晌;
程序員只需要告訴 GCD 想要執(zhí)行什么任務,不需要編寫任何線程管理代碼饿自。

NSOperation汰翠、NSOperationQueue 是基于 GCD 更高一層的封裝,完全面向?qū)ο笳汛啤5潜?GCD 更簡單易用复唤、代碼可讀性也更高。

  • 可添加完成的代碼塊烛卧,在操作完成后執(zhí)行佛纫。
  • 添加操作之間的依賴關系,設定操作執(zhí)行的優(yōu)先級总放,方便的控制執(zhí)行順序呈宇;設置最大并發(fā)數(shù)。
  • 可以很方便的取消一個操作的執(zhí)行间聊。
  • 使用 KVO 觀察對操作執(zhí)行狀態(tài)的更改:isExecuteing攒盈、isFinished抵拘、isCancelled哎榴。
image

同步和異步主要影響:能不能開啟新的線程
同步:在當前線程中執(zhí)行任務,不具備開啟新線程的能力
異步:在新的線程中執(zhí)行任務僵蛛,具備開啟新線程的能力

并發(fā)和串行主要影響:任務的執(zhí)行方式
并發(fā):多個任務并發(fā)(同時)執(zhí)行
串行:一個任務執(zhí)行完畢后尚蝌,再執(zhí)行下一個任務

image

二. CADisplayLink、NSTimer使用注意

  • CADisplayLink充尉、NSTimer會對target產(chǎn)生強引用飘言,如果target又對它們產(chǎn)生強引用,那么就會引發(fā)循環(huán)引用驼侠。解決辦法是使用代理對象NSProxy姿鸿。
  • NSTimer依賴于RunLoop,如果RunLoop的任務過于繁重倒源,可能會導致NSTimer不準時苛预。而GCD的定時器會更加準時。

三. 內(nèi)存布局

  • 棧區(qū)(heap):由系統(tǒng)去管理笋熬。地址從高到低分配热某。先進后出。會存一些局部變量,函數(shù)跳轉(zhuǎn)跳轉(zhuǎn)時現(xiàn)場保護(寄存器值保存于恢復)昔馋,這些系統(tǒng)都會幫我們自動實現(xiàn)筹吐,無需我們干預。所以大量的局部變量秘遏,深遞歸丘薛,函數(shù)循環(huán)調(diào)用都可能耗盡棧內(nèi)存而造成程序崩潰 。
  • 堆區(qū)(stack):需要我們自己管理內(nèi)存邦危,alloc申請內(nèi)存release釋放內(nèi)存榔袋。創(chuàng)建的對象也都放在這里。 地址是從低到高分配铡俐。堆是所有程序共享的內(nèi)存凰兑,當N個這樣的內(nèi)存得不到釋放,堆區(qū)會被擠爆审丘,程序立馬癱瘓吏够。這就是內(nèi)存泄漏。
  • 全局區(qū)/靜態(tài)區(qū)(staic):全局變量和靜態(tài)變量的存儲是放在一塊的滩报,初始化的全局變量和靜態(tài)變量在一塊區(qū)域锅知, 未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。程序結束后有系統(tǒng)釋放脓钾。
  • 常量區(qū):常量字符串就是放在這里的售睹,還有const常量。
  • 代碼區(qū):存放App代碼可训,App程序會拷貝到這里昌妹。
image

四. Tagged Pointer

  • 從64bit開始,iOS引入了Tagged Pointer技術握截,用于優(yōu)化NSNumber飞崖、NSDate、NSString等小對象的存儲
  • 在沒有使用Tagged Pointer之前谨胞, NSNumber等對象需要動態(tài)分配內(nèi)存固歪、維護引用計數(shù)等,NSNumber指針存儲的是堆中NSNumber對象的地址值
  • 使用Tagged Pointer之后胯努,NSNumber指針里面存儲的數(shù)據(jù)變成了:Tag + Data牢裳,也就是將數(shù)據(jù)直接存儲在了指針中
  • 當指針不夠存儲數(shù)據(jù)時,才會使用動態(tài)分配內(nèi)存的方式來存儲數(shù)據(jù)
  • objc_msgSend能識別Tagged Pointer叶沛,比如NSNumber的intValue方法蒲讯,直接從指針提取數(shù)據(jù),節(jié)省了以前的調(diào)用開銷
  • 如何判斷一個指針是否為Tagged Pointer恬汁?
    iOS平臺伶椿,最高有效位是1(第64bit)辜伟;Mac平臺,最低有效位是1
image

五. copy和mutableCopy

image

六. OC對象的內(nèi)存管理

  • 在iOS中脊另,使用引用計數(shù)來管理OC對象的內(nèi)存导狡。
  • 一個新創(chuàng)建的OC對象引用計數(shù)默認是1,當引用計數(shù)減為0偎痛,OC對象就會銷毀旱捧,釋放其占用的內(nèi)存空間。
  • 調(diào)用retain會讓OC對象的引用計數(shù)+1踩麦,調(diào)用release會讓OC對象的引用計數(shù)-1枚赡。
  • 當調(diào)用alloc、new谓谦、copy贫橙、mutableCopy方法返回了一個對象,在不需要這個對象時反粥,要調(diào)用release或者autorelease來釋放它卢肃;想擁有某個對象,就讓它的引用計數(shù)+1才顿;不想再擁有某個對象莫湘,就讓它的引用計數(shù)-1。
image
image

七. AutoreleasePool自動釋放池

AutoreleasePool(自動釋放池) 是OC中的一種內(nèi)存自動回收機制郑气,在釋放池中的調(diào)用了autorelease方法的對象都會被壓在該池的頂部(以棧的形式管理對象)幅垮。當自動釋放池被銷毀的時候,在該池中的對象會自動調(diào)用release方法來釋放資源尾组,銷毀對象忙芒。以此來達到自動管理內(nèi)存的目的。

image

__AtAutoreleasePool 實際是一個結構體演怎,在內(nèi)部首先執(zhí)行objc_autoreleasePoolPush()匕争,然后在調(diào)用objc_autoreleasePoolPop(atautoreleasepoolobj)。

struct __AtAutoreleasePool {
  __AtAutoreleasePool() {
//構造函數(shù)爷耀,在創(chuàng)建結構體時調(diào)用
atautoreleasepoolobj = objc_autoreleasePoolPush();
}
  ~__AtAutoreleasePool() {
//析構函數(shù),在結構體銷毀的時候調(diào)用
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
  void * atautoreleasepoolobj;
};

AutoreleasePoolPage的結構

  • 每個AutoreleasePoolPage對象占用4096字節(jié)內(nèi)存拍皮,除了用來存放它內(nèi)部的成員變量歹叮,剩下的空間用來存放autorelease對象的地址。
  • 所有的AutoreleasePoolPage對象通過雙向鏈表的形式連接在一起铆帽。
image
  • 調(diào)用push方法會將一個POOL_BOUNDARY入棧咆耿,并且返回其存放的內(nèi)存地址
  • 調(diào)用pop方法時傳入一個POOL_BOUNDARY的內(nèi)存地址,會從最后一個入棧的對象開始發(fā)送release消息爹橱,直到遇到這個POOL_BOUNDARY
  • id *next指向了下一個能存放autorelease對象地址的區(qū)域

Autorelease何時釋放萨螺?

1、手動調(diào)用AutoreleasePool的釋放方法(drain方法)
2、Autorelease對象是在當前的runloop迭代結束時釋放的慰技,而它能夠釋放的原因是系統(tǒng)在每個runloop迭代中都加入了自動釋放池Push和Pop

Runloop和Autorelease的關系

  • App啟動后椭盏,蘋果在主線程 RunLoop 里注冊了兩個 Observer,其回調(diào)都是 _wrapRunLoopWithAutoreleasePoolHandler()吻商。
  • 第一個 Observer 監(jiān)視的事件是 Entry(即將進入Loop)掏颊,其回調(diào)內(nèi)會調(diào)用 _objc_autoreleasePoolPush() 創(chuàng)建自動釋放池。其 order 是 -2147483647艾帐,優(yōu)先級最高乌叶,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前。
  • 第二個 Observer 監(jiān)視了兩個事件: BeforeWaiting(準備進入休眠) 時調(diào)用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池并創(chuàng)建新池柒爸;Exit(即將退出Loop) 時調(diào)用 _objc_autoreleasePoolPop() 來釋放自動釋放池准浴。這個 Observer 的 order 是 2147483647,優(yōu)先級最低捎稚,保證其釋放池子發(fā)生在其他所有回調(diào)之后兄裂。
image

八. 圖片的解壓縮到渲染過程

    1. 假設我們使用 +imageWithContentsOfFile: 方法從磁盤中加載一張圖片,這個時候的圖片并沒有解壓縮阳藻;
    1. 然后將生成的 UIImage 賦值給 UIImageView 晰奖;
    1. 接著一個隱式的 CATransaction 捕獲到了 UIImageView 圖層樹的變化;
    1. 在主線程的下一個 runloop 到來時腥泥,Core Animation 提交了這個隱式的 transaction 匾南,這個過程可能會對圖片進行 copy 操作,而受圖片是否字節(jié)對齊等因素的影響蛔外,這個 copy 操作可能會涉及以下部分或全部步驟:
  • 分配內(nèi)存緩沖區(qū)用于管理文件 IO 和解壓縮操作蛆楞;
  • 將文件數(shù)據(jù)從磁盤讀到內(nèi)存中;
  • 將壓縮的圖片數(shù)據(jù)解碼成未壓縮的位圖形式夹厌,這是一個非常耗時的 CPU 操作豹爹;
  • 最后 Core Animation 中CALayer使用未壓縮的位圖數(shù)據(jù)渲染 UIImageView 的圖層。
  • CPU計算好圖片的Frame,對圖片解壓之后.就會交給GPU來做圖片渲染
    1. 渲染流程
  • GPU獲取獲取圖片的坐標
  • 將坐標交給頂點著色器(頂點計算)
  • 將圖片光柵化(獲取圖片對應屏幕上的像素點)
  • 片元著色器計算(計算每個像素點的最終顯示的顏色值)
  • 從幀緩存區(qū)中渲染到屏幕上

總結:圖片渲染到屏幕的過程: 讀取文件->計算Frame->圖片解碼->解碼后紋理圖片位圖數(shù)據(jù)通過數(shù)據(jù)總線交給GPU->GPU獲取圖片F(xiàn)rame->頂點變換計算->光柵化->根據(jù)紋理坐標獲取每個像素點的顏色值(如果出現(xiàn)透明值需要將每個像素點的顏色*透明度值)->渲染到幀緩存區(qū)->渲染到屏幕

九. 應用卡頓的原因以及優(yōu)化

CPU: 計算視圖frame矛纹,文本計算和排版臂聋,圖片解碼,需要繪制紋理圖片通過數(shù)據(jù)總線交給GPU或南。
GPU: 紋理混合孩等,頂點變換與計算,像素點的填充計算,渲染到幀緩沖區(qū)采够。
平時所說的“卡頓”主要是因為在主線程執(zhí)行了比較耗時的操作肄方,
可以添加Observer到主線程RunLoop中,通過監(jiān)聽RunLoop狀態(tài)切換的耗時蹬癌,以達到監(jiān)控卡頓的目的权她。

  • 1. 屏幕呈像原理

image
  • 2. 卡頓產(chǎn)生的原因

image

在 VSync 信號到來后虹茶,系統(tǒng)圖形服務會通過 CADisplayLink 等機制通知 App,App 主線程開始在 CPU 中計算顯示內(nèi)容隅要,比如視圖的創(chuàng)建蝴罪、布局計算、圖片解碼拾徙、文本繪制等洲炊。隨后 CPU 會將計算好的內(nèi)容提交到 GPU 去,由 GPU 進行變換尼啡、合成暂衡、渲染。隨后 GPU 會把渲染結果提交到幀緩沖區(qū)去崖瞭,等待下一次 VSync 信號到來時顯示到屏幕上狂巢。由于垂直同步的機制,如果在一個 VSync 時間內(nèi)书聚,CPU 或者 GPU 沒有完成內(nèi)容提交唧领,則那一幀就會被丟棄,等待下一次機會再顯示雌续,而這時顯示屏會保留之前的內(nèi)容不變斩个。這就是界面卡頓的原因。
從上面的圖中可以看到驯杜,CPU 和 GPU 不論哪個阻礙了顯示流程受啥,都會造成掉幀現(xiàn)象。所以開發(fā)時鸽心,也需要分別對 CPU 和 GPU 壓力進行評估和優(yōu)化滚局。

  • 3. 卡頓優(yōu)化

CPU

  • 盡量用輕量級的對象,比如用不到事件處理的地方顽频,可以考慮使用CALayer取代UIView
  • 不要頻繁地調(diào)用UIView的相關屬性藤肢,比如frame、bounds糯景、transform等屬性嘁圈,盡量減少不必要的修改
  • 盡量提前計算好布局,在有需要時一次性調(diào)整對應的屬性莺奸,不要多次修改屬性
  • Autolayout會比直接設置frame消耗更多的CPU資源
  • 圖片的size最好剛好跟UIImageView的size保持一致
  • 控制一下線程的最大并發(fā)數(shù)量
  • 盡量把耗時的操作放到子線程:文本處理(尺寸計算丑孩、繪制)、圖片處理(解碼灭贷、繪制)等

GPU

  • 盡量避免短時間內(nèi)大量圖片的顯示,盡可能將多張圖片合成一張進行顯示
  • GPU能處理的最大紋理尺寸是4096x4096略贮,一旦超過這個尺寸甚疟,就會占用CPU資源進行處理仗岖,所以紋理盡量不要超過這個尺寸
  • 盡量減少視圖數(shù)量和層次
  • 減少透明的視圖(alpha<1),不透明的就設置opaque為YES
  • 盡量避免出現(xiàn) 離屏渲染
image

十. APP的啟動

APP的冷啟動可以概括為3大階段:dyld览妖、runtime轧拄、main

  • 1. dyld

dyld(dynamic link editor),Apple的動態(tài)鏈接器讽膏,可以用來裝載Mach-O文件(可執(zhí)行文件檩电、動態(tài)庫等)。
啟動APP時府树,dyld所做的事情有:

  • 裝載APP的可執(zhí)行文件俐末,同時會遞歸加載所有依賴的動態(tài)庫.
  • 當dyld把可執(zhí)行文件、動態(tài)庫都裝載完畢后奄侠,會通知Runtime進行下一步的處理.
  • 2. runtime

啟動APP時卓箫,runtime所做的事情有:

  • 調(diào)用map_images進行可執(zhí)行文件內(nèi)容的解析和處理
  • 在load_images中調(diào)用call_load_methods,調(diào)用所有Class和Category的+load方法
  • 進行各種objc結構的初始化(注冊Objc類 垄潮、初始化類對象等等)
  • 調(diào)用C++靜態(tài)初始化器和attribute((constructor))修飾的函數(shù)
  • 3. main

接下來就是UIApplicationMain函數(shù)烹卒,AppDelegate的application:didFinishLaunchingWithOptions:方法

  • 4. APP啟動優(yōu)化

image

推薦學習資料:

Swift從入門到精通
每周一道算法題
戀上數(shù)據(jù)結構與算法(一)
戀上數(shù)據(jù)結構與算法(二)

如果需要跟我交流的話:
※ 簡書:http://www.reibang.com/u/e15d1f644bea

原文鏈接:http://www.reibang.com/p/895b8e6fbeaa

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市弯洗,隨后出現(xiàn)的幾起案子旅急,更是在濱河造成了極大的恐慌,老刑警劉巖牡整,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件藐吮,死亡現(xiàn)場離奇詭異,居然都是意外死亡果正,警方通過查閱死者的電腦和手機炎码,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來秋泳,“玉大人潦闲,你說我怎么就攤上這事∑戎澹” “怎么了歉闰?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長卓起。 經(jīng)常有香客問我和敬,道長,這世上最難降的妖魔是什么戏阅? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任昼弟,我火速辦了婚禮,結果婚禮上奕筐,老公的妹妹穿的比我還像新娘舱痘。我一直安慰自己变骡,他們只是感情好,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布芭逝。 她就那樣靜靜地躺著塌碌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪旬盯。 梳的紋絲不亂的頭發(fā)上台妆,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機與錄音胖翰,去河邊找鬼接剩。 笑死,一個胖子當著我的面吹牛泡态,可吹牛的內(nèi)容都是我干的搂漠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼某弦,長吁一口氣:“原來是場噩夢啊……” “哼桐汤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起靶壮,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤怔毛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后腾降,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拣度,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年螃壤,在試婚紗的時候發(fā)現(xiàn)自己被綠了抗果。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡奸晴,死狀恐怖冤馏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寄啼,我是刑警寧澤逮光,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站墩划,受9級特大地震影響涕刚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜乙帮,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一杜漠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦碑幅、人聲如沸戴陡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至异吻,卻和暖如春裹赴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背诀浪。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工棋返, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人雷猪。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓睛竣,卻偏偏與公主長得像,于是被迫代替她去往敵國和親求摇。 傳聞我的和親對象是個殘疾皇子射沟,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359

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

  • 1. 父類實現(xiàn)深拷貝時,子類如何實現(xiàn)深度拷貝与境。父類沒有實現(xiàn)深拷貝時验夯,子類如何實現(xiàn)深度拷貝。 1.1 深拷貝同淺拷貝...
    i愛吃土豆的貓閱讀 392評論 0 1
  • 1.OC的類可以多重繼承嗎摔刁?可以實現(xiàn)多個接口嗎挥转?要想實現(xiàn)類似多重繼承如何實現(xiàn)?答:OC不可以實現(xiàn)多重繼承共屈“笠ィ可以實現(xiàn)...
    歐辰_OSR閱讀 1,992評論 0 30
  • 技術基礎 1、我們說的Objective-C是動態(tài)運行時語言是什么意思拗引? 答:OC可以通過Runtime這個運行時...
    慌莫染閱讀 1,365評論 0 4
  • 1.為什么說Objective-C是一門動態(tài)的語言借宵? 1.object-c類的類型和數(shù)據(jù)變量的類型都是在運行是確定...
    蝸牛上上升閱讀 818評論 0 3
  • iOS 基礎知識概述 基本修飾屬性 assion-基本用于修飾基本數(shù)據(jù)類型 如 int 等 是弱引用 copyco...
    浮萍向北閱讀 379評論 0 5