iOS 常見的基礎(chǔ)知識,相信很多人回答不上

一、什么是arc?(arc是為了解決什么問題誕生的?)

Automatic Reference Counting,自動引用計(jì)數(shù),即ARC,WWDC2011和iOS5所引入的。ARC是新的LLVM 3.0編譯器的一項(xiàng)特性,使用ARC,解決了手動內(nèi)存管理的麻煩。永遠(yuǎn)不寫retain,release和autorelease三個(gè)關(guān)鍵字。

當(dāng)ARC開啟時(shí),編譯器將自動在代碼合適的地方插入retain,release和autorelease。

關(guān)于效率:ARC是Objective-C編譯器的特性,而不是運(yùn)行時(shí)特性或者垃圾回收機(jī)制,ARC所做的只不過是在代碼編譯時(shí)為你自動在合適的位置插入release或autorelease,就如同之前MRC時(shí)你所做的那樣。因此,至少在效率上ARC機(jī)制是不會比MRC弱的,而因?yàn)榭梢栽谧詈线m的地方完成引用計(jì)數(shù)的維護(hù),以及部分優(yōu)化,使用ARC甚至能比MRC取得更高的運(yùn)行效率。

ARC的基本規(guī)則:只要某個(gè)對象被任一strong指針指向,那么它將不會被銷毀。如果對象沒有被任何strong指針指向,那么就將被銷毀。weak類型的指針也可以指向?qū)ο螅遣⒉粫钟性搶ο蟆?/p>

使用ARC以后,不論是strong還是weak類型的指針,都不再會指向一個(gè)dealloced的對象,從根源上解決了意外釋放導(dǎo)致的crash。

那么是為了什么而誕生的ARC?

當(dāng)然是因?yàn)镸RC手動管理內(nèi)存的缺陷,那么MRC的缺陷有哪些呢:

1.當(dāng)我們要釋放一個(gè)堆內(nèi)存時(shí),首先要確定指向這個(gè)堆空間的指針都被release了。(避免提前釋放)

2.釋放指針指向的堆空間,首先要確定哪些指針指向同一個(gè)堆,這些指針只能釋放一次。(MRC下即誰創(chuàng)建,誰釋放,避免重復(fù)釋放)

3.模塊化操作時(shí),對象可能被多個(gè)模塊創(chuàng)建和使用,不能確定最后由誰去釋放。

4.多線程操作時(shí),不確定哪個(gè)線程最后使用完畢

這就是為什么需要誕生ARC的原因啦。

二、請解釋以下keywords的區(qū)別:assign vs weak, __block vs __weak

assign vs weak:

(1).weak,表明該屬性定義了一種“非擁有關(guān)系” (nonowning relationship)。為這種屬性設(shè)置新值時(shí),設(shè)置方法既不保留新值,也不釋放舊值。

(2).assign也可以修飾對象,但是用assign修飾的對象在釋放后,指針的地址還是存在的,也就是說指針并沒有被置為nil,會造成眾所周知的野指針異常。然而,assign修飾的基礎(chǔ)數(shù)據(jù)類型(例如NSInteger等)和C數(shù)據(jù)類型(int,float,double,char)等一般分配在棧空間上,??臻g的內(nèi)存會由系統(tǒng)自動處理,當(dāng)分配的??臻g的內(nèi)存沒有被指針指向時(shí)就會被銷毀,所以不會造成野指針異常。

(3).weak比 assign多了一個(gè)功能就是當(dāng)屬性所指向的對象消失的時(shí)候(也就是內(nèi)存引用計(jì)數(shù)為0)會自動賦值為 nil,這樣再向 weak修飾的屬性發(fā)送消息就不會導(dǎo)致野指針操作crash。

__block vs __weak:

_weak__typeof(&*self)weakSelf =self; 等同于

__weakUIViewController*weakSelf =self;

為什么不用__block 是因?yàn)橥ㄟ^引用來訪問self的實(shí)例變量 ,self被retain,block也是一個(gè)強(qiáng)引用,引起循環(huán)引用,用__week是弱引用,當(dāng)self釋放時(shí),weakSelf已經(jīng)等于nil。

三、__block在arc和非arc下含義一樣嗎?

MRC下__block修飾的變量,并不改變引用計(jì)數(shù),同時(shí)block內(nèi)部并不對引入的外部對象,更改引用計(jì)數(shù)。

ARC下block會被修改為__NSMallocBlock__,同時(shí)引用計(jì)數(shù)增加了

四、使用atomic一定是線程安全的嗎?

當(dāng)使用atomic時(shí),雖然對屬性的讀和寫是原子性的,但是仍然可能出現(xiàn)線程錯(cuò)誤:當(dāng)線程A進(jìn)行寫操作,這時(shí)其他線程的讀或者寫操作會因?yàn)樵摬僮鞫却.?dāng)A線程的寫操作結(jié)束后,B線程進(jìn)行寫操作,然后當(dāng)A線程需要讀操作時(shí),卻獲得了在B線程中的值,這就破壞了線程安全,如果有線程C在A線程讀操作前release了該屬性,那么還會導(dǎo)致程序崩潰。所以僅僅使用atomic并不會使得線程安全,我們還要為線程添加lock來確保線程的安全。

也就是要注意:atomic所說的線程安全只是保證了getter和setter存取方法的線程安全,并不能保證整個(gè)對象是線程安全的。如下列所示:

比如:@property(atomic,strong)NSMutableArray *arr;

如果一個(gè)線程循環(huán)的讀數(shù)據(jù),一個(gè)線程循環(huán)寫數(shù)據(jù),那么肯定會產(chǎn)生內(nèi)存問題,因?yàn)檫@和setter、getter沒有關(guān)系。如使用[self.arr objectAtIndex:index]就不是線程安全的。好的解決方案就是加鎖。

據(jù)說,atomic要比nonatomic慢大約20倍。一般如果條件允許,我們可以讓服務(wù)器來進(jìn)行加鎖操作。

五、描述一個(gè)你遇到過的retain cycle例子。

父類@class 子類,并且聲明了子類對象,子類引用了父類,聲明了父類對象,在Main函數(shù)中,對子類中的父類賦值,并且對父類中子類賦值。就會導(dǎo)致retain cycle 循環(huán)引用,詳細(xì)看如下代碼:

子類
父類
mian函數(shù)調(diào)用

這樣,引用循環(huán)就形成了,造成的結(jié)果就是,子對象對父對象的引用無法再取得,導(dǎo)致兩個(gè)對象被隱式引用,最后兩個(gè)test實(shí)例的引用計(jì)數(shù)都是1,最后無法釋放。

六、+(void)load; +(void)initialize;有什么用處?

+ initialize和+ load是 NSObject 類的兩個(gè)類方法,它們會在運(yùn)行時(shí)自動調(diào)用,我們可以利用其特性做一些初始化操作。

initialize和load的區(qū)別在于:load是只要類所在文件被引用就會被調(diào)用,而initialize是在類或者其子類的第一個(gè)方法被調(diào)用前調(diào)用。所以如果類沒有被引用進(jìn)項(xiàng)目,就不會有l(wèi)oad調(diào)用;但即使類文件被引用進(jìn)來,但是沒有使用,那么initialize也不會被調(diào)用。詳情信息?

七、為什么其他語言里叫函數(shù)調(diào)用,OC里則是給對象發(fā)消息(或者談下對runtime的理解)

在OC中,消息是直到運(yùn)行時(shí)才被綁定在方法實(shí)現(xiàn)上的。在編譯的過程中,編譯器會把一個(gè)消息表達(dá)式轉(zhuǎn)換成對objc_msgSend方法的調(diào)用。objc_msgSend方法會以接收消息的對象(receiver)消息的方法名(selector)作為兩個(gè)主要的參數(shù),再加上消息本身帶有的參數(shù)

[receiver message];

會被轉(zhuǎn)化成

objc_msgSend(receiver, selector);

當(dāng)程序運(yùn)行的時(shí)候,運(yùn)行時(shí)系統(tǒng)會根據(jù)傳入objc_msgSend方法的receiver和selector來查找應(yīng)該被執(zhí)行的過程。也就是說,具體哪段代碼被執(zhí)行是由receiver和selector這兩者共同決定的。

那么怎么根據(jù)receiver和selector來查找對應(yīng)的過程呢?

發(fā)送消息的關(guān)鍵就是,編譯器給每個(gè)類和每個(gè)對象都構(gòu)建了一個(gè)結(jié)構(gòu)。每個(gè)類的結(jié)構(gòu)都包含了一個(gè)指向自己父類的指針,和一個(gè)分發(fā)表。分發(fā)表中存放著這個(gè)類的每個(gè)selector和這個(gè)selector對應(yīng)的過程的入口。

而當(dāng)一個(gè)繼承自NSObject或NSProxy的對象被創(chuàng)建的時(shí)候,在對象的變量中,還有一個(gè)指向它對應(yīng)的類結(jié)構(gòu)的指針,這個(gè)指針叫做isa。所以,當(dāng)?shù)弥猺eceiver時(shí),objc_msgSend方法會通過receiver的isa指針找到它對應(yīng)的類的結(jié)構(gòu),并查找這個(gè)類的分發(fā)表中是否有需要的selector。如果沒有找到,則沿著指向父類的指針,去父類的分發(fā)表中查找。

為了加速這個(gè)過程,運(yùn)行時(shí)系統(tǒng)會為每個(gè)類緩存selector和對應(yīng)的方法的地址。每個(gè)類都有一個(gè)這樣的cache,其中存放的不僅有它本身的方法,還有它繼承的方法。

如果objc_msgSend方法找到了這個(gè)過程,它會調(diào)用這個(gè)過程,并且傳遞所有的參數(shù)以及兩個(gè)隱藏參數(shù)receiverselector。正是因?yàn)閭鬟f了這兩個(gè)隱藏參數(shù),程序員才能在OC源代碼中使用self和_cmd。

唯一一個(gè)能繞過晚綁定的方式,就是直接得到某個(gè)方法的地址,然后像調(diào)用函數(shù)一樣調(diào)用它。NSObject中有一個(gè)方法methodForSelector:,它可以直接返回對應(yīng)方法的函數(shù)指針。

注意,在調(diào)用這個(gè)函數(shù)指針的時(shí)候,也需要傳遞receiver和selector的這兩個(gè)隱藏參數(shù)

八、什么是method swizzling?

其實(shí)跟字面的意思很相近。方法的調(diào)和??梢匀バ薷膐c中兩個(gè)方法的調(diào)用。就是把兩個(gè)實(shí)現(xiàn)調(diào)換

Method ori_Method =? class_getInstanceMethod([MYclass class], @selector(lastObject));

Method my_Method = class_getInstanceMethod([MYclass class], @selector(myLastObject));

method_exchangeImplementations(ori_Method, my_Method);

九、UIView和CALayer是啥關(guān)系

1. 首先UIView可以響應(yīng)事件,Layer不可以.

UIKit使用UIResponder作為響應(yīng)對象,來響應(yīng)系統(tǒng)傳遞過來的事件并進(jìn)行處理。在 UIResponder中定義了處理各種事件和事件傳遞的接口。

UIApplication、UIViewController、UIView、和所有從UIView派生出來的UIKit類(包括UIWindow)都直接或間接地繼承自UIResponder類。

CALayer直接繼承 NSObject,并沒有相應(yīng)的處理事件的接口。

2. UIView是CALayer的delegate

3. UIView主要處理事件,CALayer負(fù)責(zé)繪制就更好

4. 每個(gè) UIView 內(nèi)部都有一個(gè) CALayer 在背后提供內(nèi)容的繪制和顯示,并且 UIView 的尺寸樣式都由內(nèi)部的 Layer 所提供。兩者都有樹狀層級結(jié)構(gòu),layer 內(nèi)部有 SubLayers,View 內(nèi)部有 SubViews.但是 Layer 比 View 多了個(gè)AnchorPoint

十、如何高性能的給UIImageView加個(gè)圓角?


使用Quartz2D直接繪制圖片

步驟:

a、創(chuàng)建目標(biāo)大小(cropWidth,cropHeight)的畫布。

b、使用UIImage的drawInRect方法進(jìn)行繪制的時(shí)候,指定rect為(-x,-y,width,height)。

c、從畫布中得到裁剪后的圖像。

- (UIImage*)cropImageWithRect:(CGRect)cropRect

{

//豆電雨

CGRect drawRect = CGRectMake(-cropRect.origin.x , -cropRect.origin.y, self.size.width * self.scale, self.size.height * self.scale);

UIGraphicsBeginImageContext(cropRect.size);

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextClearRect(context, CGRectMake(0, 0, cropRect.size.width, cropRect.size.height));

[self drawInRect:drawRect];

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return image;

}

@end

十一、使用drawRect有什么影響

drawRect方法依賴Core Graphics框架來進(jìn)行自定義的繪制,但這種方法主要的缺點(diǎn)就是它處理touch事件的方式:每次按鈕被點(diǎn)擊后,都會用setNeddsDisplay進(jìn)行強(qiáng)制重繪;而且不止一次,每次單點(diǎn)事件觸發(fā)兩次執(zhí)行。這樣的話從性能的角度來說,對CPU和內(nèi)存來說都是欠佳的。特別是如果在我們的界面上有多個(gè)這樣的UIButton實(shí)例。

這個(gè)方法的主要作用是根據(jù)傳入的 rect 來繪制圖像參見文檔. 這個(gè)方法的默認(rèn)實(shí)現(xiàn)沒有做任何事情, 我們可以在這個(gè)方法中使用 Core Graphics 和 UIKit 來繪制視圖的內(nèi)容.

這個(gè)方法的調(diào)用機(jī)制也是非常特別. 當(dāng)你調(diào)用 setNeedsDisplay 方法時(shí), UIKit 將會把當(dāng)前圖層標(biāo)記為 dirty, 但還是會顯示原來的內(nèi)容, 直到下一次的視圖渲染周期, 才會為標(biāo)記為 dirty 的圖層重新建立 Core Graphics 上下文, 然后將內(nèi)存中的數(shù)據(jù)恢復(fù)出來, 再使用 CGContextRef 進(jìn)行繪制.

十二、ASIHttpRequest或者SDWebImage里面給UIImageView加載圖片的邏輯是什么樣的?(把UIImageView放到UITableViewCell里面問更贊)

首先判斷url中的圖片是否在imagecache中存在,如果存在的話,直接顯示在tableviewcell上,如果不存在,就會去尋找沙盒中是否存在,如果沙盒中存在,那么會將沙盒中url信息緩存到imagecache中,接著顯示到tableviewcell,如果不存在,顯示占位圖,然后查看是否url圖片信息已經(jīng)存在operacache中進(jìn)行下載,如果不是,生成一個(gè)operacache下載進(jìn)程,下載完成之后,移除下載進(jìn)程,將圖片加入imagecache中,顯示在tableviewcell中。清晰的思路如圖:

SDWebImage

十三、麻煩你設(shè)計(jì)個(gè)簡單的圖片內(nèi)存緩存器(移除策略是一定要說的)

圖片的內(nèi)存緩存,可以考慮將圖片數(shù)據(jù)保存到一個(gè)數(shù)據(jù)模型中。所以在程序運(yùn)行時(shí)這個(gè)模型都存在內(nèi)存中。

移除策略:釋放數(shù)據(jù)模型對象

十四、講講你用Instrument優(yōu)化動畫性能的經(jīng)歷吧(別問我什么是Instrument)

優(yōu)化的話,基礎(chǔ)的的,reuse大家都知道,然后把opaque都設(shè)置為yes(有個(gè)選項(xiàng)可以顯示那些為false的UIView),然后把cornerRadius和shadow(主要是shadow)優(yōu)化,不要用代碼寫。然后不要在delegate方法里使用類似 tableview.cellForIndexpath 還是 indexpathForCell 類似的方法 。

要求高一點(diǎn),就盡可能緩存和預(yù)加載。而且不在delegate里減少需要計(jì)算的東西,和占內(nèi)存的東西。在后臺進(jìn)程先把layer的image繪制好,然后緩存起來要顯示的時(shí)候再加載。

十五、loadView是干嘛用的?

viewController的方法,會在viewDidLoad之前進(jìn)行調(diào)用。

很多人都會疑惑self.view,這個(gè)view道理是哪里來的,就是在這里。一般不需要去操作這個(gè)。但如果有特殊的需求,要求這個(gè)self.view是我們自己自定義的view時(shí)候就可以用這個(gè)方法,

MyView *myview = [[MyView alloc]init];

self.view = myview;

十六、viewWillLayoutSubView是什么

當(dāng)viewController的bounds又改變,調(diào)用這個(gè)方法來實(shí)現(xiàn)subview的位置??芍貙戇@個(gè)方法來實(shí)現(xiàn)父視圖變化subview跟著變化

十七、GCD里面有哪幾種Queue?你自己建立過串行queue嗎?背后的線程模型是什么樣的?

參見GCD詳解

十八、用過coredata或者sqlite嗎?讀寫是分線程的嗎?遇到過死鎖沒?咋解決的

最好是在同一個(gè)線程里面,比價(jià)方便。如果要分線程的話要注意:

1、只用一個(gè)NSPersistentStoreCoordinator

2、每個(gè)線程創(chuàng)建一個(gè)NSManagedObjectContext

3、不要傳遞NSManagedObject,傳objectID,通過fetch獲得。

4、先存后取,利用NSManagedObjectContext

-mergeChangesFromContextDidSaveNotification:

5、保護(hù)思路清晰。鏈接?

死鎖解決方法:鏈接

十九、http的post和get啥區(qū)別?(區(qū)別挺多的,麻煩多說點(diǎn))

原理的區(qū)別

一般我們在瀏覽器輸入一個(gè)網(wǎng)址訪問網(wǎng)站都是GET請求;再FORM表單中,可以通過設(shè)置Method指定提交方式為GET或者POST提交方式,默認(rèn)為GET提交方式。

HTTP定義了與服務(wù)器交互的不同方法,其中最基本的四種:GET,POST,PUT,DELETE,HEAD,其中GET和HEAD被稱為安全方法,因?yàn)槭褂肎ET和HEAD的HTTP請求不會產(chǎn)生什么動作。不會產(chǎn)生動作意味著GET和HEAD的HTTP請求不會在服務(wù)器上產(chǎn)生任何結(jié)果。但是安全方法并不是什么動作都不產(chǎn)生,這里的安全方法僅僅指不會修改信息。

根據(jù)HTTP規(guī)范,POST可能會修改服務(wù)器上的資源的請求。比如CSDN的博客,用戶提交一篇文章或者一個(gè)讀者提交評論是通過POST請求來實(shí)現(xiàn)的,因?yàn)樵偬峤晃恼禄蛘咴u論提交后資源(即某個(gè)頁面)不同了,或者說資源被修改了,這些便是“不安全方法”。

兩種請求方式的區(qū)別:

1、GET請求,請求的數(shù)據(jù)會附加在URL之后,以?分割URL和傳輸數(shù)據(jù),多個(gè)參數(shù)用&連接。URL的編碼格式采用的是ASCII編碼,而不是uniclde,即是說所有的非ASCII字符都要編碼之后再傳輸。

POST請求:POST請求會把請求的數(shù)據(jù)放置在HTTP請求包的包體中。上面的item=bandsaw就是實(shí)際的傳輸數(shù)據(jù)。

因此,GET請求的數(shù)據(jù)會暴露在地址欄中,而POST請求則不會。

2、傳輸數(shù)據(jù)的大小

在HTTP規(guī)范中,沒有對URL的長度和傳輸?shù)臄?shù)據(jù)大小進(jìn)行限制。但是在實(shí)際開發(fā)過程中,對于GET,特定的瀏覽器和服務(wù)器對URL的長度有限制。因此,在使用GET請求時(shí),傳輸數(shù)據(jù)會受到URL長度的限制。

對于POST,由于不是URL傳值,理論上是不會受限制的,但是實(shí)際上各個(gè)服務(wù)器會規(guī)定對POST提交數(shù)據(jù)大小進(jìn)行限制,Apache、IIS都有各自的配置。

3、安全性

POST的安全性比GET的高。這里的安全是指真正的安全,而不同于上面GET提到的安全方法中的安全,上面提到的安全僅僅是不修改服務(wù)器的數(shù)據(jù)。比如,在進(jìn)行登錄操作,通過GET請求,用戶名和密碼都會暴露再URL上,因?yàn)榈卿涰撁嬗锌赡鼙粸g覽器緩存以及其他人查看瀏覽器的歷史記錄的原因,此時(shí)的用戶名和密碼就很容易被他人拿到了。除此之外,GET請求提交的數(shù)據(jù)還可能會造成Cross-site

request frogery攻擊

4、HTTP中的GET,POST,SOAP協(xié)議都是在HTTP上運(yùn)行的

二十、什么是Binary search tree? search的時(shí)間復(fù)雜度是多少



最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容