iOS基礎知識備忘

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修飾過的?

  1. 在arm64架構(gòu)之前,isa就是一個普通的指針,存儲著Class、Meta-Class對象的內(nèi)存地址
  2. 從arm64架構(gòu)開始,對isa進行了優(yōu)化,變成了一個共用體(union)結(jié)構(gòu),還使用位域來存儲更多的信息
  3. 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
    };
};
isa位域.png

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ū)別
  1. 調(diào)用方式
    1> load是根據(jù)函數(shù)地址直接調(diào)用
    2> initialize是通過objc_msgSend調(diào)用

  2. 調(diào)用時刻
    1> load是runtime加載類、分類的時候調(diào)用(只會調(diào)用一次)
    2> initialize是類第一次接收到消息的時候調(diào)用,每個類只會initialize一次(父類的initialize方法可能會被調(diào)用多次)

調(diào)用順序
  1. 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)用

  2. 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ā)送(當前類、父類中查找方法)
消息發(fā)送階段,查找方法的過程

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

動態(tài)方法解析過程

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)典錯誤。
  • 若方法AforwardingTargetForSelector:的返回值不為nil,就代表轉(zhuǎn)發(fā)成功,這條消息(方法實現(xiàn))由返回值處理:objc_msgSend(返回值, SEL)
  • 若方法BmethodSignatureForSelector:的返回值不為nil,會繼續(xù)調(diào)用forwardInvocation:方法C,開發(fā)者可以在該方法中自定義任何邏輯。
  • 注:以上消息轉(zhuǎn)發(fā)過程涉及到的方法A/B/C,都有對象方法、類方法兩個版本(可以是減號-方法,也可以是加號+方法)
消息轉(zhuǎn)發(fā)過程

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變量捕獲.png
block的類型
  • block有三種類型,可通過class方法或isa指針查看具體類型,最終都是繼承自NSBlock類型:
1、沒有訪問auto變量:             __NSGlobalBlock__ (_NSConcreteGlobalBlock)    
2、訪問了auto變量:               __NSStackBlock__  (_NSConcreteStackBlock)     
3、__NSStackBlock__調(diào)用了copy:  __NSMallocBlock__ (_NSConcreteMallocBlock)    
各類型block在內(nèi)存上的存儲區(qū)域.png
  • 每一種類型的block調(diào)用了copy后的結(jié)果如下:
各類型block調(diào)用copy的結(jié)果.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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

  • 1. load方法和initialize方法 相同點 在不考慮開發(fā)者主動使用的情況下,系統(tǒng)最多會調(diào)用一次 如果父類...
    hahaland閱讀 266評論 1 3
  • Objective-C 1. import的用法 拷貝文件內(nèi)容可以自動防止文件的內(nèi)容被重復拷貝(#define宏定...
    馬文濤閱讀 5,466評論 3 17
  • object-c 的多繼承Objective-C不支持多繼承,由于消息機制名字查找發(fā)生在運行時而非編譯時,很難解決...
    桃逸閱讀 229評論 0 1
  • category 和 extension 的區(qū)別 分類有名字,類擴展沒有分類名字,是一種特殊的分類 分類只能擴展方...
    Andyzhao閱讀 6,938評論 1 58
  • 一、什么是arc?(arc是為了解決什么問題誕生的?) Automatic Reference Counting,...
    2d2383806a31閱讀 314評論 0 1

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