1、weak關(guān)鍵字的作用
weak的作用是弱引用,它修飾的對象在釋放時會置為nil,避免錯誤的內(nèi)存訪問。一般用于delegate、block、NSTimer中,避免循環(huán)引用造成的內(nèi)存泄漏問題。
weak修飾的對象釋放時,weak指針自動置為nil的原理?
runtime維護了一張weak表,存儲了指向某個對象的weak指針地址。weak表其實是一個哈希表,key是指向某個對象的地址,value是指向某個對象的weak指針地址數(shù)組,當該對象被銷毀時,會根據(jù)該對象的地址(key)獲取指向該對象的weak指針數(shù)組,然后遍歷該數(shù)組將weak指針依次置為nil,從weak表中刪除該記錄,最后從引用計數(shù)表中刪除廢棄對象的地址為鍵值的記錄。
系統(tǒng)如何知道哪些對象是被__weak修飾過的?
- 在arm64架構(gòu)之前,isa就是一個普通的指針,存儲著Class、Meta-Class對象的內(nèi)存地址
- 從arm64架構(gòu)開始,對isa進行了優(yōu)化,變成了一個共用體(union)結(jié)構(gòu),還使用位域來存儲更多的信息
- isa的結(jié)構(gòu)中有一個
weakly_referenced的成員變量,該成員變量記錄了對象是否被弱引用指向過。
//isa的底層數(shù)據(jù)結(jié)構(gòu)
union isa_t {
Class cls;
uintptr_t bits;
struct {
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
};
};

2、引用循環(huán)
當兩個不同的對象各有一個強引用指向?qū)Ψ降臅r候,就會造成循環(huán)引用。
NSTimer是如何造成循環(huán)引用的?
在ViewController(簡稱VC)中使用timer屬性時,VC強引用timer,timer的target又是VC,就造成了循環(huán)引用。當你在VC的dealloc方法中調(diào)用timer的invalidate方法來銷毀timer時,會發(fā)現(xiàn)pop出當前VC時,并沒有調(diào)用dealloc方法,VC在等timer釋放后才走dealloc,而timer的釋放在dealloc中,所以就造成了循環(huán)引用。
如何解決NSTimer的循環(huán)引用?
- 蘋果新的api接口解決方案(iOS10.0以上可用)
- 使用NSProxy方案
- 對NSTimer進行封裝
3、OC對象的本質(zhì)
一個NSObject對象占用多少內(nèi)存?
- 系統(tǒng)分配了16個字節(jié)給NSObject對象(通過malloc_size函數(shù)獲得);
- 但NSObject對象內(nèi)部只使用了8個字節(jié)的空間(64bit環(huán)境下,可以通過class_getInstanceSize函數(shù)獲得);
對象的isa指針指向哪里?
- instance對象的isa指向class對象;
- class對象的isa指向meta-class對象;
- meta-class對象的isa指向基類的meta-class對象;
OC的類信息存放在哪里?
- 對象方法、成員變量、屬性、協(xié)議信息存放在class對象中;
- 類方法,存放在meta-class對象中;
- 成員變量的具體值,存放在instance對象中;
4、TCP擁堵、TCP丟包問題
5、https配置流程
http與https的區(qū)別?
1、https需要到ca申請證書,一般免費證書很少,需要交費;
2、http是超文本傳輸協(xié)議,信息是明文傳輸,https是具有安全性的SSL加密傳輸協(xié)議;
3、http和https的連接方式不同,用的端口也不同,前者是80,后者是443;
4、http的連接很簡單,是無狀態(tài)的;https協(xié)議是由SSL+HTTP協(xié)議構(gòu)建的可進行加密傳輸、身份認證的網(wǎng)絡協(xié)議,比http協(xié)議安全;
iOS配置流程?
項目中網(wǎng)絡交互基于AFN,要求AFN版本在3.0以上。代碼部分:
// 設置AFN請求管理者的時候,添加SSL認證:
// 1.獲得請求管理者
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 2.加上這個函數(shù),https ssl 驗證。
[manager setSecurityPolicy:[self customSecurityPolicy]];
// https ssl 驗證函數(shù)
- (AFSecurityPolicy *)customSecurityPolicy
{
// 先導入證書
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"xxx" ofType:@"cer"];//證書的路徑
NSData *cerData = [NSData dataWithContentsOfFile:cerPath];
// AFSSLPinningModeCertificate 使用證書驗證模式
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
securityPolicy.pinnedCertificates = [NSSet setWithObject:cerData];
// allowInvalidCertificates 是否允許無效證書(也就是自建的證書),默認為NO
securityPolicy.allowInvalidCertificates = NO;
//validatesDomainName 是否需要驗證域名,默認為YES;
securityPolicy.validatesDomainName = YES;
return securityPolicy;
}
// 3.關(guān)于證書
從沃通獲取到HTTPS證書后,會得到一個有密碼的壓縮包文件,使用for other server里面的domain.crt的證書文件。
6、atomic屬性作用?
atomic修飾的對象,setter和getter方法是線程安全的(因為在setter和getter賦值取值的時候添加了自旋鎖),但不能保證整個對象的線程安全。
為什么說atomic沒辦法保證整個對象的線程安全?
1.對于NSArray類型 @property(atomic)NSArray *array我們用atomic修飾,數(shù)組的添加和刪除并不是線程安全的,這是因為數(shù)組比較特殊,我們要分成兩部分考慮,一部分是&array也就是這個數(shù)組本身,另一部分是他所指向的內(nèi)存部分。atomic限制的只是&array部分,對于它指向的對象沒有任何限制。
atomic表示,我TM也很冤?。。。?!
2.當線程A進行寫操作,這時其他線程的讀或者寫操作會因為該操作而等待。當A線程的寫操作結(jié)束后,B線程進行寫操作,然后當A線程需要讀操作時,卻獲得了在B線程中的值,這就破壞了線程安全,如果有線程C在A線程讀操作前release了該屬性,那么還會導致程序崩潰。所以僅僅使用atomic并不會使得線程安全,我們還要為線程添加lock來確保線程的安全。
個人覺得這個就有點杠精的意味了,atomic還要管到你方法外面去了?????不過面試人家問你還要這么答,要嚴謹!!,
7、iOS用什么方式實現(xiàn)對一個對象的KVO?(KVO的本質(zhì)是什么?)
- 利用runtime API動態(tài)生成一個子類,并且讓instance對象的isa指向這個全新的子類;
- 當修改instance對象的屬性時,會調(diào)用Foundation的
_NSSetxxxValueAndNotify函數(shù); -
_NSSetxxxValueAndNotify內(nèi)部會調(diào)用:
willChangeValueForKey:
父類原來的setter方法
didChangeValueForKey: -
didChangeValueForKey:方法內(nèi)部會觸發(fā)監(jiān)聽器(Observer)的監(jiān)聽方法observeValueForKeyPath: ofObject: change: context:
如何手動觸發(fā)KVO?
- 手動調(diào)用
willChangeValueForKey:和didChangeValueForKey:方法
直接修改成員變量會觸發(fā)KVO嗎?
- 不會觸發(fā)KVO
8、通過KVC修改屬性會觸發(fā)KVO嗎?
- 會觸發(fā)KVO
KVC的賦值和取值過程是怎樣的?原理是什么?
賦值過程即[setValue: forKey:] 方法實現(xiàn)原理:
- 按照
setKey:、_setKey:順序查找方法 - a> 找到了方法,傳遞參數(shù)調(diào)用方法
- b> 沒找到方法,查看
+(BOOL)accessInstanceVariablesDirectly方法的返回值 - 返回值為YES,按照
_key、_isKey、key、isKey順序來查找成員變量
找到成員變量直接賦值
找不到成員變量調(diào)用setValue: forUndefinedKey:方法,并拋出異常NSUnknownKeyException - 返回值為NO,調(diào)用
setValue: forUndefinedKey:方法,并拋出異常NSUnknownKeyException -
+(BOOL)accessInstanceVariablesDirectly方法的默認返回值是YES
取值過程:
- 首先按照
getKey、key、isKey、_key順序查找方法 - a>找到了方法,調(diào)用方法取值
- b>未找到方法,查看
+(BOOL)accessInstanceVariablesDirectly方法的返回值 - 返回值為YES,按照
_key、_isKey、key、isKey順序查找成員變量
找到成員變量,就返回該成員變量的值
未找到成員變量,調(diào)用valueForUndefinedKey:方法,并拋出異常NSUnknownKeyException - 返回值為NO,調(diào)用
valueForUndefinedKey:方法,并拋出異常NSUnknownKeyException
9、Category的實現(xiàn)原理?
- Category編譯后的底層結(jié)構(gòu)是
struct category_t,里面存儲著分類的對象方法、類方法、屬性、協(xié)議信息 - 在程序運行的時候,runtime會將Category的數(shù)據(jù),合并到類信息中(類對象、元類對象中)
Category的加載處理過程?
- 通過runtime加載某個類的所有Category數(shù)據(jù)
- 把所有Category的方法、屬性、協(xié)議數(shù)據(jù),合并到一個大數(shù)組中
后參與編譯的Category數(shù)據(jù),會在數(shù)組的前面 - 將合并后的分類數(shù)據(jù)(方法、屬性、協(xié)議),插入到類原來數(shù)據(jù)的前面
Category和Class Extension的區(qū)別是什么?
- Class Extension在編譯的時候,它的數(shù)據(jù)就已經(jīng)包含在類信息中
- Category是在運行時,才會將數(shù)據(jù)合并到類信息中
Category中有l(wèi)oad方法嗎?load方法在什么時候調(diào)用的?load方法能繼承嗎?
- 有l(wèi)oad方法
- load方法在runtime加載類、分類的時候調(diào)用
- 可以繼承,但是一般情況下不會主動去調(diào)用load方法,都是讓系統(tǒng)自動調(diào)用
load、initialize方法的區(qū)別是什么?它們在Category中的調(diào)用順序?以及出現(xiàn)繼承時它們之間的調(diào)用過程?
區(qū)別
調(diào)用方式
1> load是根據(jù)函數(shù)地址直接調(diào)用
2> initialize是通過objc_msgSend調(diào)用調(diào)用時刻
1> load是runtime加載類、分類的時候調(diào)用(只會調(diào)用一次)
2> initialize是類第一次接收到消息的時候調(diào)用,每個類只會initialize一次(父類的initialize方法可能會被調(diào)用多次)
調(diào)用順序
-
load
1> 先調(diào)用類的load
a) 先編譯的類,優(yōu)先調(diào)用
b) 調(diào)用子類的load方法之前,會先調(diào)用父類的load方法2> 再調(diào)用分類的load
a) 先編譯的分類,優(yōu)先調(diào)用 initialize
1> 先初始化父類
2> 再初始化子類(可能最終調(diào)用的是父類的initialize方法)
3> 如果子類沒有實現(xiàn)initialize方法,會調(diào)用父類的initialize,所以父類的initialize可能會被調(diào)用多次
4> 如果分類實現(xiàn)了initialize方法,就覆蓋類本身的initialize調(diào)用
Category能否添加成員變量?如果可以,如何添加?
- 不能直接給Category添加成員變量,但可以通過runtime的API間接實現(xiàn)Category有成員變量的效果。
- 添加關(guān)聯(lián)對象:
void objc_setAssociatedObject(id object, const void * key, id value, objc_AssociationPolicy policy) - 獲得關(guān)聯(lián)對象:
id objc_getAssociatedObject(id object, const void * key) - 移除所有關(guān)聯(lián)對象:
void objc_removeAssociatedObjects(id object)
10、講一下OC的消息機制?
- OC的方法調(diào)用其實都會轉(zhuǎn)成
objc_msgSend函數(shù)的調(diào)用,給receiver(方法調(diào)用者)發(fā)送了一條消息(方法名)。 -
objc_msgSend函數(shù)底層有三大階段:
1>消息發(fā)送(當前類、父類中查找方法)

2>動態(tài)方法解析(消息發(fā)送階段未找到方法實現(xiàn),開發(fā)者可以在這個階段實現(xiàn)+resolveInstanceMethod:或+resolveClassMethod:方法來動態(tài)添加方法實現(xiàn)。動態(tài)解析過后,會重新走 消息發(fā)送 的流程,并且是直接“從receiverClass的cache中查找方法”這一步開始執(zhí)行)

3>消息轉(zhuǎn)發(fā)(如果在 動態(tài)方法解析 過程沒有做動態(tài)添加方法實現(xiàn)的處理,那么程序會進入消息轉(zhuǎn)發(fā)過程)
- 首先會調(diào)用
forwardingTargetForSelector:方法A,返回值為nil,會繼續(xù)調(diào)用methodSignatureForSelector:方法B,若返回值為nil,程序會報錯,拋出找不到方法選擇器的經(jīng)典錯誤。 - 若方法A
forwardingTargetForSelector:的返回值不為nil,就代表轉(zhuǎn)發(fā)成功,這條消息(方法實現(xiàn))由返回值處理:objc_msgSend(返回值, SEL) - 若方法B
methodSignatureForSelector:的返回值不為nil,會繼續(xù)調(diào)用forwardInvocation:方法C,開發(fā)者可以在該方法中自定義任何邏輯。 - 注:以上消息轉(zhuǎn)發(fā)過程涉及到的方法A/B/C,都有對象方法、類方法兩個版本(可以是減號-方法,也可以是加號+方法)

11、對runtime的理解?在項目中的應用場景有哪些?
- 簡單來說,OC是一門動態(tài)性比較強的編程語言,允許很多操作推遲到程序運行時再進行。
- OC的動態(tài)性就是由
runtime來支撐和實現(xiàn)的,runtime是一套C語言的API,封裝了很多動態(tài)性相關(guān)的函數(shù)。 - 平時編寫的OC代碼,底層都是轉(zhuǎn)換成了
runtimeAPI進行調(diào)用。
應用場景
- 利用關(guān)聯(lián)對象(
objc_setAssociatedObject)給分類添加屬性。 - 遍歷類的所有成員變量(修改文本框占位文字顏色、字典轉(zhuǎn)模型、自動歸解檔)
- 交換方法實現(xiàn)(交換系統(tǒng)的方法)
- 利用消息轉(zhuǎn)發(fā)機制解決方法找不到的異常問題
12、block的本質(zhì)?
-
block本質(zhì)也是一個OC對象,它內(nèi)部也有一個isa指針。 -
block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對象。
block的變量捕獲機制
- 為了保證block內(nèi)部能正常訪問外部的變量,block有個變量捕獲機制:

block的類型
-
block有三種類型,可通過class方法或isa指針查看具體類型,最終都是繼承自NSBlock類型:
1、沒有訪問auto變量: __NSGlobalBlock__ (_NSConcreteGlobalBlock)
2、訪問了auto變量: __NSStackBlock__ (_NSConcreteStackBlock)
3、__NSStackBlock__調(diào)用了copy: __NSMallocBlock__ (_NSConcreteMallocBlock)

- 每一種類型的
block調(diào)用了copy后的結(jié)果如下:
