問(wèn): 關(guān)鍵字static的作用是什么?
- 函數(shù)體內(nèi)
static變量的作用范圍為該函數(shù)體,不同于auto變量,該變量的內(nèi)存只被分配一次,因此其值在下次調(diào)用時(shí)仍維持上次的值;
- 函數(shù)體內(nèi)
- 在模塊內(nèi)的
static全局變量可以被模塊內(nèi)所用函數(shù)訪問(wèn),但不能被模塊外其它函數(shù)訪問(wèn);
- 在模塊內(nèi)的
- 在模塊內(nèi)的
static函數(shù)只可被這一模塊內(nèi)的其它函數(shù)調(diào)用,這個(gè)函數(shù)的使用范圍被限制在聲明它的模塊內(nèi);
- 在模塊內(nèi)的
問(wèn): 關(guān)鍵字const是什么含義? 分別解釋下列語(yǔ)句中const的作用?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
含義
- 欲阻止一個(gè)變量被改變,可以使用const關(guān)鍵字。在定義該const變量時(shí),通常需要對(duì)它進(jìn)行初始化,因?yàn)橐院缶蜎](méi)有機(jī)會(huì)再去改變它了;
- 對(duì)指針來(lái)說(shuō),可以指定指針本身為const,也可以指定指針?biāo)傅臄?shù)據(jù)為const,或二者同時(shí)指定為const;
- 在一個(gè)函數(shù)聲明中,const可以修飾形參,表明它是一個(gè)輸入?yún)?shù),在函數(shù)內(nèi)部不能改變其值;
- 對(duì)于類的成員函數(shù),若指定其為const類型,則表明其是一個(gè)常函數(shù),不能修改類的成員變量;
作用
const int a; //a是一個(gè)常整型數(shù)
int const a; //a是一個(gè)常整型數(shù)
const int *a; //a是一個(gè)指向常整型數(shù)的指針(也就是,整型數(shù)是不可修改的,但指針可以)
int * const a; //a是一個(gè)指向整型數(shù)的常指針(也就是說(shuō),指針指向的整型數(shù)是可以修改的,但指針是不可修改的)
int const * a const; //a是一個(gè)指向常整型數(shù)的常指針(也就是說(shuō),指針指向的整型數(shù)是不可修改的,同時(shí)指針也是不可修改的)
問(wèn): 使用nonatomic一定是線程安全的嗎?
-
nonatomic: 非原子性,set方法的實(shí)現(xiàn)不加鎖,不安全,性能高
-
-
atomic:atomic性能低,atomic通過(guò)鎖定機(jī)制來(lái)確保其原子性,但只是讀/寫(xiě)安全,不能絕對(duì)保證線程的安全,當(dāng)多線程同時(shí)訪問(wèn)的時(shí)候,會(huì)造成線程不安全??梢允褂镁€程鎖來(lái)保證線程的安全。
-
問(wèn): 對(duì)于語(yǔ)句N(xiāo)SString *obj = [[NSData alloc] init]; ,編譯時(shí)和運(yùn)行時(shí)obj分別是什么類型?
- 編譯時(shí)是
NSString類型
- 編譯時(shí)是
- 運(yùn)行時(shí)是
NSData類型
- 運(yùn)行時(shí)是
問(wèn): Objective-C如何對(duì)內(nèi)存管理的,說(shuō)說(shuō)你的看法和解決方法?
- 每個(gè)對(duì)象都有一個(gè)引用計(jì)數(shù)器,每個(gè)新對(duì)象的計(jì)數(shù)器是1,當(dāng)對(duì)象的計(jì)數(shù)器減為0時(shí),就會(huì)被銷(xiāo)毀
- 通過(guò)retain可以讓對(duì)象的計(jì)數(shù)器+1、release可以讓對(duì)象的計(jì)數(shù)器-1
- 還可以通過(guò)autorelease pool管理內(nèi)存
- 如果用ARC,編譯器會(huì)自動(dòng)生成管理內(nèi)存的代碼
注意:不管是MRC還是ARC都是在編譯時(shí)完成的
問(wèn): iOS數(shù)據(jù)持久化有哪些?
為何要持久化:iOS 開(kāi)發(fā)可以沒(méi)有持久化,持久化更多的是業(yè)務(wù)需求;比如記錄用戶是否登陸,下次進(jìn)應(yīng)用不需要再登陸。
因?yàn)?iOS 的 沙盒機(jī)制,所以持久化分為兩類:沙盒內(nèi) 和 沙盒外。
- 沙盒內(nèi)
(1)NSKeyedArchiver: 只要遵循了NSCoding協(xié)議并正確實(shí)現(xiàn)了initWithCoder和encodeWithCoder方法的類都可以通過(guò)NSKeyedArchiver來(lái)序列化。
歸檔使用archiveRootObject,解歸檔使用unarchiveObjectWithFile;需要指定文件路徑。
(2)NSUserDefaults:[NSUserDefaults standardUserDefaults]獲取NSUserDefaults對(duì)象,以key-value方式進(jìn)行持久化操作。
(3)plist: 寫(xiě)入使用writeToFile,讀取使用xxxWithContentsOfFile;需要指定文件路徑。
(4) 數(shù)據(jù)庫(kù):sqlite、CoreData和Realm等
(5) 文件: 這里要和plist區(qū)分一下,plist方式是字典/數(shù)組數(shù)據(jù)格式寫(xiě)入文件;而這里的文件方式不限數(shù)據(jù)格式。
- 沙盒內(nèi)
- 沙盒外
沙盒內(nèi)的方式在應(yīng)用被刪除后數(shù)據(jù)都會(huì)丟失,如果想要不丟失則需要使用KeyChain。
KeyChain本質(zhì)是一個(gè)sqlite數(shù)據(jù)庫(kù),其保存的所有數(shù)據(jù)都是加密過(guò)的。
KeyChain分為私有和公有,公有則需要指定group,一個(gè)group中的應(yīng)用可以共享此KeyChain。
使用KeyChain過(guò)程中要理解下面幾個(gè)問(wèn)題:
①:自己使用的KeyChain和系統(tǒng)自帶的KeyChain數(shù)據(jù)是隔離的,內(nèi)部應(yīng)該是不同數(shù)據(jù)庫(kù)文件;
②:KeyChain數(shù)據(jù)可備份到iCloud中;
③:不需要聯(lián)網(wǎng),也不用登陸iCloud賬號(hào);一個(gè)設(shè)備一個(gè)sqlite數(shù)據(jù)庫(kù),但是不同應(yīng)用組不共享數(shù)據(jù);
④:要在另一臺(tái)設(shè)備上使用當(dāng)前設(shè)備存儲(chǔ)的KeyChain信息,需要當(dāng)前設(shè)備進(jìn)行數(shù)據(jù)備份,再在另一設(shè)備上復(fù)原數(shù)據(jù);比較常用的是iCloud備份方式;
⑤:系統(tǒng)自帶的KeyChain中賬號(hào)密碼分類數(shù)據(jù)可在系統(tǒng)設(shè)置->賬號(hào)與密碼里面看到,你退出iCloud賬號(hào)還是存在,只是iCloud會(huì)幫你備份如果你設(shè)置了的話;這個(gè)和照片是一樣的道理。
- 沙盒外
問(wèn): id和NSObject*的區(qū)別?
-
id可以指向OC中的任何對(duì)象,而NSObject*只能指向NSObject及子類對(duì)象
問(wèn): strong 和 weak 的區(qū)別?
問(wèn): (堆和棧) 哪些數(shù)據(jù)是放在堆上的,哪些是放在棧上的?
- 棧:由系統(tǒng)自動(dòng)分配,速度較快,不會(huì)產(chǎn)生內(nèi)存碎片,
- 堆:是由alloc分配的內(nèi)存,速度比較慢,而且容易產(chǎn)生內(nèi)存碎片,不過(guò)用起來(lái)最方便。
問(wèn): UITableView 優(yōu)化?
- cell復(fù)用
我們經(jīng)常在注意cellForRowAtIndexPath:中為每一個(gè)cell綁定數(shù)據(jù),實(shí)際上在調(diào)用cellForRowAtIndexPath:的時(shí)候cell還沒(méi)有被顯示出來(lái),為了提高效率我們應(yīng)該把數(shù)據(jù)綁定的操作放在cell顯示出來(lái)后再執(zhí)行,可以在tableView:willDisplayCell:forRowAtIndexPath:(以后簡(jiǎn)稱willDisplayCell)方法中綁定數(shù)據(jù)。
注意willDisplayCell在cell在tableview展示之前就會(huì)調(diào)用,此時(shí)cell實(shí)例已經(jīng)生成,所以不能更改cell的結(jié)構(gòu),只能是改動(dòng)cell上的UI的一些屬性(例如label的內(nèi)容等)。
- cell復(fù)用
- cell高度的計(jì)算
(1)定高的cell,應(yīng)該采用如下方式:self.tableView.rowHeight = 88;
(2)動(dòng)態(tài)高度的cell:tableView: tableViewheightForRowAtIndexPath:,該方法實(shí)現(xiàn)后,上面的rowHeight的設(shè)置將會(huì)變成無(wú)效。在這個(gè)方法中,我們需要提高cell高度的計(jì)算效率,來(lái)節(jié)省時(shí)間。
自從iOS8之后有了self-sizing cell的概念,cell可以自己算出高度,使用self-sizing cell需要滿足以下三個(gè)條件:
① 使用Autolayout進(jìn)行UI布局約束(要求cell.contentView的四條邊都與內(nèi)部元素有約束關(guān)系)。
② 指定TableView的estimatedRowHeight屬性的默認(rèn)值。
③ 指定TableView的rowHeight屬性為UITableViewAutomaticDimension。
- cell高度的計(jì)算
- 渲染
(1)當(dāng)有圖像時(shí),預(yù)渲染圖像,在bitmap context先將其畫(huà)一遍,導(dǎo)出成UIImage對(duì)象,然后再繪制到屏幕,這會(huì)大大提高渲染速度。具體內(nèi)容可以自行查找“利用預(yù)渲染加速顯示iOS圖像”相關(guān)資料。
(2)渲染最好時(shí)的操作之一就是混合(blending)了,所以我們不要使用透明背景,將cell的opaque值設(shè)為Yes,背景色不要使用clearColor,盡量不要使用陰影漸變等。
(3)由于混合操作是使用GPU來(lái)執(zhí)行,我們可以用CPU來(lái)渲染,這樣混合操作就不再執(zhí)行??梢栽?UIView的drawRect方法中自定義繪制。
- 渲染
- 減少視圖的數(shù)目
我們?cè)?cell上添加系統(tǒng)控件的時(shí)候,實(shí)際上系統(tǒng)都會(huì)調(diào)用底層的接口進(jìn)行繪制,大量添加控件時(shí),會(huì)消耗很大的資源并且也會(huì)影響渲染的性能。當(dāng)使用默認(rèn)的UITableViewCell并且在它的ContentView上面添加控件時(shí)會(huì)相當(dāng)消耗性能。所以目前最佳的方法還是繼承UITableViewCell,并重寫(xiě)drawRect方法。
- 減少視圖的數(shù)目
- 減少多余的繪制操作
在實(shí)現(xiàn)drawRect方法的時(shí)候,它的參數(shù)rect就是我們需要繪制的區(qū)域,在rect范圍之外的區(qū)域我們不需要進(jìn)行繪制,否則會(huì)消耗相當(dāng)大的資源。
- 減少多余的繪制操作
- 不要給cell動(dòng)態(tài)添加subView
在初始化cell的時(shí)候就將所有需要展示的添加完畢,然后根據(jù)需要來(lái)設(shè)置hide屬性顯示和隱藏。
- 不要給cell動(dòng)態(tài)添加subView
- 異步化UI,不要阻塞主線程
我們時(shí)常會(huì)看到這樣一個(gè)現(xiàn)象,就是加載時(shí)整個(gè)頁(yè)面卡住不動(dòng),怎么點(diǎn)都沒(méi)用,仿佛死機(jī)了一般。原因是主線程被阻塞了。所以對(duì)于網(wǎng)路數(shù)據(jù)的請(qǐng)求或者圖片的加載,我們可以開(kāi)啟多線程,將耗時(shí)操作放到子線程中進(jìn)行,異步化操作。這個(gè)或許每個(gè)iOS開(kāi)發(fā)者都知道的知識(shí),不必多講。
- 異步化UI,不要阻塞主線程
- 滑動(dòng)時(shí)按需加載對(duì)應(yīng)的內(nèi)容
如果目標(biāo)行與當(dāng)前行相差超過(guò)指定行數(shù),只在目標(biāo)滾動(dòng)范圍的前后指定3行加載。
滑動(dòng)很快時(shí),只加載目標(biāo)范圍內(nèi)的cell,這樣按需加載(配合SDWebImage),極大提高流暢度。
- 滑動(dòng)時(shí)按需加載對(duì)應(yīng)的內(nèi)容
問(wèn): 消息列表頁(yè)面如何優(yōu)化?
首先我們發(fā)消息時(shí)候觀察一下消息列表的特性,當(dāng)發(fā)送一條消息時(shí)候,消息的數(shù)量會(huì)變化,列表會(huì)出現(xiàn)在最上邊的位置,列表內(nèi)的內(nèi)容會(huì)發(fā)生變化。從消息列表的特性,我們就可以分析出要優(yōu)化的點(diǎn)了。通過(guò)這些點(diǎn),我們做了一些優(yōu)化:
- 如果列表消息從沒(méi)顯示過(guò)需要刷新列表,創(chuàng)建好一個(gè)
cell后,將cell插入到第一位上,cell插入的性能要高于刷新tableview的性能。
- 如果列表消息從沒(méi)顯示過(guò)需要刷新列表,創(chuàng)建好一個(gè)
- 如果消息已經(jīng)顯示過(guò)了,但是并不是第一位,則需要刷新列表。
- 如果消息已經(jīng)顯示,并且是第一位,則只需要
cell的內(nèi)容變化。
- 如果消息已經(jīng)顯示,并且是第一位,則只需要
- 只修改
cell里的內(nèi)容,不進(jìn)行刷新cell整體,這里要注意的是,一定要最小化刷新。刷新點(diǎn)越小,性能損耗越小。我們項(xiàng)目架構(gòu)是MVVM,采用了ReactiveCocoa框架,針對(duì)每個(gè)cell上的可變化的控件數(shù)據(jù)進(jìn)行了監(jiān)聽(tīng),每一個(gè)cell上對(duì)應(yīng)一個(gè)vm,這樣當(dāng)vm上的數(shù)據(jù)變化時(shí)候,cell上的數(shù)據(jù)也就跟著變了。做到了最小化刷新。
- 只修改
- 避免使用
autolayout計(jì)算位置,這個(gè)很重要,在性能要求高的情況下,autolayout計(jì)算會(huì)很耗時(shí)間,尤其在算tableview高度的時(shí)候可見(jiàn)一斑。可喜的是消息列表的高度是固定的,所以在計(jì)算高度時(shí)候我們并未花費(fèi)時(shí)間。
- 避免使用
- 使用
(__kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath而不使用-(nullable __kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier來(lái)查找cell,因?yàn)橄逻叺姆椒〞?huì)多查一次,耗時(shí)更長(zhǎng)一點(diǎn)。
- 使用
-
cellForRowAtIndexPath方法只負(fù)責(zé)創(chuàng)建cell,willDisplayCell方法才給cell進(jìn)行賦值操作。從方法名字就可以看出來(lái)原因
-
- 當(dāng)來(lái)消息轟炸時(shí)候,必然會(huì)是不同的人發(fā)來(lái)的消息,會(huì)導(dǎo)致
tableview不可避免的刷新,如果不加處理必然會(huì)卡頓,要知道,機(jī)器也是有瓶頸的。這里我們做的優(yōu)化是根據(jù)cpu的使用率選擇性的刷新tableview。后來(lái)我們發(fā)現(xiàn)微信也是有這個(gè)現(xiàn)象,并不是實(shí)時(shí)的刷新,我們猜測(cè)也是類似處理。
- 當(dāng)來(lái)消息轟炸時(shí)候,必然會(huì)是不同的人發(fā)來(lái)的消息,會(huì)導(dǎo)致
- 重繪制系統(tǒng)控件,相信你也發(fā)現(xiàn)了消息列表里,主要有兩個(gè)控件,一個(gè)是頭像,一個(gè)是
label。而系統(tǒng)的UIImageView用來(lái)顯示頭像,未免有點(diǎn)重。我們的處理是使用UIView,設(shè)置View得layer.content來(lái)處理。針對(duì)layer層做的setImageWithUrl的第三方庫(kù)也不少,大家可以自行查詢。另一個(gè)就是label,如果你能集成UIView自己繪制一個(gè)label,我想也許會(huì)有一點(diǎn)效果。
- 重繪制系統(tǒng)控件,相信你也發(fā)現(xiàn)了消息列表里,主要有兩個(gè)控件,一個(gè)是頭像,一個(gè)是
問(wèn): 聊天界面如何優(yōu)化?
聊天界面的優(yōu)化算是比較繁瑣的了,但是優(yōu)化點(diǎn)跟回話列表的優(yōu)化差不多。上邊提到回話列表里最耗時(shí)的tableview的高度是固定的,而聊天界面的幾乎每條消息的高度都可能不一樣,所以我們?cè)趦?yōu)化聊天界面時(shí)候最重要的一點(diǎn)就是計(jì)算tableviewcell的高度。而我們?cè)谟?jì)算tableview的高度是怎么做的呢?
主要有兩個(gè)準(zhǔn)則:
(1) 第一個(gè)是能在后臺(tái)線程執(zhí)行的都放在后臺(tái)線程里。
(2) 第二個(gè)計(jì)算高度要放在顯示之前。
- 巧妙的選擇控件。(比如上個(gè)問(wèn)題提到的用
UIView的layer.content來(lái)代替UIImageView, 圖片加點(diǎn)擊也可以用view,然后監(jiān)聽(tīng)view的touch事件,像button這種重量級(jí)的控件在性能為主的app面前,我對(duì)他們都是棄之如敝履。)
- 巧妙的選擇控件。(比如上個(gè)問(wèn)題提到的用
- 減少使用
layer層的cornerRadius,mask等圓角的繪制,這會(huì)引發(fā)離屏渲染,增加cpu的占用率。如果業(yè)務(wù)需要的話,我們可以通過(guò)UIBezierPath來(lái)drawInRect它。
- 減少使用
- 避免設(shè)置透明
- 避免
autolayout設(shè)定控件位置
- 避免
- 盡可能的減少視圖的層級(jí),如果你能把所有的控件都繪制到一個(gè)
View上,可想而知性能會(huì)爆棚。
- 盡可能的減少視圖的層級(jí),如果你能把所有的控件都繪制到一個(gè)
問(wèn): Swift的可選類型?
問(wèn): Swift 中 Struct 和 Class的區(qū)別 ?
-
property初始化的不同:
主要的差別就是class在初始化時(shí)不能直接把property放在 默認(rèn)的constructor的參數(shù)里,而是需要自己創(chuàng)建一個(gè)帶參數(shù)的constructor
-
- 變量賦值方式不同(深淺copy)
struct賦值“=”的時(shí)候,會(huì)copy一份完整相同的內(nèi)容給另一個(gè)變量 -> 【開(kāi)辟了新的內(nèi)存地址】(深拷貝)
class賦值“=”的時(shí)候,不會(huì)copy一份完整的內(nèi)容給另一個(gè)變量,只是增加了原變量?jī)?nèi)存地址的引用而已 -> 【沒(méi)有開(kāi)辟了新的內(nèi)存地址】(淺拷貝)
- 變量賦值方式不同(深淺copy)
-
immutable變量
Swift語(yǔ)言的特色之一就是可變動(dòng)內(nèi)容和不可變內(nèi)容用var和let來(lái)甄別,如果初始為let的變量再去修改會(huì)發(fā)生編譯錯(cuò)誤。
struct也遵循這一特性
class不存在這樣的問(wèn)題
-
-
struct和class的差別是struct的function要去改變property的值的時(shí)候要加上mutating,而class不用。
-
-
struct不能繼承,class可以繼承。
-
-
struct分配在棧中,class分配在堆中。
-
Swift 用 Struct 作為數(shù)據(jù)模型時(shí)需要注意什么問(wèn)題?
優(yōu)點(diǎn):
- 安全性:因?yàn)?
Struct是用值類型傳遞的,它們沒(méi)有引用計(jì)數(shù)。
- 安全性:因?yàn)?
- 內(nèi)存:由于他們沒(méi)有引用數(shù),他們不會(huì)因?yàn)檠h(huán)引用導(dǎo)致內(nèi)存泄漏。
- 速度:值類型通常來(lái)說(shuō)是以棧的形式分配的,而不是用堆。因此他們比
Class要快很多!
- 速度:值類型通常來(lái)說(shuō)是以棧的形式分配的,而不是用堆。因此他們比
- 拷貝:
Objective-C里拷貝一個(gè)對(duì)象,你必須選用正確的拷貝類型(深拷貝、淺拷貝),而值類型的拷貝則非常輕松!
- 拷貝:
- 線程安全:值類型是自動(dòng)線程安全的。無(wú)論你從哪個(gè)線程去訪問(wèn)你的
Struct,都非常簡(jiǎn)單。
- 線程安全:值類型是自動(dòng)線程安全的。無(wú)論你從哪個(gè)線程去訪問(wèn)你的
缺點(diǎn): (需要注意的地方)
-
Objective-C:當(dāng)你的項(xiàng)目的代碼是Swift和Objective-C混合開(kāi)發(fā)時(shí),你會(huì)發(fā)現(xiàn)在Objective-C的代碼里無(wú)法調(diào)用Swift的Struct。因?yàn)橐?Objective-C里調(diào)用Swift代碼的話,對(duì)象需要繼承于NSObject。
Struct不是Objective-C的好朋友。
-
- 繼承:
Struct不能相互繼承。
- 繼承:
-
NSUserDefaults:Struct不能被序列化成NSData對(duì)象。
-