如果你不知道你接下來需要補(bǔ)充學(xué)習(xí)哪一部分知識(shí)罚斗,做點(diǎn)面試題吧。
基礎(chǔ)部分
1宅楞、線程和進(jìn)程有什么區(qū)別
進(jìn)程是一個(gè)程序執(zhí)行的實(shí)例针姿,是資源分配的最小單位
線程是進(jìn)程中的一個(gè)實(shí)例,是操作系統(tǒng)可以識(shí)別的最小執(zhí)行和調(diào)度單位
那么厌衙,線程和進(jìn)程與堆距淫、棧之間的關(guān)系?
棧是線程獨(dú)有的婶希,保存其運(yùn)行狀態(tài)和局部自動(dòng)變量榕暇,棧空間是線程安全的喻杈,棧被自動(dòng)分配到進(jìn)程的內(nèi)存空間彤枢,棧內(nèi)存無需開發(fā)管理,系統(tǒng)自動(dòng)管理
堆在操作系統(tǒng)初始化進(jìn)程的時(shí)候分配筒饰,運(yùn)行過程可以要求更多額外的堆內(nèi)存缴啡,但是需要返回,不然呢就是內(nèi)存泄露
2龄砰、線程之間的通信
例如在多線程并發(fā)條件下盟猖,為了讓線程之間可以更方便的共同完成一個(gè)任務(wù),需要一些協(xié)調(diào)通信换棚,采取的通信方式就是 等待式镐、喚起。
也就是 wait() 和 notify()固蚤、 notifyAll()
3娘汞、幾個(gè)線程問題
1、串行隊(duì)列 不管是不是async 或者 sync 你要執(zhí)行 即使是async中等待 也得等著走完夕玩,因?yàn)榫鸵粋€(gè)隊(duì)列在進(jìn)行
3你弦、并行隊(duì)列惊豺,如果async sleep等待,其他的可以同步進(jìn)行 不需要等他禽作,但是如果是sync等待在隊(duì)列前面尸昧, 那還是乖乖等著。畢竟隊(duì)列同步的話順序執(zhí)行旷偿,即使是同步并行烹俗,只要是sync 同步 ,就在一個(gè)線程里跑 就是串行執(zhí)行的萍程,如果是異步就會(huì)開啟線程可以利用并行隊(duì)列并行執(zhí)行幢妄。
4、iOS 幾種鎖的性能問題
http://www.reibang.com/p/b1edc6b0937a
@synchronized 性能最差
自旋鎖最好茫负,dispatch_semaphore 信號(hào)較好蕉鸳,然后是NSLock性能較好
os_unfair_lock 互斥鎖 替換自旋鎖
蘋果通過這個(gè)處理了優(yōu)先級(jí)反轉(zhuǎn)的問題
**臨界區(qū):**指的是一塊對(duì)公共資源進(jìn)行訪問的代碼,并非一種機(jī)制或是算法忍法。
**自旋鎖:**是用于多線程同步的一種鎖潮尝,線程反復(fù)檢查鎖變量是否可用。由于線程在這一過程中保持執(zhí)行缔赠,因此是一種`忙等待`衍锚。一旦獲取了自旋鎖友题,線程會(huì)一直保持該鎖嗤堰,直至顯式釋放自旋鎖。 自旋鎖避免了進(jìn)程上下文的調(diào)度開銷度宦,因此對(duì)于線程只會(huì)阻塞很短時(shí)間的場(chǎng)合是有效的踢匣。
**互斥鎖(Mutex):**是一種用于多線程編程中,防止兩條線程同時(shí)對(duì)同一公共資源(比如全局變量)進(jìn)行讀寫的機(jī)制戈抄。該目的通過將代碼切片成一個(gè)一個(gè)的臨界區(qū)而達(dá)成。
**讀寫鎖:**是計(jì)算機(jī)程序的并發(fā)控制的一種同步機(jī)制,也稱“共享-互斥鎖”食磕、多讀者-單寫者鎖) 用于解決多線程對(duì)公共資源讀寫問題弦赖。讀操作可并發(fā)重入,寫操作是互斥的裸诽。 讀寫鎖通常用互斥鎖嫂用、條件變量、信號(hào)量實(shí)現(xiàn)丈冬。
**信號(hào)量(semaphore):**是一種更高級(jí)的同步機(jī)制嘱函,`互斥鎖`可以說是semaphore在僅取值0/1時(shí)的特例。`信號(hào)量`可以有更多的取值空間埂蕊,用來實(shí)現(xiàn)更加復(fù)雜的同步往弓,而不單單是線程間互斥疏唾。
**條件鎖:**就是條件變量,當(dāng)進(jìn)程的某些資源要求不滿足時(shí)就進(jìn)入休眠函似,也就是鎖住了槐脏。當(dāng)資源被分配到了,條件鎖打開撇寞,進(jìn)程繼續(xù)運(yùn)行准给。
4、iOS通過MachPort向特定線程發(fā)送通知
mach kernel屬于蘋果內(nèi)核重抖,RunLoop依靠它實(shí)現(xiàn)了休眠和喚醒而避免了CPU的空轉(zhuǎn): mach_port
Runloop是基于pthread進(jìn)行管理的露氮,pthread是基于c的跨平臺(tái)多線程操作底層API。它是mach thread的上層封裝
3钟沛、當(dāng)用一個(gè)不存在的key來查找兩個(gè)不同長度的字典畔规,那么哪個(gè)效率會(huì)高?
表面上看可能是一樣快恨统,因?yàn)樽值涞讓佣加昧斯1砣ǎ檎业臅r(shí)間復(fù)雜度為 O(1),(最差的時(shí)候是O(n))都是一樣的畜埋,但是可能會(huì)由于兩個(gè)哈希表的負(fù)載因子不同莫绣,倒是查找的時(shí)間也是不同的。
4悠鞍、什么是指針常量和常量指針
指針常量是 常量对室,指針修飾它,這個(gè)常量的值是一個(gè)指針 int a咖祭; int *const b = &a;
常量指針本質(zhì)是指針掩宜,常量修飾它 const int *p;
5、不借用第三個(gè)變量么翰,如何交換兩個(gè)變量的值牺汤?
算術(shù)運(yùn)算
int a,b;
a=10;b=12;
a=b-a; //a=2;b=12
b=b-a; //a=2;b=10
a=b+a; //a=12;b=10
位運(yùn)算 異或
int a=10,b=12; //a=1010^b=1100;
a=a^b; //a=0110^b=1100;
b=a^b; //a=0110^b=1010;
a=a^b; //a=1100=12;b=1010;
棧實(shí)現(xiàn)
int exchange(int x,int y)
{
stack S;
push(S,x);
push(S,y);
x=pop(S);
y=pop(S);
}
6、用遞歸算法求1到n的和
func add(n: Int) -> Int {
var sum = 0
if n > 0 {
sum = n + add(n: n - 1)
} else {
sum = 0
}
return sum
}
7浩嫌、100個(gè)數(shù)字檐迟,求最大值的時(shí)間復(fù)雜度
需要一輪遍歷 O(n)
那遞歸算法的時(shí)間復(fù)雜度是多少:nlogn
8、http 的 POST 和 GET 啥區(qū)別码耐?
GET請(qǐng)求的數(shù)據(jù)會(huì)附在URL之后
POST把提交的數(shù)據(jù)則放置在是HTTP包的包體中
GET請(qǐng)求URL受瀏覽器影響 所以有長度限制
POST沒有追迟,一般服務(wù)器會(huì)做POST數(shù)據(jù)長度的限制
POST的數(shù)據(jù)傳輸不是直接拼接URL 所以相對(duì)安全一些
9、http和https的區(qū)別伐坏,說一下http和https的請(qǐng)求過程怔匣?
http + ssl/tls = https
主要介紹一下,ssl的驗(yàn)證過程 保證安全和數(shù)據(jù)完整性
可以補(bǔ)充一下PKI證書體系的部分
10、如何用HTTP實(shí)現(xiàn)長連接每瞒?
web端:
Connection:keep-alive
服務(wù)器在閑置時(shí)候會(huì)向客戶端發(fā)生偵測(cè)包金闽,默認(rèn)閑置時(shí)間是2個(gè)小時(shí)
移動(dòng)端:
基于tcp的長連接,socket編程技術(shù)
12剿骨、聊下HTTP post的body體使用form-urlencoded和multipart/form-data的區(qū)別代芜。
application/x-www-form-urlencoded:窗體數(shù)據(jù)被編碼為名稱/值對(duì)。這是標(biāo)準(zhǔn)的編碼格式浓利。
multipart/form-data:窗體數(shù)據(jù)被編碼為一條消息挤庇,頁上的每個(gè)控件對(duì)應(yīng)消息中的一個(gè)部分
13、通信底層原理
OSI采用了分層的結(jié)構(gòu)化技術(shù)贷掖,共分七層:
物理層:為設(shè)備間的數(shù)據(jù)通信提供傳輸媒體和互連設(shè)備嫡秕,光纖、無線信道等等
數(shù)據(jù)鏈路層:為網(wǎng)絡(luò)層提供數(shù)據(jù)傳送服務(wù)的苹威,包括鏈路連接的建立昆咽、拆除和分離;對(duì)幀的收發(fā)順序控制
網(wǎng)絡(luò)層:數(shù)據(jù)傳送的單位是分組或者包牙甫,網(wǎng)絡(luò)層在給兩個(gè)不同地理位置的主機(jī)之間提供
傳輸層:定義了一些傳輸數(shù)據(jù)的協(xié)議和端口號(hào)掷酗,TCP, UDP;主要從下層接收的數(shù)據(jù)進(jìn)行分段和傳輸窟哺,到達(dá)目的地后再重組
會(huì)話層:通過傳輸層建立數(shù)據(jù)傳輸通道泻轰,主要在你的系統(tǒng)之間發(fā)起會(huì)話或者接受會(huì)話請(qǐng)求(IP、MAC且轨、主機(jī)名稱)
表示層:可確保一個(gè)系統(tǒng)的應(yīng)用層所發(fā)送的信息可以被另一個(gè)系統(tǒng)的應(yīng)用層讀取浮声,主要做的就是把應(yīng)用層提供的信息變換為能夠共同理解的形式,提供字符代碼殖告,數(shù)據(jù)格式阿蝶,控制信息格式,加密等的統(tǒng)一表示黄绩。
應(yīng)用層:為用戶的應(yīng)用程序提供網(wǎng)絡(luò)服務(wù)
TCP/IP 采用四層結(jié)構(gòu):
網(wǎng)絡(luò)接口層:硬件、幀頭幀尾的添加
網(wǎng)絡(luò)互聯(lián)層:確定目標(biāo)計(jì)算機(jī)的IP地址
傳輸層:TCP玷过,確定如何傳輸
應(yīng)用層:app
14爽丹、介紹一下XMPP?
XMPP是一種以XML為基礎(chǔ)的開放式實(shí)時(shí)通信協(xié)議辛蚊。
XMPP 是一種很類似于http協(xié)議的一種數(shù)據(jù)傳輸協(xié)議粤蝎,它的過程就如同“解包裝–〉包裝”的過程,用戶只需要明白它接受的類型袋马,并理解它返回的類型初澎,就可以很好的利用xmpp來進(jìn)行數(shù)據(jù)通訊。基于可擴(kuò)展標(biāo)記語言(XML)的協(xié)議
XMPP基本結(jié)構(gòu):客戶端 服務(wù)器 網(wǎng)關(guān)
通信能夠在這三者的任意兩個(gè)之間雙向發(fā)生碑宴。服務(wù)器同時(shí)承擔(dān)了客戶端信息記錄软啼,連接管理和信息的路由功能。網(wǎng)關(guān)承擔(dān)著與異構(gòu)即時(shí)通信系統(tǒng)的互聯(lián)互通延柠,異構(gòu)系統(tǒng)可以包括SMS(短信)祸挪,MSN,ICQ等贞间』咛酰基本的網(wǎng)絡(luò)形式是單客戶端通過TCP/IP連接到單服務(wù)器,然后在之上傳輸XML增热。
XMPP核心協(xié)議通信的基本模式就是先建立一個(gè)stream整以,然后協(xié)商一堆安全之類的東西,中間通信過程就是客戶端發(fā)送XML Stanza峻仇,一個(gè)接一個(gè)的悄蕾。服務(wù)器根據(jù)客戶端發(fā)送的信息以及程序的邏輯,發(fā)送XML Stanza給客戶端础浮。但是這個(gè)過程并不是一問一答的帆调,任何時(shí)候都有可能從一方發(fā)信給另外一方。通信的最后階段是關(guān)閉流豆同,關(guān)閉TCP/IP連接番刊。
客戶端1 <--> XMPP服務(wù)器 <--> 客戶端2
兩個(gè)客戶端可以分別和服務(wù)器通信,但是客戶端之間的通信必須經(jīng)過服務(wù)器
用于一些即時(shí)通信
15影锈、ssl / tls證書 作用
保障通信雙方的可靠性芹务,通信的安全和數(shù)據(jù)的完整性
https和ssl在握手方向有什么區(qū)別?
一個(gè)是連接握手鸭廷,一個(gè)是安全校驗(yàn)握手枣抱,描述一下兩者握手過程
具體原理見參考中的 網(wǎng)絡(luò)知識(shí)整理。
16辆床、socket連接和 http 連接區(qū)別
Http是基于Tcp的佳晶,而Socket是一套編程接口讓我們更方便的使用Tcp/Ip協(xié)議;
Http是應(yīng)用層協(xié)議讼载,在Tcp/Udp上一層轿秧。
1、Http是基于"請(qǐng)求-響應(yīng)"的咨堤,服務(wù)器不能主動(dòng)向客戶端推送數(shù)據(jù)菇篡,只能借助客戶端請(qǐng)求到后向客戶端推送數(shù)據(jù),而Sokcet雙方隨時(shí)可以互發(fā)數(shù)據(jù)一喘;
2驱还、Http不是持久連接的,Socket用Tcp是持久連接;
3议蟆、Http基于Tcp闷沥,Socket可以基于Tcp/Udp;
4咪鲜、Http連接是通過Socket實(shí)現(xiàn)的狐赡;
5、Http連接后發(fā)送的數(shù)據(jù)必須滿足Http協(xié)議規(guī)定的格式:請(qǐng)求頭疟丙、請(qǐng)求頭和請(qǐng)求體颖侄,而Socket連接后發(fā)送的數(shù)據(jù)沒有格式要求。
Socket的實(shí)現(xiàn)原理及 Socket之間是如何通信的
網(wǎng)絡(luò)上的兩個(gè)程序通過一個(gè)雙向的通信連接實(shí)現(xiàn)數(shù)據(jù)的交換享郊,這個(gè)連接的一端稱為一個(gè)socket览祖。
建立網(wǎng)絡(luò)通信連接至少要一對(duì)端口號(hào)(socket)。
socket本質(zhì)是編程接口(API)炊琉,對(duì)TCP/IP的封裝展蒂,TCP/IP也要提供可供程序員做網(wǎng)絡(luò)開發(fā)所用的接口,這就是Socket編程接口苔咪;
HTTP是轎車锰悼,提供了封裝或者顯示數(shù)據(jù)的具體形式;Socket是發(fā)動(dòng)機(jī)团赏,提供了網(wǎng)絡(luò)通信的能力箕般。
socket分為客戶端和服務(wù)端,客戶端發(fā)送連接請(qǐng)求舔清,服務(wù)端等待連接請(qǐng)求
當(dāng)服務(wù)端socket監(jiān)聽到客戶端socket的請(qǐng)求時(shí)丝里,就響應(yīng)客戶端套接字的請(qǐng)求,建立一個(gè)新的線程体谒,把服務(wù)端套接字描述發(fā)送給客戶端杯聚,一旦客戶端確認(rèn)了此描述,雙方正式建立連接抒痒,而服務(wù)端socket繼續(xù)處于監(jiān)聽狀態(tài)幌绍,等待其他連接請(qǐng)求
17、說一下HTTP協(xié)議以及經(jīng)常使用的code碼的含義评汰。
一些常見的狀態(tài)代碼為:
200 - 服務(wù)器成功返回網(wǎng)頁
300 - 重定向之類
404 - 請(qǐng)求的網(wǎng)頁不存在
503 - 服務(wù)器暫時(shí)不可用
18纷捞、網(wǎng)絡(luò)擁塞控制、tcp的慢啟動(dòng)
不要一開始就發(fā)送大量的數(shù)據(jù)被去,先探測(cè)一下網(wǎng)絡(luò)的擁塞程度,也就是說由小到大逐漸增加擁塞窗口的大小奖唯。
簡(jiǎn)單來說 擁塞控制就是防止過多的數(shù)據(jù)注入網(wǎng)絡(luò)中惨缆,這樣可以使網(wǎng)絡(luò)中的路由器或鏈路不致過載。
原理:
請(qǐng)求發(fā)送,每次按窗口數(shù)發(fā)送數(shù)據(jù)坯墨,收到一個(gè)確認(rèn)就把窗口值加一寂汇,逐漸遞增,這就是慢開始算法
當(dāng)網(wǎng)絡(luò)擁塞捣染,窗口重新回 1 最大慢開始門限變?yōu)槌霈F(xiàn)問題的網(wǎng)絡(luò)擁塞窗口值的一半 這就是擁塞避免算法
然后再次循環(huán)骄瓣。
19、TCP 三次握手耍攘、四次揮手榕栏,為什么 斷開連接是4次揮手呢
因?yàn)門CP連接的時(shí)候,最后一次握手表示收到服務(wù)器確認(rèn)的請(qǐng)求可以攜帶需要發(fā)給服務(wù)器的數(shù)據(jù)蕾各,三次是最短可能
四次揮手是確卑谴牛客戶端 沒有消息要發(fā)給服務(wù)端,服務(wù)端也沒有消息要發(fā)給客戶端了式曲,也可以不用四次妨托,但是就會(huì)增加空等待的資源浪費(fèi)
20、事件響應(yīng)
蘋果注冊(cè)了一個(gè) Source1 (基于 mach port 的) 用來接收系統(tǒng)事件吝羞,其回調(diào)函數(shù)為 __IOHIDEventSystemClientQueueCallback()兰伤。
當(dāng)一個(gè)硬件事件(觸摸/鎖屏/搖晃等)發(fā)生后,首先由 IOKit.framework 生成一個(gè) IOHIDEvent 事件并由 SpringBoard 接收钧排。SpringBoard 只接收按鍵(鎖屏/靜音等)敦腔,觸摸,加速卖氨,接近傳感器等幾種 Event会烙,隨后用 mach port 轉(zhuǎn)發(fā)給需要的App進(jìn)程。
觸摸事件其實(shí)是Source1接收系統(tǒng)事件后在回調(diào) __IOHIDEventSystemClientQueueCallback()內(nèi)觸發(fā)的 Source0筒捺,Source0 再觸發(fā)的 _UIApplicationHandleEventQueue()柏腻。source0一定是要喚醒runloop及時(shí)響應(yīng)并執(zhí)行的,如果runloop此時(shí)在休眠等待系統(tǒng)的 mach_msg事件系吭,那么就會(huì)通過source1來喚醒runloop執(zhí)行五嫂。
_UIApplicationHandleEventQueue() 會(huì)把 IOHIDEvent 處理并包裝成 UIEvent 進(jìn)行處理或分發(fā),其中包括識(shí)別 UIGesture/處理屏幕旋轉(zhuǎn)/發(fā)送給 UIWindow 等肯尺。
21沃缘、對(duì)象的創(chuàng)建
Person *p = [Person alloc];
Person *p1 = [p init];
Person *p2 = [p init];
NSLog(@"p ==> %@", p);
NSLog(@"p1 ==> %@", p1);
NSLog(@"p2 ==> %@", p2);
輸出地址都是一樣的
init 僅僅是把 alloc 創(chuàng)建的對(duì)象返回,
new 相當(dāng)于 alloc + init
alloc 調(diào)用calloc 分配內(nèi)存则吟,接著就是 initInstanceIsa(cls, hasCxxDtor) 初始化isa
擴(kuò)展:
一個(gè)NSObject對(duì)象占用多少內(nèi)存槐臀??氓仲?水慨?
new一個(gè)NSObject對(duì)象解析出來其實(shí)就是一個(gè)結(jié)構(gòu)體,里面有個(gè)isa指針
struct NSObject_IMP {
Class isa;
};
isa 指針在64位架構(gòu)占用8個(gè)字節(jié)
一個(gè)指針占用多少個(gè)字節(jié)得糜??晰洒?
64位計(jì)算器 8個(gè)字節(jié)朝抖,32位計(jì)算器 4個(gè)字節(jié),地址就是指針谍珊,指針指向地址治宣,而地址是內(nèi)存單元的編號(hào),一個(gè)指針占用幾個(gè)字節(jié)砌滞,等于是一個(gè)地址的內(nèi)存單元編號(hào)有多長侮邀。
4*8 = 32
那一個(gè)對(duì)象占的就是 8 字節(jié)
22、Tagged Pointer
NSString 相關(guān)類說明表格
類名 | 存儲(chǔ)區(qū)域 | 初始化的引用計(jì)數(shù)(retainCount) | 作用描述 |
---|---|---|---|
NSString | 堆區(qū) | 1 | 開發(fā)者常用的不可變字符串類布持,編譯期間會(huì)轉(zhuǎn)換到其他類型 |
NSMutableString | 堆區(qū) | 1 | 開發(fā)者常用的可變字符串類豌拙,編譯期間會(huì)轉(zhuǎn)換到其他類型 |
__NSCFString | 堆區(qū) | 1 | 可變字符串 NSMutableString 類,編譯期間會(huì)轉(zhuǎn)換到該類型 |
__NSCFConstantString | 堆區(qū) | 2^64-1 | 不可變字符串 NSString 類题暖,編譯期間會(huì)轉(zhuǎn)換到該類型 |
NSTaggedPointerString | 棧區(qū) | 2^64-1 | Tagged Pointer對(duì)象按傅,并不是真的對(duì)象 |
http://www.reibang.com/p/dcbf48a733f9
23、isa是什么
isa
成員變量在64位
CPU架構(gòu)下是8字節(jié)胧卤,且排在objc_class
結(jié)構(gòu)體的前8字節(jié)唯绍。
從objc_object
的結(jié)構(gòu)可以說明,當(dāng)系統(tǒng)為一個(gè)對(duì)象分配好內(nèi)存枝誊,并初始化實(shí)例變量后况芒,在這些對(duì)象的實(shí)例變量的結(jié)構(gòu)體中的第一個(gè)就是isa
。
isa
將對(duì)象和類關(guān)聯(lián)起來叶撒,起到了中間橋梁的作用绝骚。
1、isa是isa_t結(jié)構(gòu)祠够,采用 聯(lián)合體+位域 的搭配來設(shè)計(jì):在不同的位上顯示不同的內(nèi)容压汪,以此來節(jié)省儲(chǔ)存空間,進(jìn)而優(yōu)化內(nèi)存古瓤。
2止剖、isa包含了cls和bits兩個(gè)成員變量,這兩個(gè)成員變量在64位CPU架構(gòu)下的長度都是8字節(jié)落君,所以isa在64位CPU架構(gòu)下的長度也是8字節(jié)穿香。
3、isa的位域上存儲(chǔ)了一些對(duì)象與類的信息绎速,并將對(duì)象與類關(guān)聯(lián)起來皮获,起到中間橋梁的作用。
4纹冤、isa指向:
對(duì)象的isa指針 指向 對(duì)象的所屬類(如person對(duì)象的isa指向Person類)
類的isa指針 指向 類的元類(如Person類的isa指向Person元類)
元類的isa指針 指向 根元類(如Person元類的isa指向NSObject元類)
根元類的isa指針 指向自身(是個(gè)圓圈)
元類的繼承關(guān)系向上傳遞(如Teacher元類 繼承自 Person元類)
24魔市、方法緩存原理
1主届、cache_t 能緩存調(diào)用過的方法
2赵哲、cache_t 的三個(gè)成員變量
_buckets : struct bucket_t * , 也就是指針數(shù)組待德,表示一系列的哈希桶(已調(diào)用的方法的 SEL 和 IMP 就緩存在這),一個(gè)桶可以存一個(gè)方法枫夺。
_mask : 側(cè)面反映哈希桶的總數(shù)
_occupied : 代表當(dāng)前已經(jīng)緩存的方法數(shù)
3将宪、當(dāng)緩存的方法達(dá)到臨界點(diǎn)(桶總數(shù)的3/4)時(shí),下次再緩存新的方法時(shí)橡庞,首先會(huì)丟棄舊的桶较坛,同時(shí)開辟新的內(nèi)存,也就是擴(kuò)容(擴(kuò)容后就全是新桶扒最,每個(gè)方法都需要重新緩存)丑勤,_occupied此時(shí)為1。
4吧趣、當(dāng)多個(gè)線程同時(shí)調(diào)用一個(gè)方法時(shí)
多線程讀緩存 : 讀緩存由匯編實(shí)現(xiàn)法竞,無鎖且高效,優(yōu)于并沒有改變 _buckets 和 _mask, 所以并無安全隱患
多線程寫緩存 : OC 用一個(gè)全局的互斥鎖來保證不會(huì)出現(xiàn)寫兩次緩存的情況
多線程讀寫緩存 : OC使用了ldp匯編指令强挫、編譯內(nèi)存屏障技術(shù)岔霸、內(nèi)存垃圾回收技術(shù)等多種手段來解決多線程讀寫的無鎖處理方案,既保證了安全俯渤,又提升了系統(tǒng)的性能呆细。
25、block原理
1八匠、block要如何hook絮爷?
如果想要Hook住系統(tǒng)的所有Block調(diào)用,需要解決如下幾個(gè)問題:
a. 如何在運(yùn)行時(shí)將所有的Block的invoke函數(shù)替換為一個(gè)統(tǒng)一的Hook函數(shù)梨树。
b. 這個(gè)統(tǒng)一的Hook函數(shù)如何調(diào)用原始Block的invoke函數(shù)坑夯。
c. 如何構(gòu)建這個(gè)統(tǒng)一的Hook函數(shù)。
fishhook __Block_copy
fishhook原理
將指向系統(tǒng)方法(外部函數(shù))的指針重新進(jìn)行綁定指向內(nèi)部函數(shù)/自定義 C 函數(shù)劝萤。
將內(nèi)部函數(shù)的指針在動(dòng)態(tài)鏈接時(shí)指向系統(tǒng)方法的地址渊涝。
這樣就把系統(tǒng)方法與自己定義的方法進(jìn)行了交換,達(dá)到 HOOK 系統(tǒng) C 函數(shù)(共享庫中的)的目的床嫌。
ASLR 引入之前 方法地址是固定的跨释,很容易被攻擊串改
2、block的本質(zhì)
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _val, __Block_byref_valPtr_0 *_valPtr, int flags=0) : a(_a) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
關(guān)鍵結(jié)構(gòu)體命名規(guī)則:前部分是__所在內(nèi)部方法名_block_impl_方法體內(nèi)第幾個(gè)block
如:__main_block_impl_0
main方法內(nèi)的第1個(gè)block
類似第二個(gè)命名為__main_block_impl_1
__main_block_impl_0 的函數(shù)地址賦給 block厌处,就是我們平時(shí)的block定義
__block_impl 結(jié)構(gòu)體:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtrl; 指向我們寫在block里面內(nèi)容函數(shù)的地址
}
block的執(zhí)行是通過 impl 找到 FuncPtrl 然后職級(jí)調(diào)用函數(shù)地址
3鳖谈、block的捕獲列表:
局部變量、static變量; 全局變量 不捕獲
__main_block_impl_0 中的參數(shù) _val阔涉,_valPtr 可以看到val已經(jīng)被捕獲缆娃,valPtr是以指針的方式捕獲進(jìn)去的
__block 修飾符
添加 __block 修飾符后捷绒,源碼如下, val變成一個(gè)結(jié)構(gòu)體了
struct __block_byref_val_0{
void *__isa;
__block_byref_val_0 *__forwarding;
int _flags;
int __size;
int val;
}
__forwarding用于指向真實(shí)變量地址
4贯要、OC Block 和 Swift閉包的區(qū)別
1暖侨、OC的block在編譯的時(shí)候就執(zhí)行了拷貝,swift 的閉包要到執(zhí)行的時(shí)候才會(huì)發(fā)生拷貝崇渗,swift可以讓他提前捕獲
var i = 1
let closure = {
[i] in
print("closure \(i)")
}
加上[i] 就行
5字逗、block的isa指針指向哪里呢
指向_NSConcreteStackBlock 類對(duì)象
block最終都是繼承自NSBlock類型
6、三類block
在內(nèi)存角度來看宅广,block分為 全局 葫掉、棧 和 堆 三種類型,
有強(qiáng)引用的block就屬于堆內(nèi)存block, __NSMallocBlock__
只用到外部局部變量跟狱、成員屬性變量俭厚、沒有強(qiáng)指針引用的block屬于棧block __NSStackBlock__
只引用全局變量或靜態(tài)變量的block,生命周期和程序生命周期一樣的block就是全局block __NSGlobalBlock__
26驶臊、KVO本質(zhì)(簡(jiǎn))
person在唄添加KVO監(jiān)聽之后發(fā)生了什么:
person -> NSKVONotifying_Person
isa指針會(huì)指向 person 子類 NSKVONotifying_Person
NSKVONotifying_Person 里的get挪挤、set方法進(jìn)行了重寫,class方法也重寫了
KVO的本質(zhì)是什么:
當(dāng)一個(gè)對(duì)象使用了KVO監(jiān)聽资铡,iOS系統(tǒng)會(huì)修改這個(gè)對(duì)象的isa指針电禀,改為指向一個(gè)全新的通過Runtime動(dòng)態(tài)創(chuàng)建的子類,子類擁有自己的set方法實(shí)現(xiàn)笤休,set方法實(shí)現(xiàn)內(nèi)部會(huì)順序調(diào)用willChangeValueForKey方法蝉揍、原來的setter方法實(shí)現(xiàn)荣暮、didChangeValueForKey方法,而didChangeValueForKey方法內(nèi)部又會(huì)調(diào)用監(jiān)聽器的observeValueForKeyPath:ofObject:change:context:監(jiān)聽方法。
如何手動(dòng)觸發(fā)KVO
答. 被監(jiān)聽的屬性的值被修改時(shí)狠半,就會(huì)自動(dòng)觸發(fā)KVO幅骄。如果想要手動(dòng)觸發(fā)KVO遣蚀,則需要我們自己調(diào)用willChangeValueForKey和didChangeValueForKey方法即可在不改變屬性值的情況下手動(dòng)觸發(fā)KVO色乾,并且這兩個(gè)方法缺一不可。
27窍奋、iOS常見崩潰
1荐健、野指針
2、訪問不存在的方法
3琳袄、數(shù)組越界江场、數(shù)組插入nil,字典key為nil
4窖逗、觀察者未釋放
5址否、masonry布局在add之前調(diào)用
6、死鎖超時(shí)
7碎紊、Timer repeat的時(shí)候釋放timer 回調(diào)找不到對(duì)象
8佑附、非主線程刷新UI
9樊诺、字符串操作crash
28、weak的具體實(shí)現(xiàn)
weak修飾對(duì)象不增加引用計(jì)數(shù)音同,系統(tǒng)通過一個(gè)hash表來實(shí)現(xiàn)對(duì)象的弱引用
__weak obj1 = obj;
編譯成
objc_initWeak(&obj1,obj)
objc_initWeak(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
location是weak指針的地址词爬,newObj是被指向的對(duì)象的地址。
weak表
struct weak_table_t {
// 保存了所有指向指定對(duì)象的 weak 指針
weak_entry_t *weak_entries;
// 存儲(chǔ)空間
size_t num_entries;
// 參與判斷引用計(jì)數(shù)輔助量
uintptr_t mask;
// hash key 最大偏移值
uintptr_t max_hash_displacement;
};
全局hash表瘟斜,使用不定類型對(duì)象地址作為key缸夹,用weak_entry_t類型結(jié)構(gòu)體對(duì)象作為value。它也是個(gè)結(jié)構(gòu)體螺句,它負(fù)責(zé)維護(hù)和存儲(chǔ)指向一個(gè)對(duì)象的所有弱引用hash表
整體流程就是:
1、objc_initWeak
2橡类、storeWeak
- oldTable newTable 獲取新舊引用散列蛇尚,調(diào)用sideTables構(gòu)造
- lockTwo 加鎖,解決選擇競(jìng)爭(zhēng)
- weak_unregister 解除舊對(duì)象與弱引用表關(guān)聯(lián)綁定
- weak_register - 構(gòu)建新的引用計(jì)數(shù)表顾画,返回一個(gè) newObj
weak指針的銷毀
dealloc調(diào)用_objc_rootDealloc調(diào)用objc_object::rootDealloc()
objc_object::rootDealloc()調(diào)用object_dispose
object_dispose調(diào)用objc_destructInstance
objc_destructInstance調(diào)用obj->clearDeallocating()
clearDeallocating里面有分支sidetable_clearDeallocating()和clearDeallocating_slow()取劫,但是這2個(gè)函數(shù)里面其實(shí)都是調(diào)用了weak_clear_no_lock。
所以我們來看看weak_clear_no_lock干了啥
從weak表中獲取廢棄對(duì)象的地址為鍵值的記錄
將包含在記錄中的所有附有 weak修飾符變量的地址研侣,賦值為nil
將weak表中該記錄刪除
從引用計(jì)數(shù)表中刪除廢棄對(duì)象的地址為鍵值的記錄
總結(jié)
runtime維護(hù)了一個(gè)weak表谱邪,用于存儲(chǔ)指向某個(gè)對(duì)象的所有weak指針。weak表其實(shí)是一個(gè)hash(哈希)表庶诡,key是所指對(duì)象的地址惦银,Value是weak指針的地址(這個(gè)地址的值是所指對(duì)象指針的地址)數(shù)組。
稍微詳細(xì)版
初始化時(shí):runtime會(huì)調(diào)用objc_initWeak函數(shù)末誓,初始化一個(gè)新的weak指針指向?qū)ο蟮牡刂贰?添加引用時(shí):objc_initWeak函數(shù)會(huì)調(diào)用 storeWeak() 函數(shù)扯俱, storeWeak() 的作用是更新指針指向,創(chuàng)建對(duì)應(yīng)的弱引用表喇澡。
釋放時(shí),調(diào)用clearDeallocating函數(shù)迅栅。clearDeallocating函數(shù)首先根據(jù)對(duì)象地址獲取所有weak指針地址的數(shù)組,然后遍歷這個(gè)數(shù)組把其中的數(shù)據(jù)設(shè)為nil晴玖,最后把這個(gè)entry從weak表中刪除读存,最后清理對(duì)象的記錄。
29呕屎、啟動(dòng)優(yōu)化的過程
pre-main
初始化空間让簿,加載鏡像,載入動(dòng)態(tài)庫鏈接器榨惰,鏈接動(dòng)態(tài)庫拜英,objcsetup,callloadmethod琅催,
動(dòng)態(tài)庫越多居凶,啟動(dòng)越慢虫给,+load方法多也會(huì)影響
優(yōu)化方向:靜態(tài)庫,cocoapods :linkage => :static指令 將所有的動(dòng)態(tài)庫轉(zhuǎn)為靜態(tài)庫侠碧。 要注意資源獲取的方式問題抹估。
合并動(dòng)態(tài)庫:cocoapods-pod-merge 支持合并動(dòng)態(tài)庫,要注意import方式會(huì)改變弄兜,作用域前綴要加上
main
main函數(shù)以后就是盡量減少第一個(gè)頁面展示前的工作药蜻,有些組件庫自己的工作可以派發(fā)到庫自己處理,或者異步處理
啟動(dòng)項(xiàng)熱拔插
30替饿、HandyJSON原理
Objective-C通過class_copyPropertyList方法加上KVC機(jī)制 很容易實(shí)現(xiàn)JSON 反序列化
Swift的反射 Mirror是只讀的
在內(nèi)存上為實(shí)例的屬性賦值
具體實(shí)現(xiàn):
Swift中语泽,一個(gè)類實(shí)例的內(nèi)存布局是有規(guī)律的:
1、32位機(jī)器上视卢,類前面有4+8個(gè)字節(jié)存儲(chǔ)meta信息踱卵,64位機(jī)器上,有8+8個(gè)字節(jié)据过;
2惋砂、內(nèi)存中,字段從前往后有序排列绳锅;
3西饵、如果該類繼承自某一個(gè)類,那么父類的字段在前鳞芙;
4眷柔、Optional會(huì)增加一個(gè)字節(jié)來存儲(chǔ).None/.Some信息;
5积蜻、每個(gè)字段需要考慮內(nèi)存對(duì)齊闯割;
對(duì)一個(gè)實(shí)例:
1、獲取它的起始指針竿拆,移動(dòng)到有效起點(diǎn)宙拉;
2、通過Mirror獲取每一個(gè)字段的字段名和字段類型丙笋,計(jì)算Model的每個(gè)屬性字段占位大行怀骸;
3御板、根據(jù)字段名在JSON中取值锥忿,轉(zhuǎn)換為和字段一樣的類型,通過指針寫入怠肋;
4敬鬓、根據(jù)本字段類型的占位大小和下一個(gè)字段類型計(jì)算下一個(gè)字段的對(duì)齊起點(diǎn);
5、移動(dòng)指針钉答,繼續(xù)處理础芍;
31、分類的方法覆蓋
類似ViewDidLoad寫在分類里面也會(huì)覆蓋主類
load方法不會(huì)覆蓋数尿,如果做得方法替換針對(duì)同一個(gè)對(duì)象 會(huì)有影響
32仑性、通知
1、通知是分線程的右蹦,如果在子線程發(fā)出的通知诊杆,接受者接到之后的方法執(zhí)行會(huì)在相應(yīng)的線程完成
2、同時(shí)注冊(cè)多個(gè)監(jiān)聽者何陆,要注意順序晨汹,可以同時(shí)給一個(gè)對(duì)象加多個(gè)接受者
3、要監(jiān)聽所有通知甲献,那么 name設(shè)置為nil , 包括系統(tǒng)通知在內(nèi)都會(huì)監(jiān)聽
4宰缤、iOS 9 以后不移除對(duì)于的監(jiān)聽者也沒事
5、hook底層objc_msgSend 監(jiān)聽每個(gè)方法的時(shí)間長度
監(jiān)聽每個(gè)方法的時(shí)間長度
16晃洒、app如何接收到觸摸事件的
1. 首先,手機(jī)中處理`觸摸事件`的是硬件系統(tǒng)進(jìn)程 朦乏,當(dāng)硬件系統(tǒng)進(jìn)程識(shí)別到觸摸事件后球及,會(huì)將這個(gè)事件`進(jìn)行封裝`,并通過`machPort`,將封裝的事件發(fā)送給當(dāng)前活躍的APP進(jìn)程呻疹。
2. 由于APP的主線程中runloop注冊(cè)了這個(gè)machPort端口吃引,就是用于接收處理這個(gè)事件的,所以這里APP收到這個(gè)消息后刽锤,開始尋找`響應(yīng)鏈`镊尺。
3. 尋找到響應(yīng)鏈后,開始`分發(fā)事件`并思,它會(huì)優(yōu)先發(fā)送給`手勢(shì)集合`庐氮,來過濾這個(gè)事件,一旦手勢(shì)集合中其中一個(gè)手勢(shì)識(shí)別了這個(gè)事件宋彼,那么這個(gè)事件將不會(huì)發(fā)送給響應(yīng)鏈對(duì)象弄砍。
4. 手勢(shì)沒有識(shí)別到這個(gè)事件,事件將會(huì)發(fā)送給響應(yīng)鏈對(duì)象`UIResponser`输涕。
17音婶、利用 runloop
解釋一下頁面的渲染的過程?
1莱坎、當(dāng)我們調(diào)用 [UIView setNeedsDisplay]
時(shí)衣式,這時(shí)會(huì)調(diào)用當(dāng)前 View.layer
的 [view.layer setNeedsDisplay]
方法。
這等于給當(dāng)前的 layer
打上了一個(gè)臟標(biāo)記,而此時(shí)并沒有直接進(jìn)行繪制工作碴卧。而是會(huì)到當(dāng)前的 Runloop
即將休眠弱卡,也就是 beforeWaiting
時(shí)才會(huì)進(jìn)行繪制工作。
緊接著會(huì)調(diào)用 [CALayer display]
螟深,進(jìn)入到真正繪制的工作谐宙。CALayer
層會(huì)判斷自己的 delegate
有沒有實(shí)現(xiàn)異步繪制的代理方法 displayer:
,這個(gè)代理方法是異步繪制的入口界弧,如果沒有實(shí)現(xiàn)這個(gè)方法凡蜻,那么會(huì)繼續(xù)進(jìn)行系統(tǒng)繪制的流程,然后繪制結(jié)束垢箕。
CALayer
內(nèi)部會(huì)創(chuàng)建一個(gè) Backing Store
划栓,用來獲取圖形上下文。接下來會(huì)判斷這個(gè) layer
是否有 delegate条获。
如果有的話忠荞,會(huì)調(diào)用 [layer.delegate drawLayer:inContext:]
,并且會(huì)返回給我們 [UIView DrawRect:]
的回調(diào)帅掘,讓我們?cè)谙到y(tǒng)繪制的基礎(chǔ)之上再做一些事情委煤。
如果沒有 delegate
,那么會(huì)調(diào)用 [CALayer drawInContext:]
修档。
以上兩個(gè)分支碧绞,最終 CALayer
都會(huì)將位圖提交到 Backing Store
,最后提交給 GPU
吱窝。
至此繪制的過程結(jié)束讥邻。
2、UI在什么時(shí)候渲染院峡?
因?yàn)閁I不是立即渲染的兴使,CA在runloop中注冊(cè)了一個(gè)即將進(jìn)入休眠的observer,在休眠之前對(duì)已提交的請(qǐng)求進(jìn)行集中渲染照激。
3发魄、卡頓問題,一次runloop繪制任務(wù)太多仍會(huì)存在卡頓問題实抡,放到下個(gè)Runloop欠母,以減少單次runloop繪制任務(wù)。(其實(shí)重用UI吆寨、子線程處理耗時(shí)的非UI操作基本上能處理大多數(shù)常見卡頓)赏淌。runloop中將一個(gè)任務(wù)放到第二次runloop中執(zhí)行。最簡(jiǎn)單使用[self performSelector:@selector(xxxxx) withObject:nil afterDelay:0]啄清,
而GCD的話六水,是不一定的俺孙。
4、Runloop支持線程喚醒的事件類型
-
基于端口的事件
如觸摸事件掷贾,系統(tǒng)將觸摸事件封裝睛榄,并且通過進(jìn)程之間端口通訊傳遞到我們的進(jìn)程。[[NSRunLoop currentRunLoop] addPort:port forMode:NSRunLoopCommonModes];
-
自定義事件
[self performSelector:@selector(taskDo) onThread:thread withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
-
基于時(shí)間的定時(shí)事件
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
5想帅、異步繪制的ASDisplayKit 為啥要用set去管理场靴,無需查找,事務(wù)去重 事務(wù)也無順序
6港准、系統(tǒng)通知也是無序的
18旨剥、幾個(gè)數(shù)據(jù)結(jié)構(gòu)問題
1、NSArray與NSSet的區(qū)別浅缸?
- NSArray內(nèi)存中存儲(chǔ)地址連續(xù)轨帜,而NSSet不連續(xù)
- NSSet效率高,內(nèi)部使用hash查找衩椒;NSArray查找需要遍歷
- NSSet通過anyObject訪問元素蚌父,NSArray通過下標(biāo)訪問
2、NSHashTable與NSMapTable毛萌?
- NSHashTable是NSSet的通用版本苟弛,對(duì)元素弱引用,可變類型阁将;可以在訪問成員時(shí)copy
- NSMapTable是NSDictionary的通用版本嗡午,對(duì)元素弱引用,可變類型冀痕;可以在訪問成員時(shí)copy
(注:NSHashTable與NSSet的區(qū)別:NSHashTable可以通過option設(shè)置元素弱引用/copyin,只有可變類型狸演。但是添加對(duì)象的時(shí)候NSHashTable耗費(fèi)時(shí)間是NSSet的兩倍言蛇。
NSMapTable與NSDictionary的區(qū)別:同上)
OC 部分
extern的作用
告訴編譯器,這個(gè)全局變量在本文件找不到就去其他文件去找宵距。如有必要需要使用#import "x.h"這樣編譯器才知道到哪里去找腊尚。使用extern前要保證對(duì)應(yīng)變量被編譯過,想要訪問全局變量可以使用extern關(guān)鍵字(全局變量定義不能有static修飾)满哪。
比如 A文件中 我聲明的全局變量 NSInteger age = 10; 但是屬性也不能直接獲取婿斥。 如下在B文件中可以獲取到 :
extern NSInteger age;
age ++;
NSLog(@"%d",age); // 11
如果不想讓age被找到,聲明為static
const的作用
常量定義哨鸭,修飾一個(gè)常量
int a = 1;
int b = 2;
int const *p = &a
// 如果const修飾的是*p民宿,那么*p的值是不能改變的,也就是p中存放的a的地址中的值無法改變像鸡,但是p的值是可以改變的(也就是p此時(shí)可以改變指向)
p = &b;
printf("---");
printf("%p",&b);
printf("---");
printf("%p",p);
printf("---");
printf("%d",*p);
//輸出 ---0x7ffeea7e89f8---0x7ffeea7e89f8---2
int *const p = &a;
// 如果const修飾的是p活鹰,那么p的值是不能改變的,也就是p中存放的a的地址無法改變(p是int類型的指針變量)。但是*p是可以變化的志群,我們并沒有用const去修飾*p着绷,所以可以通過*p去改變a的值
*p = b;
static的作用
static NSInteger staticValue = 0;
static關(guān)鍵字修飾局部變量:
當(dāng)static關(guān)鍵字修飾局部變量時(shí),只會(huì)初始化一次且在程序中只有一份內(nèi)存
關(guān)鍵字static不可以改變局部變量的作用域锌云,但可延長局部變量的生命周期(直到程序結(jié)束才銷毀)
static關(guān)鍵字修飾全局變量:
當(dāng)static關(guān)鍵字修飾全局變量時(shí)荠医,作用域僅限于當(dāng)前文件,外部類是不可以訪問到該全局變量的(即使在外部使用extern關(guān)鍵字也無法訪問)
如果需要直接訪問 需要引用頭文件
宏定義
宏定義屬于預(yù)編譯指令桑涎,在程序運(yùn)行之前已經(jīng)編譯好了的
#define M_PI 3.14159265358979323846264338327950288
#define SELF(x) x //NSLog(@"Hello %@",SELF(name));
#define PLUS(x,y) x + y //printf("%d",PLUS(3,2));
#define MIN(A,B) A < B ? A : B // int a = MIN(1,2);
#define NSLog(format, ...) do { \ fprintf(stderr, "<%s : %d> %s\n", \
[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], \
__LINE__, __func__); \
(NSLog)((format), ##__VA_ARGS__); \
fprintf(stderr, "-------\n"); \
} while (0)
1彬向、block分幾種?分別是怎么樣產(chǎn)生的石洗?block的實(shí)質(zhì)是什么幢泼?
在內(nèi)存角度來看,block分為 全局 讲衫、棧 和 堆 三種類型缕棵,
有強(qiáng)引用的block就屬于堆內(nèi)存block,
只用到外部局部變量、成員屬性變量涉兽、沒有強(qiáng)指針引用的block屬于棧block
只引用全局變量或靜態(tài)變量的block招驴,生命周期和程序生命周期一樣的block就是全局block
block的實(shí)質(zhì)是一個(gè)對(duì)象,一個(gè)結(jié)構(gòu)體
2枷畏、__block修飾的變量為什么能在block里面能改變其值别厘?
__block修飾符標(biāo)記后,block就會(huì)訪問標(biāo)記變量本身內(nèi)存地址拥诡,而未標(biāo)記對(duì)象則訪問截獲拷貝后的變量的內(nèi)存地址
3触趴、block應(yīng)該用copy關(guān)鍵字還是strong關(guān)鍵字?
block 使用 copy 是從 MRC 遺留下來的“傳統(tǒng)”
在 MRC 中,方法內(nèi)部的 block 是在棧區(qū)的,使用 copy 可以把它放到堆區(qū)渴肉。
在 ARC 中寫不寫都行
對(duì)于 block 使用 copy 還是 strong 效果是一樣的冗懦,但寫上 copy 也無傷大雅,還能時(shí)刻提醒我們:編譯器自動(dòng)對(duì) block 進(jìn)行了 copy 操作仇祭。如果不寫 copy 披蕉,該類的調(diào)用者有可能會(huì)忘記或者根本不知道“編譯器會(huì)自動(dòng)對(duì) block 進(jìn)行了 copy 操作”,他們有可能會(huì)在調(diào)用之前自行拷貝屬性值乌奇。這種操作多余而低效没讲。
4、@property 的本質(zhì)是什么礁苗?
@property = ivar + getter + setter;
“屬性” (property)有兩大概念:ivar(實(shí)例變量)爬凑、getter+setter(存取方法)
“屬性” (property)作為 Objective-C 的一項(xiàng)特性,主要的作用就在于封裝對(duì)象中的數(shù)據(jù)寂屏。 Objective-C 對(duì)象通常會(huì)把其所需要的數(shù)據(jù)保存為各種實(shí)例變量贰谣。實(shí)例變量一般通過“存取方法”(access method)來訪問娜搂。其中,“獲取方法” (getter)用于讀取變量值吱抚,而“設(shè)置方法” (setter)用于寫入變量值百宇。
5、ivar秘豹、getter携御、setter 是如何生成并添加到類中的
引申一個(gè)問題:@synthesize 和 @dynamic 分別有什么作用?
完成屬性(@property)定義后既绕,編譯器會(huì)自動(dòng)編寫訪問這些屬性所需的方法啄刹,此過程叫做“自動(dòng)合成”(autosynthesis)。
我們也可以在類的實(shí)現(xiàn)代碼里通過 @synthesize 語法來指定實(shí)例變量的名字凄贩。
@synthesize lastName = _myLastName;
或者通過 @dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶自己實(shí)現(xiàn)誓军,不自動(dòng)生成。
@property有兩個(gè)對(duì)應(yīng)的詞疲扎,
一個(gè)是@synthesize(合成實(shí)例變量)昵时,一個(gè)是@dynamic。
如果@synthesize和@dynamic都沒有寫椒丧,那么默認(rèn)的就是 @synthesize var = _var;
// 在類的實(shí)現(xiàn)代碼里通過 @synthesize 語法可以來指定實(shí)例變量的名字壹甥。(@synthesize var = _newVar;)
1. @synthesize 的語義是如果你沒有手動(dòng)實(shí)現(xiàn)setter方法和getter方法,那么編譯器會(huì)自動(dòng)為你加上這兩個(gè)方法壶熏。
2. @dynamic 告訴編譯器句柠,屬性的setter與getter方法由用戶自己實(shí)現(xiàn),不自動(dòng)生成(如棒假,@dynamic var)溯职。
5、用@property聲明的 NSString / NSArray / NSDictionary 經(jīng)常使用 copy 關(guān)鍵字帽哑,為什么缸榄?如果改用strong關(guān)鍵字,可能造成什么問題祝拯?
用 @property 聲明 NSString、NSArray她肯、NSDictionary 經(jīng)常使用 copy 關(guān)鍵字佳头,是因?yàn)樗麄冇袑?duì)應(yīng)的可變類型:NSMutableString、NSMutableArray晴氨、NSMutableDictionary康嘉,他們之間可能進(jìn)行賦值操作(就是把可變的賦值給不可變的),為確保對(duì)象中的字符串值不會(huì)無意間變動(dòng)籽前,應(yīng)該在設(shè)置新屬性值時(shí)拷貝一份亭珍。
1. 因?yàn)楦割愔羔樋梢灾赶蜃宇悓?duì)象,使用 copy 的目的是為了讓本對(duì)象的屬性不受外界影響,使用 copy 無論給我傳入是一個(gè)可變對(duì)象還是不可對(duì)象,我本身持有的就是一個(gè)不可變的副本敷钾。
2. 如果我們使用是 strong ,那么這個(gè)屬性就有可能指向一個(gè)可變對(duì)象,如果這個(gè)可變對(duì)象在外部被修改了,那么會(huì)影響該屬性。
//總結(jié):使用copy的目的是肄梨,防止把可變類型的對(duì)象賦值給不可變類型的對(duì)象時(shí)阻荒,可變類型對(duì)象的值發(fā)送變化會(huì)無意間篡改不可變類型對(duì)象原來的值。
這里還有一個(gè)引申問題:
NSMutableArray 如果用 copy修飾了會(huì)出現(xiàn)什么問題?
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArray0 addObject:]: unrecognized selector sent to instance 0x600000a100c0'
由于使用的是copy屬性众羡,本身的可變屬性默認(rèn)有一個(gè)不可變的拷貝 NSArray 侨赡,所以我們用這個(gè)可變數(shù)組去添加元素的時(shí)候,找不到對(duì)應(yīng)方法而發(fā)生crash粱侣。
6羊壹、淺拷貝和深拷貝的區(qū)別?
淺拷貝(copy):只復(fù)制指向?qū)ο蟮闹羔樒胗ぃ粡?fù)制引用對(duì)象本身油猫。
深拷貝(mutableCopy):復(fù)制引用對(duì)象本身。內(nèi)存中存在了兩份獨(dú)立對(duì)象本身柠偶,當(dāng)修改A時(shí)情妖,A_copy不變。
只有對(duì)不可變對(duì)象進(jìn)行copy操作是指針復(fù)制(淺復(fù)制)嚣州,其它情況都是內(nèi)容復(fù)制(深復(fù)制)
8鲫售、如何讓自己的類用copy修飾符
若想令自己所寫的對(duì)象具有拷貝功能,則需實(shí)現(xiàn) NSCopying 協(xié)議该肴。如果自定義的對(duì)象分為可變版本與不可變版本情竹,那么就要同時(shí)實(shí)現(xiàn) NSCopying 與 NSMutableCopying 協(xié)議。
具體步驟:
1. 需聲明該類遵從 NSCopying 協(xié)議
2. 實(shí)現(xiàn) NSCopying 協(xié)議的方法匀哄。
// 該協(xié)議只有一個(gè)方法:
- (id)copyWithZone:(NSZone *)zone;
// 注意:使用 copy 修飾符秦效,調(diào)用的是copy方法,其實(shí)真正需要實(shí)現(xiàn)的是 “copyWithZone” 方法涎嚼。
9阱州、ViewController生命周期
按照?qǐng)?zhí)行順序排列:
1. initWithCoder:通過nib文件初始化時(shí)觸發(fā)。
2. awakeFromNib:nib文件被加載的時(shí)候法梯,會(huì)發(fā)生一個(gè)awakeFromNib的消息到nib文件中的每個(gè)對(duì)象苔货。
//如果不是nib初始化 上面兩個(gè)換成 initWithNibName:bundle:
3. loadView:開始加載視圖控制器自帶的view。
4. viewDidLoad:視圖控制器的view被加載完成立哑。
5. viewWillAppear:視圖控制器的view將要顯示在window上夜惭。
6. updateViewConstraints:視圖控制器的view開始更新AutoLayout約束。
7. viewWillLayoutSubviews:視圖控制器的view將要更新內(nèi)容視圖的位置铛绰。
8. viewDidLayoutSubviews:視圖控制器的view已經(jīng)更新視圖的位置诈茧。
9. viewDidAppear:視圖控制器的view已經(jīng)展示到window上。
10. viewWillDisappear:視圖控制器的view將要從window上消失捂掰。
11. viewDidDisappear:視圖控制器的view已經(jīng)從window上消失敢会。
10曾沈、OC的反射機(jī)制
1). class反射
通過類名的字符串形式實(shí)例化對(duì)象。
Class class = NSClassFromString(@"student");
Student *stu = [[class alloc] init];
將類名變?yōu)樽址? Class class =[Student class];
NSString *className = NSStringFromClass(class);
2). SEL的反射
通過方法的字符串形式實(shí)例化方法鸥昏。
SEL selector = NSSelectorFromString(@"setName");
[stu performSelector:selector withObject:@"Mike"];
將方法變成字符串塞俱。
NSStringFromSelector(@selector*(setName:));
11、self 和 super
self 是類的隱藏參數(shù)互广,指向當(dāng)前調(diào)用方法的這個(gè)類的實(shí)例肩碟。
super是一個(gè)Magic Keyword羹唠,它本質(zhì)是一個(gè)編譯器標(biāo)示符盹舞,和self是指向的同一個(gè)消息接收者眷唉。
不同的是:super會(huì)告訴編譯器,調(diào)用class這個(gè)方法時(shí)旅敷,要去父類的方法生棍,而不是本類里的。
12媳谁、id 和 NSObject*的區(qū)別
id是一個(gè) objc_object 結(jié)構(gòu)體指針涂滴,定義是
typedef struct objc_object *id
id可以理解為指向?qū)ο蟮闹羔槨K衞c的對(duì)象 id都可以指向晴音,編譯器不會(huì)做類型檢查柔纵,id調(diào)用任何存在的方法都不會(huì)在編譯階段報(bào)錯(cuò),當(dāng)然如果這個(gè)id指向的對(duì)象沒有這個(gè)方法锤躁,該崩潰還是會(huì)崩潰的搁料。
NSObject *指向的必須是NSObject的子類,調(diào)用的也只能是NSObjec里面的方法否則就要做強(qiáng)制類型轉(zhuǎn)換系羞。
不是所有的OC對(duì)象都是NSObject的子類郭计,還有一些繼承自NSProxy。NSObject *可指向的類型是id的子集椒振。
引申: id 和 instancetype 的區(qū)別
instancetype的作用昭伸,就是使那些非關(guān)聯(lián)返回類型的方法返回所在類的類型!
相同點(diǎn):
都可以作為方法的返回類型
不同點(diǎn):
instancetype可以返回和方法所在類相同類型的對(duì)象澎迎,id只能返回未知類型的對(duì)象
instancetype只能作為返回值庐杨,不能像id那樣作為參數(shù)
13、NSDictionary的實(shí)現(xiàn)原理是什么夹供?
一:字典原理
NSDictionary(字典)是使用hash表來實(shí)現(xiàn)key和value之間的映射和存儲(chǔ)的
方法:- (void)setObject:(id)anObject forKey:(id)aKey;
Objective-C中的字典NSDictionary底層其實(shí)是一個(gè)哈希表
引申:字典的查詢工作原理
字典的工作原理 辑莫?怎100w個(gè)中是怎么快速去取value?
位圖算法:
給定10億個(gè)不重復(fù)的正int的整數(shù)罩引,沒排過序的,然后再給一個(gè)數(shù)枝笨,如何快速判斷這個(gè)數(shù)是否在那10億個(gè)數(shù)當(dāng)中袁铐。
解法:遍歷40個(gè)億數(shù)字揭蜒,映射到BitMap中,然后對(duì)于給出的數(shù)剔桨,直接判斷指定的位上存在不存在即可屉更。
14、你們的App是如何處理本地?cái)?shù)據(jù)安全的(比如用戶名的密碼)洒缀?
本地盡量不存儲(chǔ)用戶隱私數(shù)據(jù)瑰谜、敏感信息
使用如AES256加密算法對(duì)數(shù)據(jù)進(jìn)行安全加密后再存入SQLite中
或者數(shù)據(jù)庫整體加密
存放在keychain里面
向Keychain中存儲(chǔ)數(shù)據(jù)時(shí),不要使用kSecAttrAccessibleAlways树绩,而是使用更安全的kSecAttrAccessibleWhenUnlocked或kSecAttrAccessibleWhenUnlockedThisDeviceOnly選項(xiàng)萨脑。
AES DES
15、遇到過BAD_ACCESS的錯(cuò)誤嗎饺饭?你是怎樣調(diào)試的渤早?
90%的錯(cuò)誤來源在于對(duì)一個(gè)已經(jīng)釋放的對(duì)象進(jìn)行release操作, 或者說對(duì)一個(gè)訪問不到的地址進(jìn)行訪問,可能是由于些變量已經(jīng)被回收了瘫俊,亦可能是由于使用棧內(nèi)存的基本類型的數(shù)據(jù)賦值給了id類型的變量鹊杖。
例如:
id x_id = [self performSelector:@selector(returnInt)];
- (int)returnInt { return 5; }
上面通過id去接受int返回值,int是存放在棧里面的扛芽,堆內(nèi)存地址如何找得到骂蓖,自然就是 EXC_BAD_ACCESS。
處理方法
1川尖、xcode可以用僵尸模式打印出對(duì)象 然后通過對(duì)象查找對(duì)應(yīng)的代碼位置
1登下、Edit Scheme - Diagnositics - Memory Management 勾選 Zombie Objects 和 Malloc Stack
2、會(huì)打印出
cyuyan[7756:17601127] *** -[UIViewController respondsToSelector:]: message sent to deallocated instance 0x7fe71240d390
這句開啟僵尸模式后打出來的輸出空厌,包含了我們需要的 進(jìn)程pid庐船、崩潰地址,終端通過下面命令查看堆棧日志來找到崩潰代碼
3嘲更、查找日志
sudo malloc_history 7756 0x7fe71240d390
2筐钟、在 other c flags中加入-D FOR_DEBUG(記住請(qǐng)只在Debug Configuration下加入此標(biāo)記)。這樣當(dāng)你程序崩潰時(shí)赋朦,Xcode的console上就會(huì)準(zhǔn)確地記錄了最后運(yùn)行的object的方法篓冲。重寫一個(gè)object的respondsToSelector方法,打印報(bào)錯(cuò)前的
#ifdef _FOR_DEBUG_
-(BOOL) respondsToSelector:(SEL)aSelector {
printf("SELECTOR: %s\n", [NSStringFromSelector(aSelector) UTF8String]);
return [super respondsToSelector:aSelector];
}
#endif
3宠哄、通過instruments的Zombies
引申:怎么定位到野指針的地方壹将。如果還沒定位到,這個(gè)對(duì)象被提前釋放了毛嫉,怎么知道該對(duì)象在什么地方釋放的
一種是多線程诽俯,一種是野指針。這兩種Crash都帶隨機(jī)性承粤,我們要讓隨機(jī)crash變成不隨機(jī)
把這一隨機(jī)的過程變成不隨機(jī)的過程暴区。對(duì)象釋放后在內(nèi)存上填上不可訪問的數(shù)據(jù)闯团,其實(shí)這種技術(shù)其實(shí)一直都有,xcode的Enable Scribble就是這個(gè)作用仙粱。
1房交、Edit Scheme - Diagnositics - Memory Management 勾選 Malloc Scribble
暫時(shí)未解決
16、如何設(shè)計(jì)一個(gè)通知中心
單例設(shè)計(jì)一個(gè)NotificationCenter伐割,NSPointerArray 保存 observer候味,對(duì)象銷毀 observer自動(dòng)變null
17、KVO隔心、KVC的實(shí)現(xiàn)原理
KVC( 鍵值編碼 )實(shí)現(xiàn)
1.KVC是基于runtime機(jī)制實(shí)現(xiàn)的
2白群、可以訪問私有成員變量、可以間接修改私有變量的值
[object setValue:@"134567" forKey:@"uid"];
就會(huì)被編譯器處理成:
// 首先找到對(duì)應(yīng)sel
SEL sel = sel_get_uid("setValue:forKey:");
// 根據(jù)object->isa找到sel對(duì)應(yīng)的IMP實(shí)現(xiàn)指針
IMP method = objc_msg_lookup (object->isa,sel);
// 調(diào)用指針完成KVC賦值
method(object, sel, @"134567", @"uid");
KVC鍵值查找原理
setValue:forKey:搜索方式
1济炎、首先搜索setKey:方法.(key指成員變量名, 首字母大寫)
2川抡、上面的setter方法沒找到, 如果類方法accessInstanceVariablesDirectly返回YES. 那么按 _key, _isKey,key, iskey的順序搜索成員名须尚。(這個(gè)類方法是NSKeyValueCodingCatogery中實(shí)現(xiàn)的類方法, 默認(rèn)實(shí)現(xiàn)為返回YES)
3崖堤、如果沒有找到成員變量, 調(diào)用setValue:forUnderfinedKey:
valueForKey:的搜索方式
1、首先按getKey, key, isKey的順序查找getter方法, 找到直接調(diào)用. 如果是BOOL耐床、int等內(nèi)建值類型, 會(huì)做NSNumber的轉(zhuǎn)換.
2密幔、上面的getter沒找到, 查找countOfKey, objectInKeyAtindex, KeyAtindexes格式的方法. 如果countOfKey和另外兩個(gè)方法中的一個(gè)找到, 那么就會(huì)返回一個(gè)可以響應(yīng)NSArray所有方法的代理集合的NSArray消息方法.
3、還沒找到, 查找countOfKey, enumeratorOfKey, memberOfKey格式的方法. 如果這三個(gè)方法都找到, 那么就返回一個(gè)可以響應(yīng)NSSet所有方法的代理集合.
4撩轰、還是沒找到, 如果類方法accessInstanceVariablesDirectly返回YES. 那么按 _key, _isKey, key, iskey的順序搜索成員名.
5胯甩、再?zèng)]找到, 調(diào)用valueForUndefinedKey.
KVO實(shí)現(xiàn) 鍵值觀察、觀察者模式的一種應(yīng)用
簡(jiǎn)答
1.KVO是基于runtime機(jī)制實(shí)現(xiàn)的
2.當(dāng)某個(gè)類的屬性對(duì)象第一次被觀察時(shí)堪嫂,系統(tǒng)就會(huì)在運(yùn)行期動(dòng)態(tài)地創(chuàng)建該類的一個(gè)派生類偎箫,在這個(gè)派生類中重寫基類中任何被觀察屬性的setter 方法。派生類在被重寫的setter方法內(nèi)實(shí)現(xiàn)真正的通知機(jī)制
3.如果原類為Person皆串,那么生成的派生類名為NSKVONotifying_Person
4.每個(gè)類對(duì)象中都有一個(gè)isa指針指向當(dāng)前類淹办,當(dāng)一個(gè)類對(duì)象的第一次被觀察,那么系統(tǒng)會(huì)偷偷將isa指針指向動(dòng)態(tài)生成的派生類恶复,從而在給被監(jiān)控屬性賦值時(shí)執(zhí)行的是派生類的setter方法
5.鍵值觀察通知依賴于NSObject 的兩個(gè)方法: willChangeValueForKey: 和 didChangevlueForKey:怜森;在一個(gè)被觀察屬性發(fā)生改變之前, willChangeValueForKey:一定會(huì)被調(diào)用谤牡,這就 會(huì)記錄舊的值副硅。而當(dāng)改變發(fā)生后,didChangeValueForKey:會(huì)被調(diào)用翅萤,繼而 observeValueForKey:ofObject:change:context: 也會(huì)被調(diào)用恐疲。
深入
1.Apple 使用了 isa 混寫(isa-swizzling)來實(shí)現(xiàn) KVO 。當(dāng)觀察對(duì)象A時(shí),KVO機(jī)制動(dòng)態(tài)創(chuàng)建一個(gè)新的名為:?NSKVONotifying_A的新類培己,該類繼承自對(duì)象A的本類糜烹,且KVO為NSKVONotifying_A重寫觀察屬性的setter?方法,setter?方法會(huì)負(fù)責(zé)在調(diào)用原?setter?方法之前和之后漱凝,通知所有觀察對(duì)象屬性值的更改情況。
2.NSKVONotifying_A類剖析:在這個(gè)過程诸迟,被觀察對(duì)象的 isa 指針從指向原來的A類茸炒,被KVO機(jī)制修改為指向系統(tǒng)新創(chuàng)建的子類 NSKVONotifying_A類,來實(shí)現(xiàn)當(dāng)前類屬性值改變的監(jiān)聽阵苇;
3.所以當(dāng)我們從應(yīng)用層面上看來壁公,完全沒有意識(shí)到有新的類出現(xiàn),這是系統(tǒng)“隱瞞”了對(duì)KVO的底層實(shí)現(xiàn)過程绅项,讓我們誤以為還是原來的類紊册。但是此時(shí)如果我們創(chuàng)建一個(gè)新的名為“NSKVONotifying_A”的類(),就會(huì)發(fā)現(xiàn)系統(tǒng)運(yùn)行到注冊(cè)KVO的那段代碼時(shí)程序就崩潰快耿,因?yàn)橄到y(tǒng)在注冊(cè)監(jiān)聽的時(shí)候動(dòng)態(tài)創(chuàng)建了名為NSKVONotifying_A的中間類囊陡,并指向這個(gè)中間類了。
4.(isa 指針的作用:每個(gè)對(duì)象都有isa 指針掀亥,指向該對(duì)象的類撞反,它告訴 Runtime 系統(tǒng)這個(gè)對(duì)象的類是什么。所以對(duì)象注冊(cè)為觀察者時(shí)搪花,isa指針指向新子類遏片,那么這個(gè)被觀察的對(duì)象就神奇地變成新子類的對(duì)象(或?qū)嵗┝恕#?因而在該對(duì)象上對(duì) setter 的調(diào)用就會(huì)調(diào)用已重寫的 setter撮竿,從而激活鍵值通知機(jī)制吮便。
5.子類setter方法剖析:KVO的鍵值觀察通知依賴于 NSObject 的兩個(gè)方法:willChangeValueForKey:和 didChangevlueForKey:,在存取數(shù)值的前后分別調(diào)用2個(gè)方法: 被觀察屬性發(fā)生改變之前幢踏,willChangeValueForKey:被調(diào)用髓需,通知系統(tǒng)該 keyPath?的屬性值即將變更;當(dāng)改變發(fā)生后惑折, didChangeValueForKey: 被調(diào)用授账,通知系統(tǒng)該 keyPath?的屬性值已經(jīng)變更;之后惨驶,?observeValueForKey:ofObject:change:context: 也會(huì)被調(diào)用白热。且重寫觀察屬性的setter?方法這種繼承方式的注入是在運(yùn)行時(shí)而不是編譯時(shí)實(shí)現(xiàn)的。
19粗卜、category為什么不能添加屬性屋确?
category 它是在運(yùn)行期決議的,因?yàn)樵谶\(yùn)行期,對(duì)象的內(nèi)存布局已經(jīng)確定攻臀,如果添加實(shí)例變量就會(huì)破壞類的內(nèi)部布局焕数,這對(duì)編譯型語言來說是災(zāi)難性的。
extension看起來很像一個(gè)匿名的category刨啸,但是extension和有名字的category幾乎完全是兩個(gè)東西堡赔。 extension在編譯期決議,它就是類的一部分设联,在編譯期和頭文件里的@interface以及實(shí)現(xiàn)文件里的@implement一起形成一個(gè)完整的類善已,它伴隨類的產(chǎn)生而產(chǎn)生,亦隨之一起消亡离例。extension一般用來隱藏類的私有信息换团,你必須有一個(gè)類的源碼才能為一個(gè)類添加extension,所以你無法為系統(tǒng)的類比如NSString添加extension宫蛆。
但是category則完全不一樣艘包,它是在運(yùn)行期決議的。
就category和extension的區(qū)別來看耀盗,我們可以推導(dǎo)出一個(gè)明顯的事實(shí)想虎,extension可以添加實(shí)例變量,而category是無法添加實(shí)例變量的袍冷。
那為什么 使用Runtime技術(shù)中的關(guān)聯(lián)對(duì)象可以為類別添加屬性磷醋。
其原因是:關(guān)聯(lián)對(duì)象都由AssociationsManager管理,AssociationsManager里面是由一個(gè)靜態(tài)AssociationsHashMap來存儲(chǔ)所有的關(guān)聯(lián)對(duì)象的胡诗。這相當(dāng)于把所有對(duì)象的關(guān)聯(lián)對(duì)象都存在一個(gè)全局map里面邓线。而map的的key是這個(gè)對(duì)象的指針地址(任意兩個(gè)不同對(duì)象的指針地址一定是不同的),而這個(gè)map的value又是另外一個(gè)AssociationsHashMap煌恢,里面保存了關(guān)聯(lián)對(duì)象的kv對(duì)骇陈。
如合清理關(guān)聯(lián)對(duì)象?
runtime的銷毀對(duì)象函數(shù)objc_destructInstance里面會(huì)判斷這個(gè)對(duì)象有沒有關(guān)聯(lián)對(duì)象瑰抵,如果有你雌,會(huì)調(diào)用_object_remove_assocations做關(guān)聯(lián)對(duì)象的清理工作。(詳見Runtime的源碼)
Objective-C Associated Objects 的實(shí)現(xiàn)原理
20二汛、說一下runloop和線程的關(guān)系
runloop與線程是一一對(duì)應(yīng)的
runloop是來管理線程的
線程和 RunLoop 之間是一一對(duì)應(yīng)的婿崭,其關(guān)系是保存在一個(gè)全局的 Dictionary 里。線程剛創(chuàng)建時(shí)并沒有 RunLoop肴颊,如果你不主動(dòng)獲取氓栈,那它一直都不會(huì)有。RunLoop 的創(chuàng)建是發(fā)生在第一次獲取時(shí)婿着,RunLoop 的銷毀是發(fā)生在線程結(jié)束時(shí)授瘦。你只能在一個(gè)線程的內(nèi)部獲取其 RunLoop(主線程除外)
21醋界、說一下autoreleasePool的實(shí)現(xiàn)原理
autoreleasePool是一個(gè)延時(shí)release的機(jī)制, 在自動(dòng)釋放池被銷毀或耗盡時(shí)提完,會(huì)向池中的所有對(duì)象發(fā)送release消息形纺,釋放所有autorelease對(duì)象。
ARC下徒欣,我們使用@autoreleasepool{}來使用一個(gè)自動(dòng)釋放池
AutoreleasePool并沒有單獨(dú)的結(jié)構(gòu)逐样,而是由若干個(gè)AutoreleasePoolPage作為結(jié)點(diǎn)以雙向鏈表的形式組合而成。整個(gè)鏈表以堆棧的形式運(yùn)作打肝。
1官研、每一個(gè)指針代表一個(gè)加入到釋放池的對(duì)象 或者是哨兵對(duì)象,哨兵對(duì)象是在
_objc_autoreleasePoolPush方法調(diào)用的時(shí)候插入的
2闯睹、然后 @autoreleasepool{} 閉包中對(duì)象被插入
2、當(dāng)自動(dòng)釋放池 pop的時(shí)候担神,所有哨兵對(duì)象之后的對(duì)象都會(huì)release
3楼吃、鏈表會(huì)在一個(gè)Page空間占滿時(shí)進(jìn)行增加,一個(gè)AutoreleasePoolPage的空間被占滿時(shí)妄讯,會(huì)新建一個(gè)AutoreleasePoolPage對(duì)象孩锡,連接鏈表,后來的autorelease對(duì)象在新的page加入亥贸。
主線程:
主線程runloop中注冊(cè)了兩個(gè)Observer躬窜,回調(diào)都是 _wrapRunLoopWithAutoreleasePoolHandler()。
第一個(gè)oberver監(jiān)聽 當(dāng)從休眠狀態(tài)即將進(jìn)入loop的時(shí)候 炕置,這個(gè)時(shí)候荣挨,構(gòu)建自動(dòng)釋放池
第二個(gè)oberver監(jiān)聽 當(dāng)準(zhǔn)備進(jìn)入休眠狀態(tài)的時(shí)候,調(diào)用 objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池并創(chuàng)建新池
子線程:
runloop默認(rèn)不開啟朴摊,不會(huì)自動(dòng)創(chuàng)建自動(dòng)釋放池默垄,在需要使用自動(dòng)釋放池的時(shí)候,需要我們手動(dòng)創(chuàng)建甚纲、添加自動(dòng)釋放池口锭,此時(shí)如果所有的異步代碼都寫在自動(dòng)釋放池中,也可以理解為當(dāng)子線程銷毀的時(shí)候介杆,自動(dòng)釋放池釋放
22鹃操、說一下簡(jiǎn)單工廠模式,工廠模式以及抽象工廠模式春哨?
簡(jiǎn)單工廠模式:根據(jù)外部信息就可以決定創(chuàng)建對(duì)象荆隘,所有產(chǎn)品都通過工廠判斷就創(chuàng)建,體系結(jié)構(gòu)很明顯悲靴,缺點(diǎn)就是集中了所有的產(chǎn)品創(chuàng)建邏輯臭胜,耦合太重莫其。
工廠模式:產(chǎn)品的各自創(chuàng)建邏輯下發(fā)到各自的工廠類中,一定程度達(dá)到解耦合耸三。 多態(tài)性乱陡,產(chǎn)品構(gòu)建邏輯可以具體到對(duì)應(yīng)的產(chǎn)品工廠類中,更加清晰仪壮。 當(dāng)我需要新產(chǎn)品的時(shí)候憨颠,只需要添加一個(gè)新的產(chǎn)品工廠,實(shí)現(xiàn)抽象工廠的產(chǎn)品產(chǎn)出方法积锅,產(chǎn)出對(duì)應(yīng)的產(chǎn)品爽彤。不影響客戶邏輯。
抽象工廠模式:當(dāng)有多個(gè)產(chǎn)品線缚陷,需要多個(gè)工廠分別生產(chǎn)不同的產(chǎn)品線產(chǎn)品适篙,這個(gè)時(shí)候我們抽象出工廠邏輯,產(chǎn)品也抽象出產(chǎn)品類型箫爷,工廠抽象類只需要構(gòu)建返回抽象產(chǎn)品的方法即可嚷节,更深程度的解耦。具體的什么工廠產(chǎn)什么產(chǎn)品邏輯下發(fā)到實(shí)際工廠實(shí)現(xiàn)虎锚。 即使添加新產(chǎn)品也不影響抽象工廠和抽象產(chǎn)品的邏輯硫痰。
23、如何設(shè)計(jì)一個(gè)網(wǎng)絡(luò)請(qǐng)求庫
網(wǎng)絡(luò)請(qǐng)求庫需要的功能:
1窜护、在任意位置發(fā)起請(qǐng)求
2效斑、請(qǐng)求表單的創(chuàng)建 (url拼接、參數(shù)填充柱徙、http請(qǐng)求方法確認(rèn))
3缓屠、UI-Loading
4、數(shù)據(jù)解析
5护侮、異常處理
6藏研、結(jié)果提示
自己分裝的 一個(gè) API 網(wǎng)絡(luò)請(qǐng)求庫
24、說一下多線程概行,你平常是怎么用的蠢挡?
常用的有 GCD 和 NSOperation 、NSThread
NSThread 用于獲取當(dāng)前線程等操作
GCD 和 NSOperation 實(shí)現(xiàn)多線程操作不需要自己管理線程凳忙,操作簡(jiǎn)單
GCD block的使用方式比NSOperation 適合簡(jiǎn)單操作业踏,NSOperation 對(duì)象級(jí)操作方法更多,更復(fù)雜操作適用
25涧卵、說一下UITableViewCell的卡頓你是怎么優(yōu)化的勤家?
一般簡(jiǎn)單的UITableViewCell都不會(huì)卡頓,TableView本身有Cell重用機(jī)制柳恐,但一些復(fù)雜的自適應(yīng)高度的cell比較容易產(chǎn)生卡頓伐脖。
1热幔、避免cell的過多重新布局,差別太大的cell之間不要選擇重用讼庇。
2绎巨、提前計(jì)算并緩存cell的高度,內(nèi)容
3蠕啄、盡量減少動(dòng)態(tài)添加View的操作
4场勤、減少所有對(duì)主線程有影響的無意義操作
5、cell中的圖片加載用異步加載歼跟,緩存等
6和媳、局部更新cell
7、減少不必要的渲染時(shí)間哈街,比如少用透明色之類的
28留瞳、什么是ARC?(ARC是為了解決什么問題誕生的骚秦?)
ARC全稱是 Automatic Reference Counting撼港,是Objective-C的內(nèi)存管理機(jī)制。簡(jiǎn)單地來說骤竹,就是代碼中自動(dòng)加入了retain/release,原先需要手動(dòng)添加的用來處理內(nèi)存管理的引用計(jì)數(shù)的代碼可以自動(dòng)地由編譯器完成了往毡。
ARC的使用是為了解決對(duì)象retain和release匹配的問題蒙揣。以前手動(dòng)管理造成內(nèi)存泄漏或者重復(fù)釋放的問題將不復(fù)存在。
以前需要手動(dòng)的通過retain去為對(duì)象獲取內(nèi)存开瞭,并用release釋放內(nèi)存懒震。所以以前的操作稱為MRC (Manual Reference Counting)。
29嗤详、請(qǐng)解釋以下keywords的區(qū)別: assign vs weak, _block vs _weak
weak和assign都是引用計(jì)數(shù)不變个扰,兩個(gè)的差別在于,weak用于object type葱色,就是指針類型递宅,而assign用于簡(jiǎn)單的數(shù)據(jù)類型,如int BOOL 等苍狰。
assign看起來跟weak一樣办龄,其實(shí)不能混用的,assign的變量在釋放后并不設(shè)置為nil(和weak不同)淋昭,當(dāng)你再去引用時(shí)候就會(huì)發(fā)生錯(cuò)誤俐填,崩潰,EXC_BAD_ACCESS.
assign 可以修飾對(duì)象么翔忽? 可以修飾英融,編譯器不會(huì)報(bào)錯(cuò)盏檐,但是訪問過程中對(duì)象容易野指針
__block 用于標(biāo)記需要在block內(nèi)部修改的變量,__weak 用于防止引用循環(huán)
30驶悟、使用atomic一定是線程安全的嗎胡野?
atomic只能保證操作也就是存取屬性的時(shí)候的存取方法是線程安全的,并不能保證整個(gè)對(duì)象就是線程安全的撩银。
比如NSMutableArray 設(shè)置值得時(shí)候是線程安全的给涕,但是通過objectAtIndex訪問的時(shí)候就不再是線程安全的了。還是需要鎖來保證線程的安全额获。
31够庙、描述一個(gè)你遇到過的retain cycle例子
VC中一個(gè)強(qiáng)引用block里面使用self
代理使用強(qiáng)引用
sqllite多線程搶寫入操作
32、+(void)load; +(void)initialize; 有什么用處抄邀?方法分別在什么時(shí)候調(diào)用的?
+(void)load;
當(dāng)類對(duì)象被引入項(xiàng)目時(shí), runtime 會(huì)向每一個(gè)類對(duì)象發(fā)送 load 消息耘眨。
load 方法會(huì)在每一個(gè)類甚至分類被引入時(shí)僅調(diào)用一次,調(diào)用的順序:父類優(yōu)先于子類, 子類優(yōu)先于分類。
由于 load 方法會(huì)在類被 import 時(shí)調(diào)用一次,而這時(shí)往往是改變類的行為的最佳時(shí)機(jī)境肾,在這里可以使用例如 method swizlling 來修改原有的方法剔难。
load 方法不會(huì)被類自動(dòng)繼承。
+(void)initialize;
也是在第一次使用這個(gè)類的時(shí)候會(huì)調(diào)用這個(gè)方法奥喻,也就是說 initialize 也是懶加載
總結(jié):
在 Objective-C 中偶宫,runtime 會(huì)自動(dòng)調(diào)用每個(gè)類的這兩個(gè)方法
1.+load 會(huì)在類初始加載時(shí)調(diào)用
2.+initialize 會(huì)在第一次調(diào)用類的類方法或?qū)嵗椒ㄖ氨徽{(diào)用
這兩個(gè)方法是可選的,且只有在實(shí)現(xiàn)了它們時(shí)才會(huì)被調(diào)用
兩者的共同點(diǎn):兩個(gè)方法都只會(huì)被調(diào)用一次
33环鲤、談一談消息發(fā)送 或者 對(duì)runtime的理解, 說一下工作中是如何使用runtime的纯趋?看過runtime源碼嗎?
runtime是 oc 語言特性冷离,方法調(diào)用采用消息發(fā)送的方式吵冒,直到項(xiàng)目運(yùn)行階段才能最終確定,并且還可以動(dòng)態(tài)添加成員變量與方法西剥。
項(xiàng)目中用的多的runtime應(yīng)該是方法實(shí)現(xiàn)的替換痹栖,動(dòng)態(tài)屬性的添加,KVO瞭空,performSelector揪阿,消息轉(zhuǎn)發(fā)之類
34、如何高性能的給UIImageView加個(gè)圓角咆畏?
如何高性能的給 UIImageView 加個(gè)圓角?
不好的解決方案:使用下面的方式會(huì)強(qiáng)制Core Animation提前渲染屏幕的離屏繪制, 而離屏繪制就會(huì)給性能帶來負(fù)面影響图甜,會(huì)有卡頓的現(xiàn)象出現(xiàn)。
self.view.layer.cornerRadius = 5.0f;
self.view.layer.masksToBounds = YES;
正確的解決方案:使用繪圖技術(shù)
- (UIImage *)circleImage {
// NO代表透明
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
// 獲得上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 添加一個(gè)圓
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
CGContextAddEllipseInRect(ctx, rect);
// 裁剪
CGContextClip(ctx);
// 將圖片畫上去
[self drawInRect:rect];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 關(guān)閉上下文
UIGraphicsEndImageContext();
return image;
}
還有一種方案:使用了貝塞爾曲線"切割"個(gè)這個(gè)圖片, 給UIImageView 添加了的圓角鳖眼,其實(shí)也是通過繪圖技術(shù)來實(shí)現(xiàn)的黑毅。
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
imageView.center = CGPointMake(200, 300);
UIImage *anotherImage = [UIImage imageNamed:@"image"];
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds
cornerRadius:50] addClip];
[anotherImage drawInRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.view addSubview:imageView];
36、設(shè)計(jì)一個(gè)檢測(cè)主線程卡頓的方案
卡頓的原因就是耗時(shí)長钦讳,設(shè)計(jì)一個(gè)檢測(cè)主線程方法執(zhí)行時(shí)間過長的方案
37矿瘦、說幾個(gè)你在工作中使用到的線程安全的例子
多線程同時(shí)操作同一個(gè)數(shù)據(jù)源的時(shí)候
AFNetworking 對(duì)于session的構(gòu)建等都是線程安全的
38枕面、用過哪些鎖?哪些鎖的性能比較高缚去?談下Objective C都有哪些鎖機(jī)制潮秘,你一般用哪個(gè)?
常用的鎖有 NSLock易结、@synchronized代碼塊枕荞、信號(hào)量 dispatch_semaphore_t
信號(hào)量性能最高
@synchronized代碼塊 最方便
32、說一下靜態(tài)庫和動(dòng)態(tài)庫之間的區(qū)別
靜態(tài)庫
.a 搞动、.framework 結(jié)尾
是一個(gè)已經(jīng)編譯好了的集合躏精,使用的時(shí)候連接器會(huì)把靜態(tài)庫合并到可執(zhí)行文件中。
動(dòng)態(tài)庫
.tbd 或 .framework結(jié)尾
編譯過程不會(huì)被鏈接到目標(biāo)代碼中, 只會(huì)將動(dòng)態(tài)庫頭文件添加到目標(biāo)app的可執(zhí)行文件鹦肿,程序運(yùn)行的時(shí)候被添加在獨(dú)立于app的內(nèi)存區(qū)域矗烛。
36、說一下你對(duì)架構(gòu)的理解箩溃? 技術(shù)架構(gòu)如何搭建瞭吃?
設(shè)計(jì)一個(gè)架構(gòu) 需要考慮多個(gè)層次
1、代碼風(fēng)格涣旨、例如 代碼整齊歪架,一個(gè)類不能干兩個(gè)事情,目錄設(shè)定要清晰一眼就知道是干什么的霹陡,不要設(shè)置什么common module之類的目錄和蚪,面向協(xié)議開發(fā),瘦Controller啊等
2穆律、規(guī)范業(yè)務(wù)塊的分層,例如 MVC 或者 MVVM导俘,統(tǒng)一的業(yè)務(wù)處理分層峦耘,讓業(yè)務(wù)代碼更清晰,耦合性也低
3旅薄、基礎(chǔ)層的定義辅髓, 開發(fā)幫助庫,例如 網(wǎng)絡(luò)庫少梁,數(shù)據(jù)持久化庫洛口,路由庫件蚕,要求易于擴(kuò)展冀墨、易于測(cè)試,易于理解上枕,讓開發(fā)小伙伴上手快妨马,接口方法設(shè)定要靈活挺举,減少開發(fā)小伙伴的使用成本
4杀赢、組件化,一個(gè)架構(gòu)本身也需要良好的封裝湘纵,合理的組件化可以讓功能更清晰脂崔,耦合性也更低,
大的組件化就是項(xiàng)目層級(jí)梧喷,把不常改動(dòng)的基礎(chǔ)庫沉底砌左,比如放pod中,經(jīng)常擴(kuò)展的內(nèi)容放在工程里面铺敌,獨(dú)立的業(yè)務(wù)塊可以通過工程的方式依賴
小的組件化就是UI方面汇歹,統(tǒng)一封裝管理UI輪子,避免一個(gè)東西出現(xiàn)很多份的情況
37适刀、為什么一定要在主線程里面更新UI秤朗?
UIKit 不是線程安全的,容易產(chǎn)生UI更新上的混亂
39笔喉、講講你用Instrument優(yōu)化動(dòng)畫性能的經(jīng)歷吧
core animation的使用
time profiler 的使用
40取视、loadView是干嘛用的?
self.view的初始化常挚,根據(jù)xib初始化或者init初始化
41作谭、viewWillLayoutSubView
controller layout觸發(fā)的時(shí)候,開發(fā)者有機(jī)會(huì)去重新layout自己的各個(gè)subview奄毡。說UI熟悉的一定要知道折欠。
當(dāng)子View發(fā)生frame的變動(dòng)的時(shí)候會(huì)觸發(fā)layoutsubView,我們可以在這個(gè)方法中提前做一些預(yù)處理
42吼过、GCD里面有哪幾種Queue锐秦?你自己建立過串行queue嗎?背后的線程模型是什么樣的盗忱?
兩種queue酱床,串行和并行。
main queue是串行趟佃,global queue是并行扇谣。
有些開發(fā)者為了在工作線程串行的處理任務(wù)會(huì)自己建立一個(gè)serial queue。背后是蘋果維護(hù)的線程池闲昭,各種queue要用線程都是這個(gè)池子里取的罐寨。
43、用過coredata或者sqlite嗎序矩?讀寫是分線程的嗎鸯绿?遇到過死鎖沒?咋解決的?
sqlite 一個(gè)線程A操作寫入楞慈、一個(gè)線程B操作讀取幔烛,在第一個(gè)線程等待寫入的過程中也發(fā)起寫入,寫入操作在普通的事務(wù)操作 begin trancaction commit transaction 囊蓝,這種情況就會(huì)死鎖
兩個(gè)線程都爭(zhēng)取寫入操作饿悬,因?yàn)樵贏線程等待變成排他鎖的過程中處于待定鎖狀態(tài),并不會(huì)拒絕B線程的保留鎖的獲取聚霜,導(dǎo)致B線程一直不釋放共享鎖狡恬,A就一直得不到排他鎖,造成死鎖蝎宇。
單個(gè)線程可以死鎖(main thread里dispatch_sync到main queue)弟劲,
多個(gè)線程直接也可以死鎖(A,B線程互相持有對(duì)方需要的資源且互相等待)姥芥。
44兔乞、NSString如何計(jì)算字符的個(gè)數(shù)?
- (int)myStrLength:(NSString *)str {
int length = 0;
char * p_str = [str cStringUsingEncoding:NSUTF8StringEncoding];
for (int i = 0; i < [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; i++) {
if (*p_str) {
p_str++;
length++;
} else {
p_str++;
}
}
return length;
}
45凉唐、PKI體系(其實(shí)就是CA證書驗(yàn)證體系)當(dāng)中加密和簽名有什么區(qū)別庸追?
簽名密鑰對(duì)用于數(shù)據(jù)的完整性檢測(cè),保證防偽造與防抵賴台囱,簽名私鑰的遺失淡溯,并不會(huì)影響對(duì)以前簽名數(shù)據(jù)的驗(yàn)證,因此簿训,簽名私鑰無須備份咱娶,因此,簽名密鑰不需要也不應(yīng)該需要第三方來管理强品,完全由持有者自己產(chǎn)生膘侮;
加密密鑰對(duì)用于數(shù)據(jù)的加密保護(hù),若加密私鑰遺失的榛,將導(dǎo)致以前的加密數(shù)據(jù)無法解密琼了,這在實(shí)際應(yīng)用中是無法接受的,加密私鑰應(yīng)該由可信的第三方(即通常所說的CA)來備份困曙,以保證加密數(shù)據(jù)的可用性表伦,因此谦去,加密密鑰對(duì)可以由第三方來產(chǎn)生慷丽,并備份。
一個(gè)加密 一個(gè)保證完整性
47鳄哭、數(shù)據(jù)庫建表的時(shí)候索引有什么用要糊?
可以大大加快數(shù)據(jù)的檢索速度,這也是創(chuàng)建索引的最主要的原因妆丘。
通過創(chuàng)建唯一性索引锄俄,可以保證數(shù)據(jù)庫表中每一行數(shù)據(jù)的唯一性局劲。
49、iOS下如何實(shí)現(xiàn)指定線程數(shù)目的線程池奶赠?
使用信號(hào)量
GCD的信號(hào)量機(jī)制(dispatch_semaphore)
信號(hào)量是一個(gè)整型值鱼填,有初始計(jì)數(shù)值;可以接收通知信號(hào)和等待信號(hào)毅戈。當(dāng)信號(hào)量收到通知信號(hào)時(shí)苹丸,計(jì)數(shù)+1;當(dāng)信號(hào)量收到等待信號(hào)時(shí)苇经,計(jì)數(shù)-1赘理;如果信號(hào)量為0,線程會(huì)阻塞扇单,直到線程信號(hào)量大于0商模,才會(huì)繼續(xù)下去。
使用信號(hào)量機(jī)制可以實(shí)現(xiàn)線程的同步蜘澜,也可以控制最大并發(fā)數(shù)施流。以下是控制最大并發(fā)數(shù)的代碼。
dispatch_queue_t workConcurrentQueue = dispatch_queue_create("cccccccc", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t serialQueue = dispatch_queue_create("sssssssss",DISPATCH_QUEUE_SERIAL);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);
for (NSInteger i = 0; i < 10; i++) {
dispatch_async(serialQueue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(workConcurrentQueue, ^{
NSLog(@"thread-info:%@開始執(zhí)行任務(wù)%d",[NSThread currentThread],(int)i);
sleep(1);
NSLog(@"thread-info:%@結(jié)束執(zhí)行任務(wù)%d",[NSThread currentThread],(int)i);
dispatch_semaphore_signal(semaphore);});
});
}
NSLog(@"主線程...!");
說明:從執(zhí)行結(jié)果中可以看出兼都,雖然將10個(gè)任務(wù)都異步加入了并發(fā)隊(duì)列嫂沉,但信號(hào)量機(jī)制控制了最大線程并發(fā)數(shù),始終是3個(gè)線程在執(zhí)行任務(wù)扮碧。此外趟章,這些線程也沒有阻塞線程。
50慎王、函數(shù)式編程當(dāng)中的 first-class function是什么意思呢蚓土?
函數(shù)是一等公民
函數(shù)能像參數(shù)那樣被傳遞到另一個(gè)函數(shù)、從另一個(gè)函數(shù)那像值一樣被返回出來赖淤、函數(shù)可以賦值給變量或者存在數(shù)據(jù)結(jié)構(gòu)中蜀漆。
51.遇到tableView卡頓嘛?會(huì)造成卡頓的原因大致有哪些咱旱?
可能造成tableView卡頓的原因有:
1.最常用的就是cell的重用确丢, 注冊(cè)重用標(biāo)識(shí)符
如果不重用cell時(shí),每當(dāng)一個(gè)cell顯示到屏幕上時(shí)吐限,就會(huì)重新創(chuàng)建一個(gè)新的cell
如果有很多數(shù)據(jù)的時(shí)候鲜侥,就會(huì)堆積很多cell。
如果重用cell诸典,為cell創(chuàng)建一個(gè)ID描函,每當(dāng)需要顯示cell 的時(shí)候,都會(huì)先去緩沖池中尋找可循環(huán)利用的cell,如果沒有再重新創(chuàng)建cell
2.避免cell的重新布局
cell的布局填充等操作 比較耗時(shí)舀寓,一般創(chuàng)建時(shí)就布局好
如可以將cell單獨(dú)放到一個(gè)自定義類胆数,初始化時(shí)就布局好
3.提前計(jì)算并緩存cell的屬性及內(nèi)容
當(dāng)我們創(chuàng)建cell的數(shù)據(jù)源方法時(shí),編譯器并不是先創(chuàng)建cell 再定cell的高度
而是先根據(jù)內(nèi)容一次確定每一個(gè)cell的高度互墓,高度確定后必尼,再創(chuàng)建要顯示的cell,滾動(dòng)時(shí)篡撵,每當(dāng)cell進(jìn)入憑虛都會(huì)計(jì)算高度胰伍,提前估算高度告訴編譯器,編譯器知道高度后酸休,緊接著就會(huì)創(chuàng)建cell骂租,這時(shí)再調(diào)用高度的具體計(jì)算方法,這樣可以方式浪費(fèi)時(shí)間去計(jì)算顯示以外的cell
4.減少cell中控件的數(shù)量
盡量使cell得布局大致相同斑司,不同風(fēng)格的cell可以使用不用的重用標(biāo)識(shí)符渗饮,初始化時(shí)添加控件,
不適用的可以先隱藏
5.不要使用ClearColor宿刮,無背景色互站,透明度也不要設(shè)置為0
渲染耗時(shí)比較長
6.使用局部更新
如果只是更新某組的話,使用reloadSection進(jìn)行局部更
7.加載網(wǎng)絡(luò)數(shù)據(jù)僵缺,下載圖片胡桃,使用異步加載,并緩存
8.少使用addView 給cell動(dòng)態(tài)添加view
9.按需加載cell磕潮,cell滾動(dòng)很快時(shí)翠胰,只加載范圍內(nèi)的cell
10.不要實(shí)現(xiàn)無用的代理方法,tableView只遵守兩個(gè)協(xié)議
11.緩存行高:estimatedHeightForRow不能和HeightForRow里面的layoutIfNeed同時(shí)存在自脯,這兩者同時(shí)存在才會(huì)出現(xiàn)“竄動(dòng)”的bug之景。所以我的建議是:只要是固定行高就寫預(yù)估行高來減少行高調(diào)用次數(shù)提升性能。如果是動(dòng)態(tài)行高就不要寫預(yù)估方法了膏潮,用一個(gè)行高的緩存字典來減少代碼的調(diào)用次數(shù)即可
12.不要做多余的繪制工作锻狗。在實(shí)現(xiàn)drawRect:的時(shí)候,它的rect參數(shù)就是需要繪制的區(qū)域焕参,這個(gè)區(qū)域之外的不需要進(jìn)行繪制轻纪。例如上例中,就可以用CGRectIntersectsRect叠纷、CGRectIntersection或CGRectContainsRect判斷是否需要繪制image和text刻帚,然后再調(diào)用繪制方法。
13.預(yù)渲染圖像讲岁。當(dāng)新的圖像出現(xiàn)時(shí)我擂,仍然會(huì)有短暫的停頓現(xiàn)象。解決的辦法就是在bitmap context里先將其畫一遍缓艳,導(dǎo)出成UIImage對(duì)象校摩,然后再繪制到屏幕;
14.使用正確的數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)數(shù)據(jù)阶淘。
53衙吩、讓你設(shè)計(jì)一種機(jī)制檢測(cè)UIViewController的內(nèi)存泄漏,你會(huì)怎么做溪窒?Instrument是如何檢測(cè)內(nèi)存泄漏的
swizzle NavigationController 的 push 和 pop方法
pop了控制器后過幾秒鐘進(jìn)行一遍判斷坤塞,如果為nil表示已銷毀,沒有則表示內(nèi)存泄露
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakSelf assertNotDealloc];
});
54澈蚌、通過[UIImage imageNamed:]生成的對(duì)象什么時(shí)候被釋放摹芙?
這種圖片加載方式帶有圖片緩存的功能,使用這種方式加載圖片后宛瞄,圖片會(huì)自動(dòng)加入系統(tǒng)緩存中浮禾,并不會(huì)立即釋放到內(nèi)存。一些資源使程序中經(jīng)常使用的圖片資源份汗,
使用這種方式會(huì)加快程序的運(yùn)行減少IO操作盈电,但對(duì)于項(xiàng)目中只用到一次的圖片,如果采用這種方案加載杯活,會(huì)增導(dǎo)致程序的內(nèi)存使用增加匆帚。
非緩存的加載方式
(UIImage *)imageWithContentsOfFile:(NSString *)path
(UIImage *)
:(NSData *)data
55、applicationWillEnterForeground和applicationDidBecomeActive都會(huì)在哪些場(chǎng)景下被調(diào)用旁钧?舉例越多越好吸重。
后臺(tái)進(jìn)入前臺(tái)
通知中心回來
正常啟動(dòng)app
56、如何終止正在運(yùn)行的工作線程歪今?
block 中 return;
[thread cancle]
57晤锹、窮舉iOS下所有的本地持久化方案。
plist
preference NSUserDefault
NSKeyedArchiver
SQLite3
coreData
沙盒
58彤委、項(xiàng)目中網(wǎng)絡(luò)層如何做安全處理
1鞭铆、盡量使用https
https可以過濾掉大部分的安全問題。https在證書申請(qǐng)焦影,服務(wù)器配置车遂,性能優(yōu)化,客戶端配置上都需要投入精力斯辰,所以缺乏安全意識(shí)的開發(fā)人員容易跳過https舶担,或者拖到以后遇到問題再優(yōu)化。https除了性能優(yōu)化麻煩一些以外其他都比想象中的簡(jiǎn)單彬呻,如果沒精力優(yōu)化性能衣陶,至少在注冊(cè)登錄模塊需要啟用https柄瑰,這部分業(yè)務(wù)對(duì)性能要求比較低。
2剪况、不要傳輸明文密碼
不知道現(xiàn)在還有多少app后臺(tái)是明文存儲(chǔ)密碼的教沾。無論客戶端,server還是網(wǎng)絡(luò)傳輸都要避免明文密碼译断,要使用hash值授翻。客戶端不要做任何密碼相關(guān)的存儲(chǔ)孙咪,hash值也不行堪唐。存儲(chǔ)token進(jìn)行下一次的認(rèn)證,而且token需要設(shè)置有效期翎蹈,使用refresh token去申請(qǐng)新的token淮菠。
3、Post并不比Get安全
事實(shí)上荤堪,Post和Get一樣不安全兜材,都是明文。參數(shù)放在QueryString或者Body沒任何安全上的差別逞力。在Http的環(huán)境下曙寡,使用Post或者Get都需要做加密和簽名處理。
4寇荧、不要使用301跳轉(zhuǎn)
301跳轉(zhuǎn)很容易被Http劫持攻擊举庶。移動(dòng)端http使用301比桌面端更危險(xiǎn),用戶看不到瀏覽器地址揩抡,無法察覺到被重定向到了其他地址户侥。如果一定要使用,確保跳轉(zhuǎn)發(fā)生在https的環(huán)境下峦嗤,而且https做了證書綁定校驗(yàn)蕊唐。
5、http請(qǐng)求都帶上MAC
所有客戶端發(fā)出的請(qǐng)求烁设,無論是查詢還是寫操作替梨,都帶上MAC(Message Authentication
Code)。MAC不但能保證請(qǐng)求沒有被篡改(Integrity)装黑,還能保證請(qǐng)求確實(shí)來自你的合法客戶端(Signing)副瀑。當(dāng)然前提是你客戶端的key沒有被泄漏,如何保證客戶端key的安全是另一個(gè)話題恋谭。MAC值的計(jì)算可以簡(jiǎn)單的處理為hash(request
params+key)糠睡。帶上MAC之后,服務(wù)器就可以過濾掉絕大部分的非法請(qǐng)求疚颊。MAC雖然帶有簽名的功能狈孔,和RSA證書的電子簽名方式卻不一樣信认,原因是MAC簽名和簽名驗(yàn)證使用的是同一個(gè)key,而RSA是使用私鑰簽名均抽,公鑰驗(yàn)證嫁赏,MAC的簽名并不具備法律效應(yīng)。
6到忽、http請(qǐng)求使用臨時(shí)密鑰
高延遲的網(wǎng)絡(luò)環(huán)境下,不經(jīng)優(yōu)化https的體驗(yàn)確實(shí)會(huì)明顯不如http清寇。在不具備https條件或?qū)W(wǎng)絡(luò)性能要求較高且缺乏https優(yōu)化經(jīng)驗(yàn)的場(chǎng)景下喘漏,http的流量也應(yīng)該使用AES進(jìn)行加密。AES的密鑰可以由客戶端來臨時(shí)生成华烟,不過這個(gè)臨時(shí)的AES
key需要使用服務(wù)器的公鑰進(jìn)行加密翩迈,確保只有自己的服務(wù)器才能解開這個(gè)請(qǐng)求的信息,當(dāng)然服務(wù)器的response也需要使用同樣的AES
key進(jìn)行加密盔夜。由于http的應(yīng)用場(chǎng)景都是由客戶端發(fā)起负饲,服務(wù)器響應(yīng),所以這種由客戶端單方生成密鑰的方式可以一定程度上便捷的保證通信安全喂链。
7返十、AES使用CBC模式
不要使用ECB模式,記得設(shè)置初始化向量椭微,每個(gè)block加密之前要和上個(gè)block的秘文進(jìn)行運(yùn)算洞坑。
59、假如Controller太臃腫蝇率,如何優(yōu)化迟杂?
1.將網(wǎng)絡(luò)請(qǐng)求抽象到單獨(dú)的類中
方便在基類中處理公共邏輯;
方便在基類中處理緩存邏輯本慕,以及其它一些公共邏輯排拷;
方便做對(duì)象的持久化。
2.將界面的封裝抽象到專門的類中
構(gòu)造專門的 UIView 的子類锅尘,來負(fù)責(zé)這些控件的拼裝监氢。這是最徹底和優(yōu)雅的方式,不過稍微麻煩一些的是藤违,你需要把這些控件的事件回調(diào)先接管忙菠,再都一一暴露回 Controller。
3.構(gòu)造 ViewModel
借鑒MVVM纺弊。具體做法就是將 ViewController 給 View 傳遞數(shù)據(jù)這個(gè)過程牛欢,抽象成構(gòu)造 ViewModel 的過程。
4.專門構(gòu)造存儲(chǔ)類
專門來處理本地?cái)?shù)據(jù)的存取淆游。
5.整合常量
60傍睹、M隔盛、V、C相互通訊規(guī)則你知道的有哪些拾稳?
MVC 是一種設(shè)計(jì)思想吮炕,一種框架模式,是一種把應(yīng)用中所有類組織起來的策略访得,它把你的程序分為三塊龙亲,分別是:
M(Model):實(shí)際上考慮的是“什么”問題,你的程序本質(zhì)上是什么悍抑,獨(dú)立于 UI 工作鳄炉。是程序中用于處理應(yīng)用程序邏輯的部分,通常負(fù)責(zé)存取數(shù)據(jù)搜骡。
C(Controller):控制你 Model 如何呈現(xiàn)在屏幕上拂盯,當(dāng)它需要數(shù)據(jù)的時(shí)候就告訴 Model,你幫我獲取某某數(shù)據(jù)记靡;當(dāng)它需要 UI 展示和更新的時(shí)候就告訴 View谈竿,你幫我生成一個(gè) UI 顯示某某數(shù)據(jù),是 Model 和 View 溝通的橋梁摸吠。
V(View):Controller 的手下空凸,是 Controller 要使用的類,用于構(gòu)建視圖寸痢,通常是根據(jù) Model 來創(chuàng)建視圖的劫恒。
要了解 MVC 如何工作,首先需要了解這三個(gè)模塊間如何通信轿腺。
MVC通信規(guī)則
http://cc.cocimg.com/api/uploads//20171127/1511752329535960.jpg
Controller to Model
可以直接單向通信两嘴。Controller 需要將 Model 呈現(xiàn)給用戶,因此需要知道模型的一切族壳,還需要有同 Model 完全通信的能力憔辫,并且能任意使用 Model 的公共 API。
Controller to View
可以直接單向通信仿荆。Controller 通過 View 來布局用戶界面贰您。
Model to View
永遠(yuǎn)不要直接通信。Model 是獨(dú)立于 UI 的拢操,并不需要和 View 直接通信锦亦,View 通過 Controller 獲取 Model 數(shù)據(jù)
View to Controller
View 不能對(duì) Controller 知道的太多,因此要通過間接的方式通信令境。
Target
action杠园。首先 Controller 會(huì)給自己留一個(gè) target,再把配套的 action 交給 View 作為聯(lián)系方式舔庶。那么 View
接收到某些變化時(shí)抛蚁,View 就會(huì)發(fā)送 action 給 target 從而達(dá)到通知的目的陈醒。這里 View 只需要發(fā)送
action,并不需要知道 Controller 如何去執(zhí)行方法瞧甩。
代理钉跷。有時(shí)候 View 沒有足夠的邏輯去判斷用戶操作是否符合規(guī)范,他會(huì)把判斷這些問題的權(quán)力委托給其他對(duì)象肚逸,他只需獲得答案就行了爷辙,并不會(huì)管是誰給的答案。
DataSoure朦促。View 沒有擁有他們所顯示數(shù)據(jù)的權(quán)力膝晾,View 只能向 Controller 請(qǐng)求數(shù)據(jù)進(jìn)行顯示,Controller 則獲取 Model 的數(shù)據(jù)整理排版后提供給 View思灰。
Model 訪問 Controller
同樣的 Model 是獨(dú)立于 UI 存在的玷犹,因此無法直接與 Controller 通信混滔,但是當(dāng) Model 本身信息發(fā)生了改變的時(shí)候洒疚,會(huì)通過下面的方式進(jìn)行間接通信。
Notification & KVO一種類似電臺(tái)的方法坯屿,Model 信息改變時(shí)會(huì)廣播消息給感興趣的人 油湖,只要 Controller 接收到了這個(gè)廣播的時(shí)候就會(huì)主動(dòng)聯(lián)系 Model,獲取新的數(shù)據(jù)并提供給 View领跛。
從上面的簡(jiǎn)單介紹中我們來簡(jiǎn)單概括一下 MVC 模式的優(yōu)點(diǎn)乏德。
1.低耦合性
2.有利于開發(fā)分工
3.有利于組件重用
4.可維護(hù)性
60、什么是MVVM吠昭,請(qǐng)?jiān)O(shè)計(jì)View model需要考慮哪些喊括?
M + V + VM , VM的作用主要用于簡(jiǎn)化Controller的負(fù)擔(dān),但是VM的設(shè)計(jì)中不可以沒有C矢棚,其實(shí)應(yīng)該是 M + V + C +VM , C 作為 關(guān)聯(lián) V 和 VM 的紐帶郑什, 最好不要直接關(guān)聯(lián)VM。
參考
運(yùn)行時(shí)蒲肋、消息轉(zhuǎn)發(fā)相關(guān)
專題(持續(xù)更新)