iOS面試資料

iOS面試題

[toc]

一精算、設(shè)計(jì)基本原則

簡(jiǎn)述六大設(shè)計(jì)基本原則(也稱(chēng) SOLID 五大原則)

單一職責(zé)原則 (SRP, Single Responsibility Principle)

  • 定義: 一個(gè)類(lèi)只負(fù)責(zé)一件事。
  • 優(yōu)點(diǎn): 類(lèi)的復(fù)雜度降低、可讀性增強(qiáng)、易維護(hù)甚亭、變更引起的風(fēng)險(xiǎn)降低。
  • 應(yīng)用: 系統(tǒng)提供的UIView和CALayer的關(guān)系:UIView負(fù)責(zé)時(shí)間傳遞、事件響應(yīng)婆瓜;CALayer負(fù)責(zé)動(dòng)畫(huà)及展示

開(kāi)閉原則(OCP, Open-Close Principle)

  • 定義: 對(duì)修改關(guān)閉、對(duì)擴(kuò)展開(kāi)放.

    • 設(shè)計(jì)的類(lèi)做好后就不再修改贡羔,如果有新的需求廉白,通過(guò)新加類(lèi)的方式來(lái)滿足个初,而不去修改現(xiàn)有的類(lèi)的代碼.
  • 優(yōu)點(diǎn): 靈活、穩(wěn)定(不需修改內(nèi)部代碼蒙秒,使得被破壞的程度大大下降)

  • 關(guān)鍵: 抽象化

  • 使用:

    • 我們可以把把行為添加到一個(gè)協(xié)議中勃黍,使用時(shí)遵守這個(gè)協(xié)議即可。
    • 添加類(lèi)目(Category)方式創(chuàng)建

里氏替換原則 (LSP,Liskov Substitution Principle)

  • 定義: 所有引用父類(lèi)的地方必須能透明地使用其子類(lèi)的對(duì)象晕讲。

    • 通俗點(diǎn)說(shuō)就是,父類(lèi)可以被子類(lèi)無(wú)縫替換马澈,且原有功能不受任何影響
  • 優(yōu)點(diǎn):

    • 代碼共享瓢省,減少創(chuàng)建類(lèi)的工作量,每個(gè)子類(lèi)都擁有父類(lèi)的所有屬性和方法
    • 提高代碼的可重用性痊班、擴(kuò)張性勤婚,項(xiàng)目的開(kāi)放性
  • 缺點(diǎn): 程序的可移植性降低,增加了對(duì)象間的耦合性

接口隔離原則(ISP, Interface Segregation Principle)

  • 定義: 客戶端不應(yīng)該依賴它不需要的接口

    • 使用多個(gè)專(zhuān)門(mén)的協(xié)議涤伐、而不是一個(gè)龐大臃腫的協(xié)議馒胆。
    • 協(xié)議中的方法應(yīng)當(dāng)盡量少
  • 例: UITableViewDataSource、UITableViewDelegate

  • 優(yōu)點(diǎn): 解耦凝果、增強(qiáng)可讀性祝迂、可擴(kuò)展性、可維護(hù)性

依賴倒置原則(DIP, Dependence Inversion Principle)

  • 定義: 抽象不應(yīng)該依賴于具體實(shí)現(xiàn)器净,具體實(shí)現(xiàn)可以依賴于抽象

  • 核心思想: 面向接口編程

  • 優(yōu)點(diǎn): 代碼結(jié)構(gòu)清晰型雳,維護(hù)容易

  • 實(shí)例: 平時(shí)我們使用 protocol 匿名對(duì)象模式就是依賴倒置原則的最好體現(xiàn)

迪米特法則(LOD, Law Of Demeter) / 最小知道原則 (LKP,Least Knowledge Principle)

  • 定義: 一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象有盡可能少的了解山害。

    • 也就是說(shuō)纠俭,如果兩個(gè)類(lèi)不必彼此直接通信,那么這兩個(gè)類(lèi)就不應(yīng)當(dāng)發(fā)生直接的相互作用浪慌。
  • 迪米特法則應(yīng)用:

    • 外觀模式(Facade)
    • 中介者模式(Mediator)
    • 匿名對(duì)象
  • 優(yōu)點(diǎn): 使對(duì)象之間的耦合降到最底冤荆,從而使得類(lèi)具有很好的可讀性和可維護(hù)性。

設(shè)計(jì)基本原則特點(diǎn)總結(jié)

  • 單一職責(zé)原則主要說(shuō)明:類(lèi)的職責(zé)要單一
  • 開(kāi)閉原則講述的是:對(duì)擴(kuò)展開(kāi)放权纤,對(duì)修改關(guān)閉
  • 里氏替換原則強(qiáng)調(diào):不要破壞繼承體系
  • 接口隔離原則講解:設(shè)計(jì)接口的時(shí)候要精簡(jiǎn)
  • 依賴倒置原則描述要:面向接口編程
  • 迪米特法則告訴我們:要降低耦合

面向?qū)ο蟮娜筇卣?/h4>
  • 面向?qū)ο缶幊趟枷胫饕腥筇卣鞯黾颍謩e是:封裝,繼承 和 多態(tài)妖碉。
    • 封裝 是指把類(lèi)中的細(xì)節(jié)進(jìn)行包裝涌庭,對(duì)外提供定義好的接口。封裝對(duì)實(shí)現(xiàn)細(xì)節(jié)進(jìn)行隱藏欧宜,使用者需要通過(guò)規(guī)定的訪問(wèn)來(lái)訪問(wèn)數(shù)據(jù)坐榆,這樣避免了使用者進(jìn)行不合理的賦值操作。
    • 繼承 是使用已存在的類(lèi)定義作為基礎(chǔ)建立新類(lèi)的技術(shù)冗茸,新類(lèi)的定義可增加新的數(shù)據(jù)或新的功能席镀,也可以用父類(lèi)的功能匹中,但不能選擇性的繼承父類(lèi)。在繼承中豪诲,子類(lèi)擁有父類(lèi)非 private 的屬性和方法顶捷;子類(lèi)可以擁有自己的屬性和方法,即子類(lèi)可以對(duì)父類(lèi)進(jìn)行擴(kuò)展屎篱;子類(lèi)可以用自己的方式實(shí)現(xiàn)父類(lèi)的方法服赎。繼承使得系統(tǒng)在變化中有了延續(xù)性,同時(shí)繼承也是封裝過(guò)程中可變的因素交播,通過(guò)繼承還可以縮小代碼量重虑。
    • 多態(tài) 是指允許不同的子類(lèi)類(lèi)型對(duì)同一消息做出不同的行為。多態(tài)可以大量減少代碼量的同時(shí)秦士,提高代碼的維護(hù)性和擴(kuò)展性缺厉。

什么是 MVC 設(shè)計(jì)模式?

  • 模型-視圖-控制器(Model-View-Controller隧土,MVC)是一種廣泛應(yīng)用于用戶交互應(yīng)用程序中的軟件設(shè)計(jì)模式提针。iOS 中的 MVC 將軟件系統(tǒng)分為 Model、View曹傀、Controller 三部分辐脖,其中
    • Model 對(duì)象 封裝了應(yīng)用程序的數(shù)據(jù),并定義操控和處理該數(shù)據(jù)的邏輯和運(yùn)算卖毁;
    • View 對(duì)象 是應(yīng)用程序中用戶可以看見(jiàn)的對(duì)象揖曾,其主要目的是顯示來(lái)自應(yīng)用程序 Model 對(duì)象的數(shù)據(jù),并使該數(shù)據(jù)可被編輯亥啦;
    • Controller 對(duì)象 在應(yīng)用程序的一個(gè)或多個(gè) View 對(duì)象和一個(gè)或多個(gè) Model 對(duì)象之間充當(dāng)媒介炭剪,Controller 可以直接訪問(wèn) Model,Model 通過(guò) Notification 和 KVO 機(jī)制與 Controller 間接通信翔脱;Controller 也可以直接控制View奴拦,View 通過(guò) action 向 Controller 報(bào)告事件的發(fā)生,但 Model 和 View 不能互相通信届吁。

什么是 MVVM错妖?主要目的是什么?有哪些優(yōu)點(diǎn)疚沐?

  • MVVM暂氯,為 Model-View-ViewModel 的簡(jiǎn)寫(xiě),本質(zhì)是 MVC 設(shè)計(jì)模式的改進(jìn)版亮蛔,將視圖處理邏輯從 C 中剝離出來(lái)給 V痴施,剩下的業(yè)務(wù)邏輯部分被稱(chēng)做 View-Model。MVVM 模式和 MVC 模式一樣,主要目的是分離視圖和模型辣吃,其優(yōu)點(diǎn)如下:
    • 低耦合:View 可以獨(dú)立于 Model 變化和修改动遭,一個(gè) ViewModel 可以綁定到不同的 View 上,當(dāng) View 變化的時(shí)候神得,Model 可以不變厘惦,當(dāng) Model 變化的時(shí)候 View 也可以不變;
    • 可重用性:如果把一些視圖邏輯放在一個(gè) ViewModel 里面哩簿,則很多 view 可重用這段視圖邏輯宵蕉;
    • 獨(dú)立開(kāi)發(fā):開(kāi)發(fā)人員可以專(zhuān)注于業(yè)務(wù)邏輯和數(shù)據(jù)的開(kāi)發(fā)(ViewModel),設(shè)計(jì)人員可以專(zhuān)注于頁(yè)面設(shè)計(jì)节榜;
    • 可測(cè)試:界面素來(lái)是較難于測(cè)試的国裳,而現(xiàn)在測(cè)試可以針對(duì) ViewModel 來(lái)寫(xiě)。

二全跨、內(nèi)存管理

  • 規(guī)則
    • 在iOS中,使用 “引用計(jì)數(shù)” 來(lái)管理OC對(duì)象的內(nèi)存
    • 新創(chuàng)建的OC對(duì)象亿遂,引用計(jì)數(shù)是1浓若;
    • 調(diào)用retain會(huì)讓OC對(duì)象的引用計(jì)數(shù)+1,調(diào)用release會(huì)讓OC對(duì)象的引用計(jì)數(shù)-1
    • 當(dāng)引用計(jì)數(shù)減為0蛇数,OC對(duì)象就會(huì)銷(xiāo)毀挪钓,釋放占用的內(nèi)存空間
    • 當(dāng)調(diào)用 alloc、new耳舅、copy碌上、mutableCopy 方法返回了一個(gè)對(duì)象,在不需要這個(gè)對(duì)象時(shí)浦徊,要調(diào)用
    • release或者aoturelease釋放

引用計(jì)數(shù)怎么存儲(chǔ)馏予?

  • 可以直接存儲(chǔ)在isa指針中
  • 如果不夠存儲(chǔ)的話后雷,會(huì)存儲(chǔ)在SideTable結(jié)構(gòu)體的refcnts散列表中
struct SideTable {
    spinlock_t stock;
    RefcountMap refcnts; // 存放著對(duì)象引用計(jì)數(shù)的散列表
    weak_table_t weak_table;
}

ARC具體為引用計(jì)數(shù)做了哪些工作召川?

  • 編譯階段自動(dòng)添加代碼
  • ARC是LLVM編譯器和Runtime系統(tǒng)相互協(xié)作的一個(gè)結(jié)果
    • 編譯器幫我們實(shí)現(xiàn)內(nèi)存管理相關(guān)的代碼
    • Runtime在程序運(yùn)行過(guò)程中處理弱引用

深拷貝與淺拷貝

  • 概念

    • 深拷貝:內(nèi)容拷貝,產(chǎn)生新的對(duì)象
    • 淺拷貝:指針拷貝湃鹊,沒(méi)有產(chǎn)生新的對(duì)象冕香,原對(duì)象的引用計(jì)數(shù)+1
    • 完全拷貝:深拷貝的一種蛹尝,能拷貝多層內(nèi)容(使用歸解檔技術(shù))
  • 執(zhí)行結(jié)果

    • copy:不可變拷貝,產(chǎn)生不可變副本
    • mutableCopy:可變拷貝悉尾,產(chǎn)生可變副本
  • 準(zhǔn)則:不可變對(duì)象的copy方法是淺拷貝突那,其余都是深拷貝??????????

    • 原因
      • 它是不可變對(duì)象,沒(méi)有必要拷貝一份出來(lái)构眯,指向同一塊地址還節(jié)省內(nèi)存
      • 不可變對(duì)象調(diào)用copy返回他本身愕难,不可變對(duì)象copy就相當(dāng)于是retain

對(duì)象的拷貝

  • 遵守協(xié)議(<NSCopying, NSMutableCopying>)
  • 實(shí)現(xiàn)協(xié)議方法
- (id)copyWithZone:(NSZone *)zone {
    Person *person = [Person allocWithZone:zone];
    person.name = self.name;
    return person;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
    Person *person = [Person allocWithZone:zone];
    person.name = self.name;
    return person;
}

集合對(duì)象的拷貝

  • 對(duì)于集合類(lèi)的可變對(duì)象來(lái)說(shuō),深拷貝并非嚴(yán)格意義上的深復(fù)制,只能算是單層深復(fù)制
  • 即雖然新開(kāi)辟了內(nèi)存地址务漩,但是存放在內(nèi)存上的值(也就是數(shù)組里的元素仍然之鄉(xiāng)員數(shù)組元素值拄衰,并沒(méi)有另外復(fù)制一份),這就叫做單層深復(fù)制
  • 對(duì)于集合類(lèi)的對(duì)象如何實(shí)現(xiàn)每一層都深拷貝呢饵骨?initWithArray:copyItems2翘悉、歸檔解檔技術(shù)
#import <Foundation/Foundation.h>

@interface Person : NSObject <NSCoding>

@property (nonatomic, copy) NSString *name;

@end


#import "Person.h"

@implementation Person

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self.name = [aDecoder decodeObjectForKey:@"name"];
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:self.name forKey:@"name"];
}

@end

自動(dòng)釋放池

  • 底層結(jié)構(gòu):AutoreleasPool是通過(guò)以AutoreleasePoolPage為結(jié)點(diǎn)的 “雙向鏈表” 來(lái)實(shí)現(xiàn)的
  • AutoreleasPool運(yùn)行的三個(gè)過(guò)程
    • objc_autoreleasePoolPush()
    • [objc autorelease]
    • objc_autoreleasePoolPop(void *)

屬性修飾詞:Copy、Strong居触、Weak妖混、Assign

  • copy

    • 會(huì)在內(nèi)存里拷貝一份對(duì)象,兩個(gè)指針指向不同的內(nèi)存地址轮洋。
    • 一般用來(lái)修飾NSString等有對(duì)應(yīng)可變類(lèi)型的對(duì)象制市,因?yàn)樗麄冇锌赡芎蛯?duì)應(yīng)的可變類(lèi)型(NSMutableString)之間進(jìn)行賦值操作,為確北子瑁可變對(duì)象變化時(shí)祥楣,對(duì)象中的字符串不被修改 ,應(yīng)該在設(shè)置屬性時(shí)拷貝一份汉柒。
    • 而若用strong修飾误褪,如果可變對(duì)象變化,對(duì)象中的字符串屬性也會(huì)跟著變化碾褂。
  • strong:

    • ARC下的strong等同于MRC下的retain都會(huì)把對(duì)象引用計(jì)數(shù)加1
  • weak

    • 修飾Object類(lèi)型兽间,修飾的對(duì)象在釋放后,指針地址會(huì)被置為nil正塌,是一種弱引用
    • 在ARC環(huán)境下嘀略,為避免循環(huán)引用,往往會(huì)把delegate屬性用weak修飾
    • weak和strong不同的是:當(dāng)一個(gè)對(duì)象不再有strong類(lèi)型的指針指向它的時(shí)候乓诽,它就會(huì)被釋放帜羊,即使還有weak型指針指向它,那么這些weak型指針也將被清除问裕。
  • assign

    • 用于對(duì)基本數(shù)據(jù)類(lèi)型進(jìn)行賦值操作逮壁,不更改引用計(jì)數(shù)
    • 也可以用來(lái)修飾對(duì)象,但是被assign修飾的對(duì)象在釋放后粮宛,指針的地址還是存在的窥淆,指針并沒(méi)有被置為nil,成為野指針?
    • 之所以可以修飾基本數(shù)據(jù)類(lèi)型巍杈,因?yàn)榛緮?shù)據(jù)類(lèi)型一般分配在棧上忧饭,棧的內(nèi)存會(huì)由系統(tǒng)自動(dòng)處理,不會(huì)造成野指針筷畦。

block屬性為什么需要用copy來(lái)修飾词裤?

  • 因?yàn)樵贛RC下刺洒,block在創(chuàng)建的時(shí)候,它的內(nèi)存是分配在棧(stack)上的吼砂,而不是在堆(heap)上逆航,可能被隨時(shí)回收。
  • 他本身的作于域是屬于創(chuàng)建時(shí)候的作用域渔肩,一旦在創(chuàng)建時(shí)候的作用域外面調(diào)用block將導(dǎo)致程序崩潰因俐。
  • 通過(guò)copy可以把block拷貝(copy)到堆,保證block的聲明域外使用周偎。
  • 在ARC下寫(xiě)不寫(xiě)都行抹剩,編譯器會(huì)自動(dòng)對(duì)block進(jìn)行copy操作。

代理為什么使用weak修飾蓉坎?

  • weak指明該對(duì)象并不負(fù)責(zé)保持delegate這個(gè)對(duì)象澳眷,delegate的銷(xiāo)毀由外部控制;
  • 如果用strong修飾蛉艾,強(qiáng)引用后外界不能銷(xiāo)毀delegate對(duì)象钳踊,會(huì)導(dǎo)致循環(huán)引用;

為什么NSMutableArray一般不用copy修飾勿侯?

- (void)setData:(NSMutableArray *)data {
    if (_data != data) {
        [_data release];
        _data = [data copy];
    }
}

拷貝完成后:可變數(shù)組->不可變數(shù)組箍土,在外操作時(shí)(添加、刪除等)會(huì)存在問(wèn)題

什么是“僵尸對(duì)象”?

  • 一個(gè)OC對(duì)象引用計(jì)數(shù)為0被釋放后就變成僵尸對(duì)象罐监,僵尸對(duì)象的內(nèi)存已經(jīng)被系統(tǒng)回收
  • 雖然可能該對(duì)象還存在,數(shù)據(jù)依然在內(nèi)存中瞒爬,但僵尸對(duì)象已經(jīng)是不穩(wěn)定對(duì)象了弓柱,不可以再訪問(wèn)或者使用
  • 它的內(nèi)存是隨時(shí)可能被別的對(duì)象申請(qǐng)而占用的

有哪些情況會(huì)出現(xiàn)內(nèi)存泄漏

  • block循環(huán)引用
  • delegate循環(huán)引用問(wèn)題
  • NSTimer循環(huán)引用
  • 地圖類(lèi)處理
  • 線程保活target:self

- (void)dealloc底層執(zhí)行了什么侧但?

- (void)dealloc {
    _objc_rootDealloc(self);
}


void _objc_rootDealloc(id obj) {
    ASSERT(obj);
    obj->rootDealloc();
}


inline void objc_object::rootDealloc() {
    if (isTaggedPointer()) return;  // fixme necessary?

    if (fastpath(isa.nonpointer  &&          // 無(wú)優(yōu)化過(guò)isa指針
                 !isa.weakly_referenced  &&  // 無(wú)弱引用
                 !isa.has_assoc  &&          // 無(wú)關(guān)聯(lián)對(duì)象
                 !isa.has_cxx_dtor  &&       // 無(wú)cxx析構(gòu)函數(shù)
                 !isa.has_sidetable_rc)) {   // 不存在引用計(jì)數(shù)器是否過(guò)大無(wú)法存儲(chǔ)在isa中(使用 sidetable 來(lái)存儲(chǔ)引用計(jì)數(shù))
        // 直接釋放
        assert(!sidetable_present());
        free(this);
    } else {
        // 下一步
        object_dispose((id)this);
    }
}


// 如果不能快速釋放矢空,則調(diào)用 object_dispose()方法,做下一步的處理
static id _object_dispose(id anObject) {
    if (anObject==nil) return nil;

    objc_destructInstance(anObject);
    
    anObject->initIsa(_objc_getFreedObjectClass ());

    free(anObject);
    return nil;
}


void *objc_destructInstance(id obj) {
    if (obj) {
        // Read all of the flags at once for performance.
        bool cxx = obj->hasCxxDtor();               // 是否存在析構(gòu)函數(shù)
        bool assoc = obj->hasAssociatedObjects();   // 是否有關(guān)聯(lián)對(duì)象

        // This order is important.
        if (cxx) object_cxxDestruct(obj);           // 銷(xiāo)毀成員變量
        if (assoc) _object_remove_assocations(obj); // 釋放動(dòng)態(tài)綁定的對(duì)象
        obj->clearDeallocating();
    }
    return obj;
}


/*
 * clearDeallocating一共做了兩件事
 *
 * 將對(duì)象弱引用表清空禀横,即將弱引用該對(duì)象的指針置為nil
 * 2屁药、清空引用計(jì)數(shù)表
 * - 當(dāng)一個(gè)對(duì)象的引用計(jì)數(shù)值過(guò)大(超過(guò)255)時(shí),引用計(jì)數(shù)會(huì)存儲(chǔ)在一個(gè)叫 SideTable 的屬性中
 * - 此時(shí)isa的 has_sidetable_rc 值為1柏锄,這就是為什么弱引用不會(huì)導(dǎo)致循環(huán)引用的原因
 */
inline void  objc_object::clearDeallocating() {
    if (slowpath(!isa.nonpointer)) {
        // Slow path for raw pointer isa.
        sidetable_clearDeallocating();
    }
    else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) {
        // Slow path for non-pointer isa with weak refs and/or side table data.
        clearDeallocating_slow();
    }
    assert(!sidetable_present());
}

三酿箭、多線程

GCD

GCD核心概念:「任務(wù)」、「隊(duì)列」

  • 任務(wù)
    概念:指操作趾娃,線程中執(zhí)行的那段代碼缭嫡,GCD主要放在block中;
    執(zhí)行任務(wù)的方式:「同步執(zhí)行」抬闷、「異步執(zhí)行」妇蛀;
    區(qū)別:是否等待隊(duì)列的任務(wù)執(zhí)行結(jié)束耕突,是否具備開(kāi)啟新縣城的能力;

    • 同步執(zhí)行(sync)

      • 同步添加任務(wù)到指定隊(duì)列中评架,在添加的任務(wù)執(zhí)行結(jié)束之前眷茁,會(huì)一直等待,直到隊(duì)列里面的任務(wù)完成之后再繼續(xù)執(zhí)行
      • 只能在當(dāng)前線程中執(zhí)行任務(wù)纵诞,不具備開(kāi)啟新線程的能力
    • 異步執(zhí)行(async)

      • 異步添加任務(wù)到指定隊(duì)列中上祈,不會(huì)做任何等待,可以繼續(xù)執(zhí)行任務(wù)
      • 可以在新的線程中執(zhí)行任務(wù)挣磨,具備開(kāi)啟新縣城的能力
      • 異步執(zhí)行雖然具有開(kāi)啟新線程的能力雇逞,但不一定開(kāi)啟新線程。(與任務(wù)指定的隊(duì)列類(lèi)型有關(guān))
  • 隊(duì)列(Dispatch Queue)
    概念:執(zhí)行任務(wù)的等待隊(duì)列茁裙,即用來(lái)存放任務(wù)的隊(duì)列
    結(jié)構(gòu):特殊的線性表塘砸,采用FIFO(先進(jìn)先出)原則。即每讀取一個(gè)任務(wù)晤锥,則從隊(duì)列中釋放一個(gè)任務(wù)

    • 串行隊(duì)列:(Serial Dispatch Queue)

      • 每次只有一個(gè)任務(wù)被執(zhí)行掉蔬,任務(wù)依次執(zhí)行(只開(kāi)啟一個(gè)線程,一個(gè)任務(wù)執(zhí)行完成后矾瘾,再執(zhí)行下一個(gè)任務(wù))
    • 并發(fā)隊(duì)列:(Concurrent Dispatch Queue)

      • 可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行女轿。(可以開(kāi)啟多個(gè)線程,并且同時(shí)執(zhí)行任務(wù))
      • 并發(fā)隊(duì)列的「并發(fā)」功能只有在異步(dispatch_async)方法下才有效
  • GCD使用步驟

    • 創(chuàng)建一個(gè)隊(duì)列(串行隊(duì)列/并發(fā)隊(duì)列)
    • 將任務(wù)追加到任務(wù)的等待隊(duì)列中壕翩,然后系統(tǒng)就會(huì)根據(jù)任務(wù)類(lèi)型執(zhí)行任務(wù)(同步執(zhí)行/異步執(zhí)行)
  • 死鎖條件

    • 使用sync函數(shù)往當(dāng)前串行隊(duì)列中添加任務(wù)蛉迹,會(huì)卡住當(dāng)前的串行隊(duì)列。

如何打造線程安全的NSMutableArray放妈?

  • 線程鎖:使用線程鎖在對(duì)數(shù)組讀寫(xiě)時(shí)候加鎖
  • 派發(fā)隊(duì)列:
    《Effective Objective 2.0》中41條提出的觀點(diǎn)北救,串行同步:將讀取和寫(xiě)入都安排在同一個(gè)隊(duì)列里,可保證數(shù)據(jù)同步芜抒。

如何異步下載多張小圖最后合成一張大圖珍策?

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{ /*加載圖片1 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片2 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片3 */ }); 
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 合并圖片
});

什么是線程安全?

  • 多線程操作過(guò)程中往往都是多個(gè)線程并發(fā)執(zhí)行的宅倒,因此同一個(gè)資源可能被多個(gè)線程同時(shí)訪問(wèn)攘宙,造成資源搶奪。
  • 線程安全就是多條線程同時(shí)訪問(wèn)一段代碼拐迁,不會(huì)造成數(shù)據(jù)混亂的情況

如何設(shè)置常駐線程蹭劈?????

  • 為當(dāng)前線程開(kāi)啟一個(gè) RunLoop (第一次調(diào)用 [NSRunLoop currentRunLoop] 方法時(shí)
    實(shí)際是會(huì)先去創(chuàng)建一個(gè) RunLoop )
  • 向當(dāng)前 RunLoop 中添加一個(gè) Port/Source 等維持 RunLoop 的事件循環(huán)(如果
    RunLoop 的 mode 中一個(gè) item 都沒(méi)有, RunLoop 會(huì)退出)
  • 啟動(dòng)該 RunLoop

在異步線程發(fā)送通知线召,在主線程接收通知链方。會(huì)不會(huì)有什么問(wèn)題?

GCD線程是如何調(diào)度的

如何實(shí)現(xiàn)多個(gè)任務(wù)執(zhí)行完后灶搜,再統(tǒng)一處理祟蚀?

  • 同步阻塞
  • 柵欄函數(shù)
  • 線程組

線程和線程之間如何通信工窍?

  • 線程通信的表現(xiàn):

    • 1個(gè)線程傳遞數(shù)據(jù)給另1個(gè)線程
    • 在1個(gè)線程中執(zhí)行完特定任務(wù)后,轉(zhuǎn)到另1個(gè)線程繼續(xù)執(zhí)行任務(wù)
  • 線程間通信常用方法:

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;

示例:

- (void)viewDidLoad {
    [super viewDidLoad];
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    // 在子線程中調(diào)用download方法下載圖片
    [self performSelectorInBackground:@selector(download) withObject:nil];
}

- (void)download {
    // 1.根據(jù)URL網(wǎng)絡(luò)中下載圖片
    NSURL *urlstr=[NSURL URLWithString:@"fdsf"];

    // 2前酿、把圖片轉(zhuǎn)換為二進(jìn)制的數(shù)據(jù), 這一行操作會(huì)比較耗時(shí)
    NSData *data=[NSData dataWithContentsOfURL:urlstr];

    // 3患雏、把數(shù)據(jù)轉(zhuǎn)換成圖片
    UIImage *image=[UIImage imageWithData:data];

    // 4、回到主線程中設(shè)置圖片
    [self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];
}

//設(shè)置顯示圖片
- (void)settingImage:(UIImage *)image {
    self.iconView.image=image;
}

談?wù)刟tomic的實(shí)現(xiàn)機(jī)制罢维,為什么不能保證絕對(duì)線程安全淹仑?

  • 實(shí)現(xiàn)機(jī)制

    • 編譯器自動(dòng)生成getter/setter方法中添加鎖保證線程安全
  • 為什么不能保證絕對(duì)安全?

    • getter/setter中加鎖肺孵,僅保證存取時(shí)線程安全匀借,不會(huì)讓你拿到一個(gè)崩潰的值
    • 無(wú)法保證對(duì)容器的修改是線程安全的,例:假設(shè)屬性是可變?nèi)萜?code>(@property (atomic) NSMutableArray *array)時(shí)
    • 重寫(xiě)getter/setter方法時(shí)平窘,只能依靠自己在getter/setter中保證線程安全
  - (void)setCurrentImage:(UIImage *)currentImage {
    if (_currentImage != currentImage) {
        [_currentImage release];
        _currentImage = [currentImage retain];
    }
}

- (UIImage *)currentImage {
    return _currentImage;
}

- (void)setCurrentImage:(UIImage *)currentImage {
    @synchronized(self) {
        if (_currentImage != currentImage) {
            [_currentImage release];
            _currentImage = [currentImage retain];
        }
    }
}

- (UIImage *)currentImage {
    @synchronized(self) {
        return _currentImage;
    }
}

進(jìn)程和線程的區(qū)別

  • 區(qū)別

    • 一個(gè)線程只能屬于一個(gè)進(jìn)程.
    • 一個(gè)進(jìn)程可以有多個(gè)線程吓肋,但至少有一個(gè)線程。
    • 線程是操作系統(tǒng)可識(shí)別的最小執(zhí)行和調(diào)度單位瑰艘。
  • 資源分配

    • 資源分配給進(jìn)程是鬼,同一進(jìn)程的所有線程共享該進(jìn)程的所有資源。
    • 同一進(jìn)程中的多個(gè)線程共享代碼段紫新、數(shù)據(jù)段均蜜、擴(kuò)展段
    • 但是每個(gè)線程擁有自己的棧段芒率,棧段又叫運(yùn)行時(shí)段囤耳,用來(lái)存放所有局部變量和臨時(shí)變量。

Notification與線程相關(guān)

  • 在多線程應(yīng)用中偶芍,Notification在哪個(gè)線程中post紫皇,就在哪個(gè)線程中被轉(zhuǎn)發(fā),而不一定是在注冊(cè)觀察者的那個(gè)線程中腋寨。
  • 換句話說(shuō)就是在哪個(gè)線程發(fā)送通知,就在哪個(gè)線程接受通知化焕。

如何實(shí)現(xiàn)在不同線程中post和轉(zhuǎn)發(fā)一個(gè)Notification萄窜?

重定向的實(shí)現(xiàn)思路:

  1. 自定義一個(gè)通知隊(duì)列(用數(shù)組類(lèi)型),讓它去維護(hù)那些我們需要重定向的Notification
  2. 我們?nèi)匀皇窍衿匠R粯尤プ?cè)一個(gè)通知的觀察者撒桨,當(dāng)Notification來(lái)了時(shí)查刻,先看看post這個(gè)Notification的線程是不是我們所期望的線程
  3. 如果不是,則將這個(gè)Notification存儲(chǔ)到我們的隊(duì)列中凤类,并發(fā)送一個(gè)信號(hào)(signal)到期望的線程中穗泵,來(lái)告訴這個(gè)線程需要處理一個(gè)Notification
  4. 指定的線程在收到信號(hào)后,將Notification從隊(duì)列中移除谜疤,并進(jìn)行處理

dispatch_once底層實(shí)現(xiàn)

線程鎖

  • 線程鎖的作用

    • 我們?cè)谑褂枚嗑€程的時(shí)候佃延,多個(gè)線程可能會(huì)訪問(wèn)同一塊資源现诀,就很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全等問(wèn)題
    • 這時(shí)候就需要我們保證每次只有一個(gè)線程訪問(wèn)這一塊資源
  • 線程鎖類(lèi)型

    • 互斥鎖
    • 自旋鎖
    • 信號(hào)量
    • 遞歸鎖
    • atomic

互斥鎖

  • 標(biāo)記用來(lái)保證在任一時(shí)刻,只能有一個(gè)線程訪問(wèn)對(duì)象
  • NSLock
  • @synchronized (self)

自旋鎖

  • OSSpinLock(YYKit作者有一篇文章寫(xiě)它不安全履肃,可以自己研究一下)
  • os_unfair_lock

信號(hào)量(Semaphore - dispatch_semaphore_t)

  • 多線程環(huán)境下使用的一種設(shè)施仔沿,是可以用來(lái)保證兩個(gè)或多個(gè)關(guān)鍵代碼段不被并發(fā)調(diào)用
  • 在進(jìn)入一個(gè)關(guān)鍵代碼段之前,線程必須獲取一個(gè)信號(hào)量尺棋;關(guān)鍵代碼段完成后封锉,該線程必須釋放信號(hào)量
  • 其它想進(jìn)入該關(guān)鍵代碼段的線程必須等待直到第一個(gè)線程釋放信號(hào)量

遞歸鎖(NSRecursiveLock)

  • 同一個(gè)線程可以多次加鎖,不會(huì)造成死鎖

atomic

  • atomic 修飾的對(duì)象膘螟,系統(tǒng)會(huì)保證在其自動(dòng)生成的 getter/setter 方法中的操作是完整的成福,不受其他線程的影響

線程不安全

  • 如果有另一個(gè)線程同時(shí)在調(diào)[name release],那可能就會(huì)crash荆残,因?yàn)?release 不受 getter/setter 操作的限制
  • 這個(gè)屬性只能說(shuō)是讀/寫(xiě)安全的奴艾,但并不是線程安全的,因?yàn)閯e的線程還能進(jìn)行讀寫(xiě)之外的其他操作

四脊阴、Block相關(guān)

Block

  • block本質(zhì)
    • block是將函數(shù)及其執(zhí)行上下文封裝起來(lái)的對(duì)象

關(guān)于block的截獲特性握侧,你是否有了解?block的截獲變量特性是怎樣的嘿期?

  • 變量捕獲機(jī)制分析:

    • 對(duì)于“基本數(shù)據(jù)類(lèi)型”的“局部變量”截獲其值
    • 對(duì)于“對(duì)象”類(lèi)型的局部變量“連同所有權(quán)修飾符”一起截獲
    • 以“指針形式”截獲局部靜態(tài)變量(指針傳遞)
    • 不截獲全局變量品擎、靜態(tài)全局變量(直接訪問(wèn))
  • 改外部變量必要條件

    • 將auto從棧copy到堆
      原因:棧中內(nèi)存管理是由系統(tǒng)管理,出了作用域就會(huì)被回收备徐,堆中才是可以由程序員管理

對(duì)棧上的block進(jìn)行copy之后萄传,假如在mrc環(huán)境下,內(nèi)存是否回泄漏蜜猾?

  • copy操作之后秀菱,堆上的block沒(méi)有額外的成員變量指向它,正如我們alloc對(duì)象后蹭睡,沒(méi)有進(jìn)行release衍菱,造成內(nèi)存泄漏

為什么block會(huì)產(chǎn)生循環(huán)引用?

  • 如果當(dāng)前block對(duì)當(dāng)前對(duì)象的某一成員變量進(jìn)行截獲肩豁,block會(huì)對(duì)當(dāng)前對(duì)象有一個(gè)強(qiáng)引用
  • 而當(dāng)前block由于當(dāng)前對(duì)象對(duì)其有一個(gè)強(qiáng)引用脊串,產(chǎn)生了一個(gè)自循環(huán)引用的一個(gè)循環(huán)引用的問(wèn)題

Block不允許修改外部變量的值原因

  • block 本質(zhì)上是一個(gè)對(duì)象,block 的花括號(hào)區(qū)域是對(duì)象內(nèi)部的一個(gè)函數(shù)清钥,變量進(jìn)入 花括號(hào)琼锋,實(shí)際就是已經(jīng)進(jìn)入了另一個(gè)函數(shù)區(qū)域---改變了作用域。

  • 在幾個(gè)作用域之間進(jìn)行切換時(shí)祟昭,如果不加上這樣的限制缕坎,變量的可維護(hù)性將大大降低。

  • 比如想在block內(nèi)聲明了一個(gè)與外部同名的變量篡悟,此時(shí)是允許呢還是不允許呢谜叹?只有加上了這樣的限制匾寝,這樣的情景才能實(shí)現(xiàn)。

  • 所以 Apple 在編譯器層面做了限制叉谜,如果在 block 內(nèi)部試圖修改 auto 變量(無(wú)修飾符)旗吁,那么直接編譯報(bào)錯(cuò)。

  • 可以把編譯器的這種行為理解為:對(duì) block 內(nèi)部捕獲到的 auto 變量設(shè)置為只讀屬性---不允許直接修改停局。

如何實(shí)現(xiàn)對(duì)外部變量的捕獲很钓?

  • 將變量設(shè)置為全局變量。原理:block內(nèi)外可直接訪問(wèn)全局變量
  • 加 static (放在靜態(tài)存儲(chǔ)區(qū)/全局初始化區(qū))董栽。原理是block內(nèi)部對(duì)外部auto變量進(jìn)行指針捕獲
  • 最優(yōu)解:使用__block 關(guān)鍵字

__block

  • 將auto變量封裝為結(jié)構(gòu)體(對(duì)象)码倦,在結(jié)構(gòu)體內(nèi)部新建一個(gè)同名的auto變量
  • block內(nèi)截獲該結(jié)構(gòu)體的指針
  • 在block中使用自動(dòng)變量時(shí),使用指針指向的結(jié)構(gòu)體中的自動(dòng)變量
__block int var = 10;
void(^blk)(void) = ^{
    var = 20;
};
blk();

轉(zhuǎn)換后的代碼:

struct __Block_byref_var_0 {
    void *__isa;
    __Block_byref_var_0 *__forwarding;
    int __flags;
    int __size;
    int var; // 10 => 20 該結(jié)構(gòu)體持有相當(dāng)于原來(lái)自動(dòng)變量的成員變量
};

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    __Block_byref_var_0 *var; // by ref
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_var_0 *_var, int flags=0) : var(_var->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};

block在修改NSMutableArray需不需要添加__block

  • 不需要
  • 原因:
    • 當(dāng)變量是一個(gè)指針的時(shí)候锭碳,block里只是復(fù)制了一份這個(gè)指針袁稽,兩個(gè)指針指向同一個(gè)地址。
    • 所以擒抛,在block里面對(duì)指針指向內(nèi)容做的修改推汽,在block外面也一樣生效。

block是如何捕獲局部變量的?

  • block捕獲外界變量時(shí)歧沪,在內(nèi)部會(huì)自動(dòng)生成同一個(gè)屬性來(lái)保存

UIView動(dòng)畫(huà)中block回調(diào)里self要不要弱引用歹撒?

  • 不需要,它不會(huì)造成循環(huán)引用诊胞,因?yàn)樗穷?lèi)方法暖夭。
  • 之所以需要弱引用本身,是因?yàn)榕聦?duì)象之間產(chǎn)生循環(huán)引用撵孤,當(dāng)前控制器不可能強(qiáng)引用一個(gè)類(lèi)迈着,所以循環(huán)無(wú)法形成。

block里面會(huì)不會(huì)存在self為空的情況(weak strong的原理)邪码?

__weak typeof(self) weakself = self;
[self wj_refresh_addRefreshHeader:^{
    __strong typeof(weakself) strongself = weakself;
    [strongself.dataSource reloadDataWithCompletion:nil];
}];
  • 有時(shí)候weakSelf在block里在執(zhí)行reloadDataWithCompletion還存在
  • 但在執(zhí)行reloadDataWithCompletion前裕菠,可能會(huì)被釋放了
  • 為了保證self在block執(zhí)行過(guò)程里一直存在,對(duì)他強(qiáng)引用strongSelf

__block與__weak的區(qū)別

  • __block不管是ARC還是MRC模式下都可以使用闭专,可以修飾對(duì)象奴潘,還可以修飾基本數(shù)據(jù)類(lèi)型
  • __weak只能在ARC模式下使用,也只能修飾對(duì)象(NSString)喻圃,不能修飾基本數(shù)據(jù)類(lèi)型(int)
  • __block對(duì)象可以在block中被重新賦值,__weak不可以。

多層block嵌套如何使用weakSelf?

__weak typeof(self) weakself = self;
[self wj_refresh_addRefreshHeader:^{
    __strong typeof(weakself) strongself = weakself;
    __weak typeof(self) weakSelf2 = strongself;
    [strongself.dataSource reloadDataWithCompletion:^(BOOL result) {
        __strong typeof(self) strongSelf2 = weakSelf2;
    }];
}];

Masonry對(duì)于block內(nèi)部引用self會(huì)不會(huì)造成循環(huán)引用摹蘑?

  • 不會(huì)
  • 這個(gè)block沒(méi)有copy免绿,是在棧上胧后,使用完直接釋放了瑞你,
- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *))block {
    self.translatesAutoresizingMaskIntoConstraints = NO;
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    block(constraintMaker);
    return [constraintMaker install];
}

__weak來(lái)解決block中的循環(huán)引用杠氢,還有別的方法嗎

  • __block
  • 將對(duì)象傳進(jìn)入修改

代理些楣、Block利弊

  • 與委托代理模式的代碼相比昂勉,用block寫(xiě)出的代碼更為整潔

  • 代理優(yōu)點(diǎn):

    • 代理語(yǔ)法清晰浪册,可讀性高,易于維護(hù)
    • 它減少代碼耦合性岗照,使事件監(jiān)聽(tīng)與事件處理分離
    • 一個(gè)控制器可以實(shí)現(xiàn)多個(gè)代理村象,滿足自定義開(kāi)發(fā)需求,靈活性較高
  • 代理缺點(diǎn):

    • 實(shí)現(xiàn)代理的過(guò)程較繁瑣
    • 跨層傳值時(shí)加大代碼的耦合性攒至,并且程序的層次結(jié)構(gòu)也變得混亂
    • 當(dāng)多個(gè)對(duì)象同時(shí)傳值時(shí)不易區(qū)分厚者,導(dǎo)致代理易用性大大降低
  • block優(yōu)點(diǎn):

    • 語(yǔ)法簡(jiǎn)潔,代碼可讀性和維護(hù)性較高
    • 配合GCD優(yōu)秀的解決多線程問(wèn)題
  • block缺點(diǎn):

    • Block中得代碼將自動(dòng)進(jìn)行一次retain操作库菲,容易造成內(nèi)存泄漏
    • Block內(nèi)默認(rèn)引用為強(qiáng)引用,容易造成循環(huán)應(yīng)用
  • 運(yùn)行成本:

    • delegate運(yùn)行成本低志膀,block的運(yùn)行成本高
    • block出棧需要將使用的數(shù)據(jù)從棧內(nèi)存拷貝到堆內(nèi)存熙宇,當(dāng)然對(duì)象的話就是假引用技術(shù),使用完block置nil才會(huì)消除
    • delegate只是保存了一個(gè)對(duì)象的指針溉浙,直接回調(diào)烫止,沒(méi)有額外的消耗。就像c的函數(shù)指針放航,只多了一個(gè)查表動(dòng)作

五烈拒、Runtime

Runtime的相關(guān)術(shù)語(yǔ)

SEL、id广鳍、Class荆几、Method、IMP赊时、Cache吨铸、Property

介紹下runtime的內(nèi)存模型(isa、對(duì)象祖秒、類(lèi)诞吱、metaclass、結(jié)構(gòu)體的存儲(chǔ)信息等)

為什么要設(shè)計(jì)metaclass

class_copyIvarList & class_copyPropertyList區(qū)別

class_rw_t 和 class_ro_t 的區(qū)別

交互兩個(gè)方法的現(xiàn)實(shí)有什么風(fēng)險(xiǎn)竭缝?

六房维、Runloop

  • 什么是RunLoop?
    • RunLoop 實(shí)際上是一個(gè)對(duì)象
    • 這個(gè)對(duì)象在循環(huán)中用來(lái)處理程序運(yùn)行過(guò)程中出現(xiàn)的各種事件(比如說(shuō)觸摸事件抬纸、UI刷新事件咙俩、定時(shí)器事件、Selector事件)
    • 從而保持程序的持續(xù)運(yùn)行
    • 在沒(méi)有事件處理的時(shí)候,會(huì)使線程進(jìn)入睡眠模式阿趁,從而節(jié)省 CPU 資源膜蛔,提高程序性能
  • 應(yīng)用范疇
    • 定時(shí)器(Timer)、PerformSelect
    • GCD Async Main Queue
    • 事件響應(yīng)脖阵、手勢(shì)識(shí)別皂股、界面刷新
    • 網(wǎng)絡(luò)請(qǐng)求
    • AutoreleasePool
  • 基本應(yīng)用
    • 保持程序的持續(xù)運(yùn)行
    • 處理App中的各種事件(比如觸摸事件、定時(shí)器事件等)
    • 節(jié)省CPU資源命黔,提高程序性能:該做事時(shí)做事呜呐,該休息時(shí)休息
  • runloop和線程的關(guān)系
    • 每條線程都有唯一的一個(gè)與之對(duì)應(yīng)的RunLoop對(duì)象
    • RunLoop保存在一個(gè)全局的Dictionary里,線程作為key纷铣,RunLoop作為value
    • 線程剛創(chuàng)建時(shí)并沒(méi)有RunLoop對(duì)象卵史,RunLoop會(huì)在第一次獲取它時(shí)創(chuàng)建
    • RunLoop會(huì)在線程結(jié)束時(shí)銷(xiāo)毀
    • 主線程的RunLoop已經(jīng)自動(dòng)獲取(創(chuàng)建)搜立,子線程默認(rèn)沒(méi)有開(kāi)啟RunLoop
/*
 * 從字典中獲取以躯,如果沒(méi)有則直接創(chuàng)建
 */
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFSpinUnlock(&loopsLock);
if (!loop) {
    CFRunLoopRef newLoop = __CFRunLoopCreate(t);
    __CFSpinLock(&loopsLock);
    loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    
    if (!loop) {
        CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
        loop = newLoop;
    }
    
    // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
    __CFSpinUnlock(&loopsLock);
    CFRelease(newLoop);
}

NSTimer相關(guān)

NSTimer準(zhǔn)嗎?如果不準(zhǔn)的話原因是什么啄踊?如何解決忧设?

  • 原因:
    • NSTimer的觸發(fā)時(shí)間到的時(shí)候,會(huì)在RunLoop中被檢測(cè)一次颠通;
    • 如果在這一次的RunLoop中做了耗時(shí)的操作址晕,會(huì)處于阻塞狀態(tài)
    • 時(shí)間超過(guò)了定時(shí)器的間隔時(shí)間,觸發(fā)時(shí)間就會(huì)推遲到下一個(gè)runloop周期
  • 解決方法:
    • 在子線程中創(chuàng)建timer顿锰,在子線程中進(jìn)行定時(shí)任務(wù)的操作谨垃,需要UI操作時(shí)切換回主線程進(jìn)行操作
    • 使用CADisplayLink(一般用來(lái)做UI展示更新,同樣存在runloop卡頓問(wèn)題)
    • 使用GCD定時(shí)器

使用NSTimer是如何處理循環(huán)引用的硼控?

使用類(lèi)方法
TODO(待填充);

談?wù)劤S玫娜N定時(shí)器優(yōu)缺點(diǎn)(NSTimer刘陶、CADisplayLink、GCD定時(shí)器)?????

  • NSTimer和CADisplayLink依賴于RunLoop牢撼,如果RunLoop的任務(wù)過(guò)于繁重匙隔,可能會(huì)導(dǎo)致NSTimer不準(zhǔn)時(shí)
  • 相比之下GCD的定時(shí)器會(huì)更加準(zhǔn)時(shí),因?yàn)镚CD不是依賴RunLoop熏版,而是由內(nèi)核決定
  • CADisplayLink和NSTimer會(huì)對(duì)target產(chǎn)生強(qiáng)引用纷责,如果target又對(duì)它們產(chǎn)生強(qiáng)引用,那么就會(huì)引發(fā)循環(huán)引用

在viewWillDisappear或者viewDidDisappear方法中將 timer = nil撼短,是否還會(huì)造成循環(huán)引用再膳?

  • 問(wèn)題一:如果只是想在離開(kāi)此頁(yè)時(shí)要釋放,進(jìn)入下一頁(yè)時(shí)不要釋放曲横,場(chǎng)景就不適用了
  • runloop->timer喂柒;controller->timer

如何利用runloop監(jiān)控卡頓???

七、KVO

KVO基礎(chǔ)

KVO 的 全稱(chēng)Key-Value Observing,俗稱(chēng)“鍵值監(jiān)聽(tīng)”胳喷,可以用于某個(gè)對(duì)象屬性值的改變

iOS用什么方式實(shí)現(xiàn)對(duì)一個(gè)對(duì)象的KVO?(KVO的本質(zhì)是什么)

  • 利用runtimeAPI動(dòng)態(tài)生成一個(gè)子類(lèi)(NSKVONotifying_XXXX)夭织,并且讓instance對(duì)象的isa指向這個(gè)全新的子類(lèi)
  • 當(dāng)修改instance對(duì)象的屬性時(shí)吭露,會(huì)動(dòng)用Foundation的_NSSetXXXValueAndNotify函數(shù)
  • willChangeValueForKey
  • 父類(lèi)原來(lái)的setter方法
  • didChangeValueForKey
  • 內(nèi)部觸發(fā)監(jiān)聽(tīng)器(ObserveValueForKeyPath:ofObject:change:context

如何手動(dòng)觸發(fā)KVO?

  • 手動(dòng)調(diào)用willChangeValueForKey
  • 修改成員變量值
  • 手動(dòng)調(diào)用didChangeValueForKey

直接修改成員變量會(huì)觸發(fā)KVO么尊惰?

  • 不會(huì)觸發(fā)KVO(原因看KVO的本質(zhì))

八讲竿、KVC

KVC基礎(chǔ)

KVC的全稱(chēng)是Key-Value Coding,俗稱(chēng)“鍵值編碼”弄屡,可以通過(guò)一個(gè)key來(lái)訪問(wèn)某個(gè)屬性

通過(guò)KVC修改屬性會(huì)出發(fā)KVO么题禀?

  • 能觸發(fā)KVO()
  • KVC在修改屬性時(shí),會(huì)調(diào)用willChangeValueForKey:和didChangeValueForKey:方法

KVC的賦值和取值過(guò)程是怎樣的膀捷?

  • 賦值過(guò)程
    • 首先會(huì)按照setKey迈嘹、_setKey的順序查找方法,找到方法全庸,直接調(diào)用方法并賦值秀仲;
    • 未找到方法,則調(diào)用+ (BOOL)accessInstanceVariablesDirectly;
    • 若accessInstanceVariablesDirectly方法返回YES壶笼,則按照_key神僵、_isKey、key覆劈、isKey的順序查找成員變量保礼,找到直接賦值,找不到則拋出異常责语;
    • 若accessInstanceVariablesDirectly方法返回NO炮障,則直接拋出異常;
  • 取值過(guò)程
    • 首先會(huì)按照getKey鹦筹、key铝阐、isKey、_key的順序查找方法铐拐,找到直接調(diào)用取值
    • 若未找到徘键,則查看+ (BOOL)accessInstanceVariablesDirectly的返回值,若返回NO遍蟋,則直接拋出異常吹害;
    • 若返回的YES,則按照_key虚青、_isKey它呀、key、isKey的順序查找成員變量,找到則取值纵穿;
    • 找不到則拋出異常下隧;

使用場(chǎng)景

  • 單層字典模型轉(zhuǎn)化:
[self.model setValuesForKeysWithDictionary:dict];
  • 通過(guò)KVC修改未暴露的屬性:
UILabel *placeholderLabel=[self.userTextField valueForKeyPath:@"placeholderLabel"];
placeholderLabel.textColor = [UIColor redColor];
  • 使用valueForKeyPath可以獲取數(shù)組中的最小值、最大值谓媒、平均值淆院、求和
CGFloat sum = [[array valueForKeyPath:@"@sum.floatValue"] floatValue];
CGFloat avg = [[array valueForKeyPath:@"@avg.floatValue"] floatValue];
CGFloat max =[[array valueForKeyPath:@"@max.floatValue"] floatValue];
CGFloat min =[[array valueForKeyPath:@"@min.floatValue"] floatValue];
  • 數(shù)組內(nèi)部去重
[dataArray valueForKeyPath:@"@distinctUnionOfObjects.self"]
  • 數(shù)組合并(去重合并:distinctUnionOfArrays.self、直接合并:unionOfArrays.self)
NSArray *temp1 = @[@3, @2, @2, @1];
NSArray *temp2 = @[@3, @4, @5];
NSLog(@"\n%@",[@[temp1, temp2] valueForKeyPath:@"@distinctUnionOfArrays.self"]);
NSLog(@"\n%@",[@[temp1, temp2] valueForKeyPath:@"@unionOfArrays.self"]);

輸出兩個(gè)數(shù)組:( 5, 1, 2, 3, 4 ), ( 3, 2, 2, 1, 3, 4, 5 )句惯。

  • 大小寫(xiě)轉(zhuǎn)換(uppercaseString)及 打印字符串長(zhǎng)度同樣適用(length)
NSArray *array = @[@"name", @"w", @"aa", @"jimsa"];
NSLog(@"%@", [array valueForKeyPath:@"uppercaseString"]);
打油帘纭:
(NAME,W,AA,JIMSA)

九、Category

Category相關(guān)

Category的使用場(chǎng)合

  • 將一個(gè)類(lèi)拆成很多模塊(其實(shí)就是解耦抢野,將相關(guān)的功能放到一起)

Category的實(shí)現(xiàn)原理

  • 通過(guò)runtime動(dòng)態(tài)將分類(lèi)的方法合并到類(lèi)對(duì)象拷淘、元類(lèi)對(duì)象中
  • Category編譯之后的底層結(jié)構(gòu)是 struct_category_t , 里面存儲(chǔ)著分類(lèi)的對(duì)象方法、類(lèi)方法指孤、屬性启涯、協(xié)議信息
  • 在程序運(yùn)行的時(shí)候,runtime會(huì)將 Category 的數(shù)據(jù)恃轩,合并到類(lèi)信息中(類(lèi)對(duì)象逝嚎、元類(lèi)對(duì)象)

category和extension區(qū)別

  • Extension在編譯時(shí),就把信息合并到類(lèi)信息中
  • Category是在運(yùn)行時(shí)详恼,才會(huì)將分類(lèi)信息合并到類(lèi)信息中
  • 分類(lèi)聲明的屬性补君,只會(huì)生成getter/setter方法聲明,不會(huì)自動(dòng)生成成員變量和getter/setter方法實(shí)現(xiàn)昧互,而擴(kuò)展會(huì)
  • 分類(lèi)不可用為類(lèi)添加實(shí)例變量挽铁,而擴(kuò)展可以

分類(lèi)的局限性

  • 無(wú)法為類(lèi)添加實(shí)例變量,但可通過(guò)關(guān)聯(lián)對(duì)象進(jìn)行實(shí)現(xiàn)
  • 分類(lèi)的方法如果和類(lèi)重名敞掘,會(huì)覆蓋原來(lái)方法的實(shí)現(xiàn)
  • 多個(gè)分類(lèi)的方法重名叽掘,會(huì)調(diào)用最后編譯的那個(gè)分類(lèi)的實(shí)現(xiàn)

為什么category不能添加屬性?使用Runtime就可以了玖雁?

  • 分類(lèi)沒(méi)有自己的isa指針
  • 類(lèi)最開(kāi)始生成了很多基本屬性更扁,比如IvarList,MethodList
  • 分類(lèi)只會(huì)將自己的method attach到主類(lèi)赫冬,并不會(huì)影響到主類(lèi)的IvarList
  • 實(shí)例變量沒(méi)有setter和getter方法浓镜。也沒(méi)有自己的isa指針
  • 關(guān)聯(lián)對(duì)象都由AssociationsManager管理
  • AssociationsManager里面是由一個(gè)靜態(tài)AssociationsHashMap來(lái)存儲(chǔ)所有的關(guān)聯(lián)對(duì)象的。
  • 相當(dāng)于把所有對(duì)象的關(guān)聯(lián)對(duì)象都存在一個(gè)全局map里面劲厌。而map的的key是這個(gè)對(duì)象的指針地址
  • 而這個(gè)map的value又是另外一個(gè)AssAssociationsHashMap膛薛,里面保存了關(guān)聯(lián)對(duì)象的kv對(duì)

Category中有l(wèi)oad方法么?load方法什么時(shí)候調(diào)用的补鼻?load方法能繼承么哄啄?

  • +load方法會(huì)在runtime加載類(lèi)雅任、分類(lèi)時(shí)調(diào)用;

  • 每個(gè)類(lèi)咨跌、分類(lèi)的+load沪么,在程序運(yùn)行過(guò)程中只調(diào)用一次

  • 調(diào)用順序

  • 先調(diào)用類(lèi)的+load,(按照編譯先后順序锌半,先編譯成玫,先調(diào)用),調(diào)用子類(lèi)的+load之前會(huì)調(diào)用父類(lèi)的+load

  • 再調(diào)用分類(lèi)的+load按照編譯先后順序調(diào)用(先編譯拳喻,先調(diào)用)

test方法和load方法的本質(zhì)區(qū)別?(+load方法為什么不會(huì)被覆蓋)

  • test方法是通過(guò)消息機(jī)制調(diào)用 objc_msgSend([MJPerson class], @selector(test))
  • +load方法調(diào)用猪腕,直接找到內(nèi)存中的地址冗澈,進(jìn)行方法調(diào)用

load調(diào)用順序

  • +load方法會(huì)在runtime加載類(lèi)、分類(lèi)時(shí)調(diào)用
  • 每個(gè)類(lèi)陋葡、分類(lèi)的+load亚亲,在程序運(yùn)行過(guò)程中只調(diào)用一次
  • 調(diào)用順序
    • 先調(diào)用類(lèi)的+load方法,之后按照編譯先后順序調(diào)用(先編譯腐缤,先調(diào)用捌归,調(diào)用子類(lèi)的+load之前會(huì)先調(diào)用父類(lèi)的+load)
    • 再調(diào)用分類(lèi)的+load,之后按照編譯先后順序調(diào)用(先編譯岭粤,先調(diào)用)

不同Category中存在同一個(gè)方法惜索,會(huì)執(zhí)行哪個(gè)方法?如果是兩個(gè)都執(zhí)行剃浇,執(zhí)行順序是什么樣的巾兆?

  • 根據(jù)Build Phases->Compile Sources中添加的文件順序,后面的會(huì)覆蓋前面的

load虎囚、initialize方法的區(qū)別是什么角塑?它們?cè)赾ategory中的調(diào)用順序?以及出現(xiàn)繼承時(shí)他們之間的調(diào)用過(guò)程淘讥???????????

區(qū)別:

  • 調(diào)用方式不同
    • load是根據(jù)函數(shù)地址直接調(diào)用
    • initialize是通過(guò)objc_msgSend調(diào)用
  • 調(diào)用時(shí)刻
    • load是runtime加載 類(lèi)/分類(lèi) 的時(shí)候調(diào)用(只會(huì)調(diào)用1次)
    • initialize是類(lèi)第一次接收消息時(shí)調(diào)用圃伶,每一個(gè)類(lèi)只會(huì)initialize一次(父類(lèi)的initialize方法可能會(huì)被調(diào)用多次)
  • 調(diào)用順序
    • load:先調(diào)用類(lèi)的load。先編譯的類(lèi)蒲列,優(yōu)先調(diào)用load(調(diào)用子類(lèi)的load之前窒朋,會(huì)先調(diào)用父類(lèi)的load)
    • 再調(diào)用分類(lèi)的load(先編譯的分類(lèi),優(yōu)先調(diào)用load)
    • initialize:先初始化父類(lèi)蝗岖, 再初始化子類(lèi)(可能最終調(diào)用的是父類(lèi)的initialize方法)

分類(lèi)中方法替換

  • category的方法沒(méi)有“完全替換掉”原來(lái)類(lèi)已經(jīng)有的方法炼邀,也就是說(shuō)如果category和原來(lái)類(lèi)都有methodA,那么category附加完成之后剪侮,類(lèi)的方法列表里會(huì)有兩個(gè)methodA
  • category的方法被放到了新方法列表的前面拭宁,而原來(lái)類(lèi)的方法被放到了新方法列表的后面
  • 這也就是我們平常所說(shuō)的category的方法會(huì)“覆蓋”掉原來(lái)類(lèi)的同名方法洛退,這是因?yàn)檫\(yùn)行時(shí)在查找方法的時(shí)候是順著方法列表的順序查找的,它只要一找到對(duì)應(yīng)名字的方法杰标,就會(huì)罷休_兵怯,殊不知后面可能還有一樣名字的方法。

為什么不能動(dòng)態(tài)添加成員變量腔剂?

  • 方法和屬性并不“屬于”類(lèi)實(shí)例媒区,而成員變量“屬于”類(lèi)實(shí)例
  • “類(lèi)實(shí)例”概念,指的是一塊內(nèi)存區(qū)域掸犬,包含了isa指針和所有的成員變量袜漩。
  • 假如允許動(dòng)態(tài)修改類(lèi)成員變量布局,已經(jīng)創(chuàng)建出的類(lèi)實(shí)例就不符合類(lèi)定義了湾碎,變成了無(wú)效對(duì)象宙攻。但方法定義是在objc_class中管理的,不管如何增刪類(lèi)方法介褥,都不影響類(lèi)實(shí)例的內(nèi)存布局座掘,已經(jīng)創(chuàng)建出的類(lèi)實(shí)例仍然可正常使用

十、網(wǎng)絡(luò)

TCP柔滔、UDP

TCP溢陪、UDP優(yōu)點(diǎn)及缺點(diǎn)

  • TCP優(yōu)點(diǎn):( 可靠,穩(wěn)定)

    • 在傳遞數(shù)據(jù)之前睛廊,會(huì)有三次握手來(lái)建立連接形真,
    • 在數(shù)據(jù)傳遞時(shí),有確認(rèn)超全、窗口没酣、重傳、擁塞控制機(jī)制卵迂,
    • 在數(shù)據(jù)傳完后裕便,還會(huì)斷開(kāi)連接用來(lái)節(jié)約系統(tǒng)資源
  • TCP缺點(diǎn):(慢,效率低见咒,占用系統(tǒng)資源高)

    • TCP在傳遞數(shù)據(jù)之前偿衰,要先建連接,這會(huì)消耗時(shí)間
    • 在數(shù)據(jù)傳遞時(shí)(確認(rèn)機(jī)制改览、重傳機(jī)制下翎、擁塞控制機(jī)制)等都會(huì)消耗大量的時(shí)間
    • 因?yàn)門(mén)CP有確認(rèn)機(jī)制、三次握手機(jī)制宝当,這些也導(dǎo)致TCP容易被人利用视事,實(shí)現(xiàn)DOS、DDOS庆揩、CC等攻擊
  • UDP的優(yōu)點(diǎn):(快)

    • UDP沒(méi)有TCP的握手俐东、確認(rèn)跌穗、窗口、重傳虏辫、擁塞控制等機(jī)制
    • UDP是一個(gè)無(wú)狀態(tài)的傳輸協(xié)議蚌吸,所以它在傳遞數(shù)據(jù)時(shí)非常快
  • UDP的缺點(diǎn):(不可靠砌庄,不穩(wěn)定)

    • 因?yàn)閁DP沒(méi)有TCP那些可靠的機(jī)制羹唠,在數(shù)據(jù)傳遞時(shí),如果網(wǎng)絡(luò)質(zhì)量不好娄昆,就會(huì)很容易丟包

TCP佩微、UDP的區(qū)別

  • TCP面向連接(如打電話要先撥號(hào)建立連接); UDP是無(wú)連接的,即發(fā)送數(shù)據(jù)
  • TCP提供可靠的服務(wù)萌焰。通過(guò)TCP連接傳送的數(shù)據(jù)哺眯,無(wú)差錯(cuò),不丟失杆怕,不重復(fù),且按序到達(dá)壳贪;UDP盡最大努力交付陵珍,即不保證可靠交付
  • TCP面向字節(jié)流,實(shí)際上是TCP把數(shù)據(jù)看成一連串無(wú)結(jié)構(gòu)的字節(jié)流; UDP是面向報(bào)文的
  • 每一條TCP連接只能是點(diǎn)到點(diǎn)的; UDP支持一對(duì)一违施,一對(duì)多互纯,多對(duì)一和多對(duì)多的交互通信

Scoket連接和HTTP連接的區(qū)別

  • HTTP協(xié)議是基于TCP連接的,是應(yīng)用層協(xié)議磕蒲,主要解決如何包裝數(shù)據(jù)留潦。Socket是對(duì)TCP/IP協(xié)議的封裝,Socket本身并不是協(xié)議辣往,而是一個(gè)調(diào)用接口(API)兔院,通過(guò)Socket,我們才能使用TCP/IP協(xié)議站削。
  • HTTP連接:短連接坊萝,客戶端向服務(wù)器發(fā)送一次請(qǐng)求,服務(wù)器響應(yīng)后連接斷開(kāi)塑娇,節(jié)省資源碱璃。服務(wù)器不能主動(dòng)給客戶端響應(yīng)转捕,iPhone主要使用類(lèi)NSURLConnection
  • Socket連接:長(zhǎng)連接,客戶端跟服務(wù)器端直接使用Socket進(jìn)行連接惦积,沒(méi)有規(guī)定連接后斷開(kāi),因此客戶端和服務(wù)器段保持連接通道猛频,雙方可以主動(dòng)發(fā)送數(shù)據(jù)

HTTP協(xié)議的特點(diǎn)狮崩,關(guān)于HTTP請(qǐng)求GET和POST的區(qū)別

  • 特點(diǎn):
    • HTTP超文本傳輸協(xié)議蛛勉,是短連接,是客戶端主動(dòng)發(fā)送請(qǐng)求厉亏,服務(wù)器做出響應(yīng)董习,服務(wù)器響應(yīng)之后,鏈接斷開(kāi)
    • HTTP是一個(gè)屬于應(yīng)用層面向?qū)ο蟮膮f(xié)議爱只,HTTP有兩類(lèi)報(bào)文:請(qǐng)求報(bào)文和響應(yīng)報(bào)文
    • HTTP請(qǐng)求報(bào)文:一個(gè)HTTP請(qǐng)求報(bào)文由請(qǐng)求行皿淋、請(qǐng)求頭部、空行和請(qǐng)求數(shù)據(jù)4部分組成
    • HTTP響應(yīng)報(bào)文:由三部分組成:狀態(tài)行恬试、消息報(bào)頭窝趣、響應(yīng)正文
  • GET請(qǐng)求
    • 參數(shù)在地址后拼接,不安全(因?yàn)樗袇?shù)都拼接在地址后面)
    • 不適合傳輸大量數(shù)據(jù)(長(zhǎng)度有限制训柴,為1024個(gè)字節(jié))
  • POST請(qǐng)求
    • 參數(shù)在請(qǐng)求數(shù)據(jù)區(qū)放著哑舒,相對(duì)GET請(qǐng)求更安全
    • 數(shù)據(jù)大小理論上沒(méi)有限制
    • 提交的數(shù)據(jù)放置在HTTP包的包體中

斷點(diǎn)續(xù)傳怎么實(shí)現(xiàn)的?

  • 斷點(diǎn)續(xù)傳主要依賴于 HTTP 頭部定義的 Range 來(lái)完成
  • 有了 Range幻馁,應(yīng)用可以通過(guò) HTTP 請(qǐng)求獲取失敗的資源洗鸵,從而來(lái)恢復(fù)下載該資源
  • 當(dāng)然并不是所有的服務(wù)器都支持 Range,但大多數(shù)服務(wù)器是可以的仗嗦。Range 是以字節(jié)計(jì)算的膘滨,請(qǐng)求的時(shí)候不必給出結(jié)尾字節(jié)數(shù),因?yàn)檎?qǐng)求方并不一定知道資源的大小

TCP建立和斷開(kāi)連接過(guò)程

a稀拐、 三次握手
  • 定義:
    • 三次握手(Three-way Handshake)其實(shí)就是指建立一個(gè)TCP連接時(shí)火邓,需要客戶端和服務(wù)器總共發(fā)送3個(gè)包。進(jìn)行三次握手的主要作用就是為了確認(rèn)雙方的接收能力和發(fā)送能力是否正常德撬、指定自己的初始化序列號(hào)為后面的可靠性傳送做準(zhǔn)備铲咨。實(shí)質(zhì)上其實(shí)就是連接服務(wù)器指定端口,建立TCP連接蜓洪,并同步連接雙方的序列號(hào)和確認(rèn)號(hào)纤勒,交換TCP窗口大小信息。
    • 第一次握手:客戶端給服務(wù)端發(fā)一個(gè) SYN 報(bào)文隆檀,并指明客戶端的初始化序列號(hào) ISN?踊东。此時(shí)客戶端處于 SYN_SEND 狀態(tài)。
      首部的同步位SYN=1刚操,初始序號(hào)seq=x闸翅,SYN=1的報(bào)文段不能攜帶數(shù)據(jù),但要消耗掉一個(gè)序號(hào)菊霜。
    • 第二次握手:服務(wù)器收到客戶端的 SYN 報(bào)文之后坚冀,會(huì)以自己的 SYN 報(bào)文作為應(yīng)答,并且也是指定了自己的初始化序列號(hào) ISN(s)鉴逞。同時(shí)會(huì)把客戶端的 ISN + 1 作為ACK 的值记某,表示自己已經(jīng)收到了客戶端的 SYN司训,此時(shí)服務(wù)器處于 SYN_REVD 的狀態(tài)。
      在確認(rèn)報(bào)文段中SYN=1液南,ACK=1壳猜,確認(rèn)號(hào)ack=x+1,初始序號(hào)seq=y滑凉。
    • 第三次握手:客戶端收到 SYN 報(bào)文之后统扳,會(huì)發(fā)送一個(gè) ACK 報(bào)文,當(dāng)然畅姊,也是一樣把服務(wù)器的 ISN + 1 作為 ACK 的值咒钟,表示已經(jīng)收到了服務(wù)端的 SYN 報(bào)文,此時(shí)客戶端處于 ESTABLISHED 狀態(tài)若未。服務(wù)器收到 ACK 報(bào)文之后朱嘴,也處于 ESTABLISHED 狀態(tài),此時(shí)粗合,雙方已建立起了連接萍嬉。
b、 三次握手為什么是三次握手
  • 如果是兩次握手:
    如客戶端發(fā)出連接請(qǐng)求隙疚,但因連接請(qǐng)求報(bào)文丟失而未收到確認(rèn)壤追,于是客戶端再重傳一次連接請(qǐng)求。后來(lái)收到了確認(rèn)甚淡,建立了連接大诸。數(shù)據(jù)傳輸完畢后捅厂,就釋放了連接贯卦,客戶端共發(fā)出了兩個(gè)連接請(qǐng)求報(bào)文段,其中第一個(gè)丟失焙贷,第二個(gè)到達(dá)了服務(wù)端撵割,但是第一個(gè)丟失的報(bào)文段只是在某些網(wǎng)絡(luò)結(jié)點(diǎn)長(zhǎng)時(shí)間滯留了,延誤到連接釋放以后的某個(gè)時(shí)間才到達(dá)服務(wù)端辙芍,此時(shí)服務(wù)端誤認(rèn)為客戶端又發(fā)出一次新的連接請(qǐng)求啡彬,于是就向客戶端發(fā)出確認(rèn)報(bào)文段,同意建立連接故硅,不采用三次握手庶灿,只要服務(wù)端發(fā)出確認(rèn),就建立新的連接了吃衅,此時(shí)客戶端忽略服務(wù)端發(fā)來(lái)的確認(rèn)往踢,也不發(fā)送數(shù)據(jù),則服務(wù)端一致等待客戶端發(fā)送數(shù)據(jù)徘层,浪費(fèi)資源峻呕。
c利职、 四次揮手
  • 定義:
    • 建立一個(gè)連接需要三次握手,而終止一個(gè)連接要經(jīng)過(guò)四次揮手(也有將四次揮手叫做四次握手的)瘦癌。這由TCP的半關(guān)閉(half-close)造成的猪贪。所謂的半關(guān)閉,其實(shí)就是TCP提供了連接的一端在結(jié)束它的發(fā)送后還能接收來(lái)自另一端數(shù)據(jù)的能力讯私。TCP 的連接的拆除需要發(fā)送四個(gè)包热押,因此稱(chēng)為四次揮手(Four-way handshake),客戶端或服務(wù)器均可主動(dòng)發(fā)起揮手動(dòng)作妄帘。
    • 第一次:TCP客戶端發(fā)送一個(gè)FIN報(bào)文楞黄,用來(lái)關(guān)閉客戶到服務(wù)器的數(shù)據(jù)傳送。
    • 第二次:服務(wù)器收到這個(gè)FIN報(bào)文抡驼,它發(fā)回一個(gè)ACK報(bào)文鬼廓,確認(rèn)序號(hào)為收到的序號(hào)加1。和SYN一樣致盟,一個(gè)FIN報(bào)文將占用一個(gè)序號(hào)碎税。
    • 第三次:服務(wù)器關(guān)閉客戶端的連接,發(fā)送一個(gè)FIN給客戶端馏锡。
    • 第四次:客戶端發(fā)回ACK報(bào)文確認(rèn)雷蹂,并將確認(rèn)序號(hào)設(shè)置為收到序號(hào)加1。
d杯道、揮手為什么需要四次匪煌?
  • 因?yàn)楫?dāng)服務(wù)端收到客戶端的SYN連接請(qǐng)求報(bào)文后,可以直接發(fā)送SYN+ACK報(bào)文党巾。其中ACK報(bào)文是用來(lái)應(yīng)答的萎庭,SYN報(bào)文是用來(lái)同步的。但是關(guān)閉連接時(shí)齿拂,當(dāng)服務(wù)端收到FIN報(bào)文時(shí)驳规,很可能并不會(huì)立即關(guān)閉SOCKET,所以只能先回復(fù)一個(gè)ACK報(bào)文署海,告訴客戶端吗购,“你發(fā)的FIN報(bào)文我收到了”。只有等到我服務(wù)端所有的報(bào)文都發(fā)送完了砸狞,我才能發(fā)送FIN報(bào)文捻勉,因此不能一起發(fā)送。故需要四次揮手刀森。
e踱启、為什么TIME_WAIT狀態(tài)需要經(jīng)過(guò)2MSL(最大報(bào)文段生存時(shí)間)才能返回到CLOSE狀態(tài)?
  • 雖然按道理,四個(gè)報(bào)文都發(fā)送完畢禽捆,我們可以直接進(jìn)入CLOSE狀態(tài)了笙什,但是我們必須假象網(wǎng)絡(luò)是不可靠的,有可以最后一個(gè)ACK丟失胚想。所以TIME_WAIT狀態(tài)就是用來(lái)重發(fā)可能丟失的ACK報(bào)文琐凭。在Client發(fā)送出最后的ACK回復(fù),但該ACK可能丟失浊服。Server如果沒(méi)有收到ACK统屈,將不斷重復(fù)發(fā)送FIN片段。所以Client不能立即關(guān)閉牙躺,它必須確認(rèn)Server接收到了該ACK愁憔。Client會(huì)在發(fā)送出ACK之后進(jìn)入到TIME_WAIT狀態(tài)。Client會(huì)設(shè)置一個(gè)計(jì)時(shí)器孽拷,等待2MSL的時(shí)間吨掌。如果在該時(shí)間內(nèi)再次收到FIN,那么Client會(huì)重發(fā)ACK并再次等待2MSL脓恕。所謂的2MSL是兩倍的MSL(Maximum Segment Lifetime)膜宋。MSL指一個(gè)片段在網(wǎng)絡(luò)中最大的存活時(shí)間,2MSL就是一個(gè)發(fā)送和一個(gè)回復(fù)所需的最大時(shí)間炼幔。如果直到2MSL秋茫,Client都沒(méi)有再次收到FIN,那么Client推斷ACK已經(jīng)被成功接收乃秀,則結(jié)束TCP連接肛著。
f、SYN的初始值(ISN initialization sequence number)是一個(gè)隨機(jī)值
  • SYN:同步標(biāo)志 跺讯,同步序列編號(hào)(Synchronize Sequence Numbers)枢贿。該標(biāo)志僅在三次握手建立TCP連接時(shí)有效。它提示TCP連接的服務(wù)端檢查序列編號(hào)抬吟,該序列編號(hào)為T(mén)CP連接初始端(一般是客戶端)的初始序列編號(hào)萨咕。在這里统抬,可以把 TCP序列編號(hào)看作是一個(gè)范圍從0到4火本,294,967聪建,295的32位計(jì)數(shù)器钙畔。通過(guò)TCP連接交換的數(shù)據(jù)中每一個(gè)字節(jié)都經(jīng)過(guò)序列編號(hào)。在TCP報(bào)頭中的序列編號(hào)欄包括了TCP分段中第一個(gè)字節(jié)的序列編號(hào)
    • 客戶端向服務(wù)器發(fā)送一個(gè)同步數(shù)據(jù)包請(qǐng)求建立連接金麸,該數(shù)據(jù)包中擎析,初始序列號(hào)(ISN)是客戶端隨機(jī)產(chǎn)生的一個(gè)值,確認(rèn)號(hào)是0;
    • 服務(wù)器收到這個(gè)同步請(qǐng)求數(shù)據(jù)包后揍魂,會(huì)對(duì)客戶端進(jìn)行一個(gè)同步確認(rèn)桨醋。這個(gè)數(shù)據(jù)包中,序列號(hào)(ISN)是服務(wù)器隨機(jī)產(chǎn)生的一個(gè)值现斋,確認(rèn)號(hào)是客戶端的初始序列號(hào)+1喜最;
    • 客戶端收到這個(gè)同步確認(rèn)數(shù)據(jù)包后,再對(duì)服務(wù)器進(jìn)行一個(gè)確認(rèn)庄蹋。該數(shù)據(jù)包中瞬内,序列號(hào)是上一個(gè)同步請(qǐng)求數(shù)據(jù)包中的確認(rèn)號(hào)值,確認(rèn)號(hào)是服務(wù)器的初始序列號(hào)+1限书。
  • ISN代表什么虫蝶?意義何在?
    • ISN倦西,發(fā)送方的字節(jié)數(shù)據(jù)編號(hào)的原點(diǎn)能真,讓對(duì)方生成一個(gè)合法的接收窗口。
  • ISN是固定不變的嗎扰柠?
    • 動(dòng)態(tài)隨機(jī)舟陆。
  • ISN為何要?jiǎng)討B(tài)隨機(jī)?
    • 增加安全性耻矮,為了避免被第三方猜測(cè)到秦躯,從而被第三方偽造的RST報(bào)文Reset。

TCP建立連接的三次握手中裆装,第二次握手發(fā)送的包會(huì)包含的標(biāo)記踱承,最正確的描述是?

網(wǎng)絡(luò)層相關(guān)

網(wǎng)絡(luò)七層協(xié)議

Charles原理

HTTPS的連接建立流程

TCP分片 和 IP分片

Cookie和Session

  • Cookie

    • HTTP協(xié)議是無(wú)狀態(tài)的哨免,服務(wù)器中沒(méi)有保存客戶端的狀態(tài)茎活,客戶端必須每次帶上自己的狀態(tài)去請(qǐng)求服務(wù)器
      基于HTTP這種特點(diǎn),就產(chǎn)生了cookie/session
    • cookie主要是用來(lái)記錄用戶狀態(tài)琢唾,區(qū)分用戶载荔,狀態(tài)保存在客戶端
  • Session

    • Web應(yīng)用程序中還經(jīng)常使用Session來(lái)記錄客戶端狀態(tài)。Session是服務(wù)器端使用的一種記錄客戶端狀態(tài)的機(jī)制采桃,使用上比Cookie簡(jiǎn)單一些懒熙,相應(yīng)的也增加了服務(wù)器的存儲(chǔ)壓力。
  • 區(qū)別

    • cookie數(shù)據(jù)存放在客戶的瀏覽器上普办,session數(shù)據(jù)放在服務(wù)器上工扎。
    • cookie相比session不是很安全,別人可以分析存放在本地的cookie并進(jìn)行cookie欺騙,考慮到安全應(yīng)當(dāng)使用session衔蹲。
    • session會(huì)在一定時(shí)間內(nèi)保存在服務(wù)器上肢娘。當(dāng)訪問(wèn)增多,會(huì)比較占用你服務(wù)器的性能,考慮到減輕服務(wù)器性能方面,應(yīng)當(dāng)使用cookie橱健。
    • 單個(gè)cookie保存的數(shù)據(jù)不能超過(guò)4K而钞,很多瀏覽器都限制一個(gè)站點(diǎn)最多保存20個(gè)cookie。而session存儲(chǔ)在服務(wù)端拘荡,可以無(wú)限量存儲(chǔ)
    • 所以:將登錄信息等重要信息存放為session;其他信息如果需要保留笨忌,可以放在cookie中

DNS是什么?DNS解析過(guò)程

  • 定義

    • 域名系統(tǒng)(Domain Name System俱病,DNS)
    • 因特網(wǎng)上的主機(jī)官疲,可以使用多種方式標(biāo)識(shí):
      一種標(biāo)識(shí)方法就是用它的主機(jī)名,比如·www.baidu.com亮隙、www.google.com途凫、gaia.cs.umass.edu
  • 另外一種方式,就是直接使用定長(zhǎng)的溢吻、有著清晰層次結(jié)構(gòu)的IP地址

  • 區(qū)別

    • 主機(jī)名:方便人們記憶和接受维费,但長(zhǎng)度不一、沒(méi)有規(guī)律的字符串促王,路由器并不方便處理
    • IP地址:路由器方便處理犀盟,不便于人們記憶
  • 為了折衷這兩種方式,需要一種能進(jìn)行主機(jī)名到IP地址轉(zhuǎn)換的目錄服務(wù)蝇狼,就是 域名系統(tǒng)(Domain Name System阅畴,DNS)

  • 作用

    • 將用戶提供的主機(jī)名解析為IP地址
  • DNS解析過(guò)程(以www.163.com為例:)

    • 打開(kāi)瀏覽器,輸入一個(gè)域名(www.163.com)迅耘〖妫客戶端會(huì)發(fā)出一個(gè)DNS請(qǐng)求到本地DNS服務(wù)器(本地DNS服務(wù)器一般都是你的網(wǎng)絡(luò)接入服務(wù)器商提供,比如中國(guó)電信颤专,中國(guó)移動(dòng))
    • 本地DNS服務(wù)器會(huì)首先查詢它的緩存記錄纽哥,如果緩存中有此條記錄,直接返回結(jié)果栖秕。如果沒(méi)有春塌,向DNS根服務(wù)器進(jìn)行查詢。
    • 根DNS服務(wù)器沒(méi)有記錄具體的域名和IP地址的對(duì)應(yīng)關(guān)系簇捍,而是給出域服務(wù)器的地址只壳,告訴他可以到域服務(wù)器上去繼續(xù)查詢
    • 本地DNS服務(wù)器繼續(xù)向域服務(wù)器發(fā)出請(qǐng)求,在這個(gè)例子中垦写,請(qǐng)求的對(duì)象是.com域服務(wù)器吕世。
    • .com域服務(wù)器收到請(qǐng)求之后彰触,也不會(huì)直接返回域名和IP地址的對(duì)應(yīng)關(guān)系梯投,而是告訴本地DNS服務(wù)器,你的域名的解析服務(wù)器的地址。
    • 最后分蓖,本地DNS服務(wù)器向域名的解析服務(wù)器發(fā)出請(qǐng)求尔艇,這時(shí)就能收到一個(gè)域名和IP地址對(duì)應(yīng)關(guān)系,
    • 本地DNS服務(wù)器不僅要把IP地址返回給用戶電腦么鹤,還要把這個(gè)對(duì)應(yīng)關(guān)系保存在緩存中终娃,以備下次別的用戶查詢時(shí),可以直接返回結(jié)果蒸甜,加快網(wǎng)絡(luò)訪問(wèn)棠耕。

HTTP和HTTPS的區(qū)別?Https為什么更加安全柠新?

  • 傳輸信息安全性不同
    • http協(xié)議:是超文本傳輸協(xié)議窍荧,信息是明文傳輸。(如果攻擊者截取了Web瀏覽器和網(wǎng)站服務(wù)器之間的傳輸報(bào)文恨憎,就可以直接讀懂其中的信息)
    • https協(xié)議:是具有安全性的ssl加密傳輸協(xié)議蕊退,為瀏覽器和服務(wù)器之間的通信加密,確保數(shù)據(jù)傳輸?shù)陌踩?/li>
  • 連接方式不同
    • http協(xié)議:http的連接很簡(jiǎn)單憔恳,是無(wú)狀態(tài)的瓤荔。
    • https協(xié)議:是由SSL+HTTP協(xié)議構(gòu)建的可進(jìn)行加密傳輸、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議钥组。
  • 端口不同
    • http協(xié)議:使用的端口是80输硝。
    • https協(xié)議:使用的端口是443.
  • 證書(shū)申請(qǐng)方式不同
    • http協(xié)議:免費(fèi)申請(qǐng)。
    • https協(xié)議:需要到ca申請(qǐng)證書(shū)程梦,一般免費(fèi)證書(shū)很少腔丧,需要交費(fèi)。

ipv6

  • IPv4 是互聯(lián)網(wǎng)協(xié)議( Internet Protocol作烟, IP)的第四版愉粤,也是第一個(gè)被廣泛使用,目前運(yùn)用最多的互聯(lián)網(wǎng)技術(shù)協(xié)議拿撩。
    IPv4 地址格式是這個(gè)樣子: 123.58.25.46
  • IPv6 是 IPv4 的下一個(gè)版本 衣厘。 IPv6 地址長(zhǎng)度為 128 位,地址空間增加了 2128-232 個(gè)压恒,它在提高安全性方面相比前代有著較大的提升影暴。此外,身份認(rèn)證和隱私權(quán)也是 IPv6 的關(guān)鍵特性探赫。
    IPv6 地址格式是這個(gè)樣子: 2001:da8:215:4009:250:56ff:fe97:40c7 型宙。

十一、UI

Storyboard/Xib和純代碼UI相比伦吠,有哪些優(yōu)缺點(diǎn)妆兑?

  • storyboard/xib優(yōu)點(diǎn)
    • 簡(jiǎn)單直接魂拦。直接通過(guò)拖拽和點(diǎn)選即可完成配置。
    • 跳轉(zhuǎn)關(guān)系清楚
  • 缺點(diǎn):
    • 協(xié)作沖突(多人提交代碼)
    • 很難做到頁(yè)面繼承和重用
    • 不便于進(jìn)行模塊化管理
    • 影響性能(多圖層渲染)

Auto Layout

Auto Layout基本原理

  • 一個(gè)約束本質(zhì)上就是一個(gè)表示視圖布局關(guān)系的線性方程搁嗓。一個(gè)完整的約束方程式

控制器

描述一下 UIViewController 的生命周期

  • 當(dāng) UIViewController 被創(chuàng)建并在屏幕上顯示芯勘,其生命周期方法執(zhí)行順序?yàn)椋?
    • alloc:創(chuàng)建對(duì)象,分配空間腺逛;
    • init (initWithNibName|initWithCoder) :初始化對(duì)象荷愕,初始化數(shù)據(jù);
    • loadView: 完成一些關(guān)鍵 view 的初始化工作棍矛,加載 view漠魏;
    • viewDidLoad:載入完成敛纲,可以進(jìn)行自定義數(shù)據(jù)以及動(dòng)態(tài)創(chuàng)建其他控件彤避;
    • viewWillAppear:表示視圖即將出現(xiàn)在屏幕之前屡久;
    • viewWillLayoutSubviews:將要對(duì)子視圖進(jìn)行調(diào)整;
    • viewDidLayoutSubviews:完成對(duì)子視圖的調(diào)整慨绳;
    • viewDidAppear:表示視圖已在屏幕上完成渲染掉冶;
    • viewWillDisappear:表示視圖將要被移除,在程序執(zhí)行完但屏幕上視圖還未消失的時(shí)候執(zhí)行脐雪;
    • viewDidDisappear:表示視圖已經(jīng)被移除厌小,在程序執(zhí)行完屏幕視圖也消失的時(shí)候執(zhí)行;
    • dealloc :視圖被銷(xiāo)毀战秋,此處需要對(duì)在 init 和 viewDidLoad 中創(chuàng)建的對(duì)象進(jìn)行釋放璧亚;
    • didReceiveMemoryWarning:內(nèi)存警告。

如果頁(yè)面 A 跳轉(zhuǎn)到 頁(yè)面 B脂信,A 的 viewDidDisappear 方法和 B 的 viewDidAppear 方法哪個(gè)先調(diào)用癣蟋?

  • A -->viewWillDisappear
  • B-->viewWillAppear
  • A-->viewDidDisappear
  • B-->viewDidAppear

離屏渲染,隱式動(dòng)畫(huà)和顯式動(dòng)畫(huà)相關(guān)

離屏渲染觸發(fā)條件

  • 背景色狰闪、邊框疯搅、背景色+邊框,再加上圓角+裁剪埋泵,因?yàn)?contents = nil 沒(méi)有需要裁剪處理的內(nèi)容幔欧,所以不會(huì)造成離屏渲染。
  • 一旦為contents設(shè)置了內(nèi)容丽声,無(wú)論是圖片礁蔗、繪制內(nèi)容、有圖像信息的子視圖等雁社,再加上圓角+裁剪浴井,就會(huì)觸發(fā)離屏渲染。

事件響應(yīng)過(guò)程(響應(yīng)鏈)

事件的傳遞 (尋找最合適的view的過(guò)程)

  • 當(dāng)一個(gè)事件發(fā)生后霉撵,事件會(huì)從父控件傳給子控件 (UIApplication->UIWindow->UIView->initial view)

事件的響應(yīng)

  • 首先看initial view能否處理這個(gè)事件磺浙,如果不能則會(huì)將事件傳遞給其上級(jí)視圖(inital view的superView)
  • 如果上級(jí)視圖仍然無(wú)法處理則會(huì)繼續(xù)往上傳遞洪囤;一直傳遞到視圖控制器view controller,首先判斷視圖控制器的根視圖view是否能處理此事件
  • 如果不能則接著判斷該視圖控制器能否處理此事件屠缭,如果還是不能則繼續(xù)向上傳遞
  • 一直到window箍鼓,如果window還是不能處理此事件則繼續(xù)交給application處理崭参,如果最后application還是不能處理此事件則將其丟棄

事件順序

  • 事件的傳遞是從上到下(父控件到子控件)
  • 事件的響應(yīng)是從下到上(順著響應(yīng)者鏈條向上傳遞:子控件到父控件)

事件穿透

  • 假設(shè)有一個(gè)黃色控件和白色控件呵曹,白色空間覆蓋在黃色控件上
  • 點(diǎn)擊白色view想要黃色view來(lái)響應(yīng)該事件,就是所謂的穿透
  • 方法一
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    CGPoint yellowPoint = [self convertPoint:point toView:_yellowView];
    if ([_yellowView pointInside:yellowPoint withEvent:event]) {
        return _yellowView;
    }
    return [super hitTest:point withEvent:event];
}
  • 方法二
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    CGPoint yellowPoint =[_yellowView convertPoint:point fromView:self];
    if ([_yellowView pointInside:yellowPoint withEvent:event]){
        return NO;
    } else {
        return [super pointInside:point withEvent:event];
    }
}

手勢(shì)識(shí)別

手勢(shì)識(shí)別的過(guò)程

這里主要說(shuō)的是關(guān)于runloop的概念點(diǎn)

  • 當(dāng)_UIApplicationHandleEventQueue()識(shí)別了一個(gè)手勢(shì)時(shí)何暮,其首先會(huì)調(diào)用Cancel奄喂,將當(dāng)前的 touchesBegin/Move/End 系列回調(diào)打斷
  • 隨后系統(tǒng)將對(duì)應(yīng)的 UIGestureRecognizer 標(biāo)記為待處理
  • 蘋(píng)果注冊(cè)了一個(gè) Observer 監(jiān)測(cè) BeforeWaiting (Loop即將進(jìn)入休眠) 事件
  • 這個(gè) Observer 的回調(diào)函數(shù)是 _UIGestureRecognizerUpdateObserver(),其內(nèi)部會(huì)獲取所有剛被標(biāo)記為待處理的 GestureRecognizer海洼,并執(zhí)行GestureRecognizer 的回調(diào)跨新。
  • 當(dāng)有 UIGestureRecognizer 的變化(創(chuàng)建/銷(xiāo)毀/狀態(tài)改變)時(shí),這個(gè)回調(diào)都會(huì)進(jìn)行相應(yīng)處理

十二坏逢、其他

數(shù)組和鏈表

數(shù)組和鏈表的區(qū)別

  • 數(shù)組在內(nèi)存上給出了連續(xù)的空間
  • 鏈表域帐,內(nèi)存地址上可以是不連續(xù)的,每個(gè)鏈表的節(jié)點(diǎn)包括原來(lái)的內(nèi)存和下一個(gè)節(jié)點(diǎn)的信息(單向的一個(gè),雙向鏈表的話,會(huì)有兩個(gè))

數(shù)組和鏈表的優(yōu)缺點(diǎn)

  • 數(shù)組:
    • 優(yōu)點(diǎn): 使用方便是整,查詢效率比鏈表高肖揣,內(nèi)存為一連續(xù)的區(qū)域
    • 缺點(diǎn): 大小固定,不適合動(dòng)態(tài)存儲(chǔ)浮入,不方便動(dòng)態(tài)添加
  • 鏈表:
    • 優(yōu)點(diǎn): 可動(dòng)態(tài)添加刪除龙优,大小可變
    • 缺點(diǎn): 只能通過(guò)順次指針訪問(wèn),查詢效率低

靜態(tài)庫(kù)事秀、動(dòng)態(tài)庫(kù)

什么是庫(kù)彤断?

  • 共享代碼,實(shí)現(xiàn)代碼的復(fù)用易迹,一般分為靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)宰衙。

靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的區(qū)別

  • 靜態(tài)庫(kù)(.a和.framework 樣式):
    • 鏈接時(shí)完整的拷貝到可執(zhí)行文件,多次使用多次拷貝睹欲,造成冗余菩浙,使包變的更大
    • 但是代碼裝載速度快,執(zhí)行速度略比動(dòng)態(tài)庫(kù)快
  • 動(dòng)態(tài)庫(kù):(.dylib和.framework)
  • 鏈接時(shí)不復(fù)制句伶,程序運(yùn)行時(shí)由系統(tǒng)加在到內(nèi)存中劲蜻,供系統(tǒng)調(diào)用,系統(tǒng)加在一次考余,多次使用先嬉,共用節(jié)省內(nèi)存。

為什么framework既是靜態(tài)又是動(dòng)態(tài)楚堤?

  • 系統(tǒng)的framework是動(dòng)態(tài)的疫蔓,自己創(chuàng)建的是靜態(tài)的含懊。

.a 和 .framework 的區(qū)別是什么?

  • .a 是單純的二進(jìn)制文件衅胀,需要 .h文件配合岔乔,不能直接使用
  • .framework是二進(jìn)制文件+資源文件,可以直接使用滚躯。 .framework = .a + .h + sorrceFile(資源文件)

NSDictionary

NSDictionary底層實(shí)現(xiàn)原理

  • 在OC中NSDictionary是使用hash表來(lái)實(shí)現(xiàn)key和value的映射和存儲(chǔ)的雏门。
    hash表存儲(chǔ)過(guò)程簡(jiǎn)單介紹:
  • 根據(jù)key值計(jì)算出它的hash值h;
  • 假設(shè)箱子的個(gè)數(shù)是n掸掏,那么鍵值對(duì)應(yīng)該放在第(h%n)個(gè)箱子中茁影。
  • 如果該箱子中已經(jīng)有了鍵值對(duì),就是用開(kāi)放尋址法或者拉鏈法解決沖突丧凤。使用拉鏈法解決哈希沖突時(shí)募闲,每個(gè)箱子其實(shí)是一個(gè)鏈表,屬于同一個(gè)箱子的所有鍵值對(duì)都會(huì)排列在鏈表中愿待。

十三浩螺、Objective-C 類(lèi)和對(duì)象相關(guān)

語(yǔ)言基礎(chǔ)

Objective-C 與 C、C++ 之間的聯(lián)系和區(qū)別是什么仍侥?

  • Objective-C 與 C++ 都是從 C 演化而來(lái)的面向?qū)ο笳Z(yǔ)言要出,兩者都兼容標(biāo)準(zhǔn) C 語(yǔ)言。Objective-C 與 C++ 的區(qū)別主要有以下幾點(diǎn):
    Objective-C 是完全動(dòng)態(tài)的访圃,而 C++是部分動(dòng)態(tài)的厨幻;
    Objective-C 不支持多重繼承, 而 C++ 支持,不過(guò) Objective-C 通過(guò) proxy(代理) 或 Category(類(lèi)別)可以更優(yōu)雅地實(shí)現(xiàn)這一特性腿时;
    Objective-C 通過(guò)互相傳遞消息實(shí)現(xiàn)函數(shù)調(diào)用况脆,而 C++ 直接進(jìn)行函數(shù)調(diào)用;
    Objective-C 采用 protocol 協(xié)議(非正式和正式)的形式來(lái)定義接口批糟,而 C++ 采用虛函數(shù)的形式來(lái)定義接口格了;
    Objective-C 沒(méi)有 C++ 里有的構(gòu)造函數(shù)和析構(gòu)函數(shù), 其對(duì)應(yīng)物為 alloc-init/free。

如何理解 Objective-C 為動(dòng)態(tài)運(yùn)行時(shí)語(yǔ)言徽鼎?

  • 主要是將數(shù)據(jù)類(lèi)型的確定由編譯時(shí)盛末,推遲到了運(yùn)行時(shí)。簡(jiǎn)單來(lái)說(shuō)否淤,運(yùn)行時(shí)機(jī)制使我們直到運(yùn)行時(shí)才去決定一個(gè)對(duì)象的類(lèi)別悄但,以及調(diào)用該類(lèi)別對(duì)象指定方法。

Objective-C 中是否支持垃圾回收機(jī)制石抡?

Objective-C 是支持垃圾回收機(jī)制的檐嚣,但是在 iOS 中不可用,iOS 開(kāi)發(fā)只支持手動(dòng)內(nèi)存管理和 ARC(Automatic Reference Counting)啰扛。

Objective-C 中堆和棧的區(qū)別是什么嚎京?

堆空間的內(nèi)存是動(dòng)態(tài)分配的嗡贺,一般用于存放 Objective-C 對(duì)象,并且需要手動(dòng)釋放內(nèi)存鞍帝,不及時(shí)回收容易產(chǎn)生內(nèi)存泄露诫睬,ARC 環(huán)境下 Objective-C 對(duì)象由編譯器管理,不需要手動(dòng)釋放帕涌;椛惴玻空間的內(nèi)存由系統(tǒng)自動(dòng)分配,一般存放非 Objective-C 對(duì)象的基本數(shù)據(jù)類(lèi)型宵膨,例如 int架谎、float 等炸宵,由系統(tǒng)編譯器管理辟躏,不需要手動(dòng)管理內(nèi)存。

類(lèi)土全、對(duì)象

OC的類(lèi)信息存放在哪里捎琐?

  • 對(duì)象方法、屬性裹匙、成員變量瑞凑、協(xié)議信息,存放在class對(duì)象中
  • 類(lèi)方法概页,存放在meta-class對(duì)象中
  • 成員變量的具體值籽御,存放在instance對(duì)象中

類(lèi)與結(jié)構(gòu)體的區(qū)別

  • 結(jié)構(gòu)體只能封裝數(shù)據(jù),而類(lèi)還可以封裝行為
  • 賦值:結(jié)構(gòu)體是拷貝惰匙,對(duì)象之間是地址
  • 結(jié)構(gòu)體變量分配在椉继停空間(如果是一個(gè)局部變量的情況下),而對(duì)象分配在堆空間

class项鬼、meta-class的結(jié)構(gòu)

struct objc_class : objc_object {
    Class ISA;
    Class superclass;
    cache_t cache;             // 方法緩存
    class_data_bits_t bits;    // 用于獲取具體的類(lèi)信息
}

& FAST_DATA_MASK

struct class_rw_t {
    uint32_t flags;
    uint32_t version;
    const class_ro_t *ro;        // 
    method_array_t methods;      // 方法列表
    property_array_t properties; // 屬性列表
    protocol_array_t protocols;  // 協(xié)議列表
    Class firstSubclass;
    Class nextSiblingClass;
    char *demangledName;
}

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name; // 類(lèi)名
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars; // 成員變量列表

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
}

對(duì)象的isa指針指向哪里哑梳?superclass指針呢?

  • instance的isa指向class
  • class的isa指向meta-class
  • meta-class的isa指向基類(lèi)的meta-class
  • class的superclass指向父類(lèi)的class(如果沒(méi)有父類(lèi)绘盟,superclass指針為nil)
  • meta-class的superclass指向父類(lèi)的meta-class
  • 基類(lèi)的meta-class的superclass指向基類(lèi)的class

方法調(diào)用查找

  • 對(duì)象方法的調(diào)用:通過(guò)instance的isa找到class鸠真,最后找到對(duì)象方法的實(shí)現(xiàn)進(jìn)行調(diào)用
  • 類(lèi)方法的調(diào)用:當(dāng)調(diào)用類(lèi)方法時(shí),通過(guò)class的isa找到meta-class龄毡,最后找到類(lèi)方法的實(shí)現(xiàn)進(jìn)行調(diào)用

class對(duì)象的superclass指針

  • Student : Person : NSObject
  • 當(dāng)Student的instance對(duì)象要調(diào)用Personal的對(duì)象方法時(shí):
    • 先通過(guò)isa找到Student的class吠卷,然后通過(guò)superclass找到Person的class,最后找到對(duì)象方法的實(shí)現(xiàn)進(jìn)行調(diào)用

meta-class對(duì)象的superclass指針

  • 當(dāng)Student的class要調(diào)用Person的類(lèi)方法時(shí)
    • 先通過(guò)isa找到Student的meta-class沦零,然后通過(guò)superclass找到Person的meta-class祭隔,最后找到類(lèi)方法的實(shí)現(xiàn)進(jìn)行調(diào)用

對(duì)象相關(guān)

屬性可以與set方法和get方法 三者同時(shí)存在嗎,如果不行,請(qǐng)說(shuō)明原因?

換句話說(shuō)就是:iOS中同時(shí)重寫(xiě)屬性的set與get方法時(shí),為什么訪問(wèn)不了下劃線屬性?

  • 原因:
    • 屬性的setter方法和getter方法是不能同時(shí)進(jìn)行重寫(xiě)蠢终,
    • 因?yàn)樾蛉粒坏┠阃瑫r(shí)重寫(xiě)了這兩個(gè)方法茴她,那么系統(tǒng)就不會(huì)幫你生成這個(gè)成員變量了
  • 解決方式:
    • @synthesize authType = _authType;
    • 意思是,將屬性的setter,getter方法程奠,作用于這個(gè)變量丈牢。

iOS如何實(shí)現(xiàn)多繼承,代碼書(shū)寫(xiě)一下

  • 使用協(xié)議組合
  • NSProxy
  • 比如我有兩個(gè)協(xié)議, 分別是 YFPerson,YFChild
#import <Foundation/Foundation.h>

@protocol YFPerson <NSObject>
@required
@property (nonatomic,copy,readonly)NSString *name;
- (NSInteger) age;
- (void)eat;
- (void)sleep;
@optional
- (void)play;
- (void)setName:(NSString *)newName;
@end
#import <Foundation/Foundation.h>

@protocol YFChild <NSObject>
@required
- (NSString *)nickname;
- (void)introduceMyselfWithName:(NSString *)name nickname:(NSString *)nickname age:(NSInteger)age;
@optional
- (void)study;
@end
  • 那么, 我在新創(chuàng)建的一個(gè) YFStudent 類(lèi)中, 只要遵守上面兩個(gè)協(xié)議, 實(shí)現(xiàn)協(xié)議里的方法, 就可以在一個(gè)類(lèi)中,實(shí)現(xiàn)多個(gè)協(xié)議中的方法了.
- (NSString *)nickname
{
    return @"龍兒";
}

- (NSInteger)age
{
    return 19;
}

- (void)sleep{
    NSLog(@"sleep");
}

- (void)eat{
    NSLog(@"eat");
}

- (void)introduceMyselfWithName:(NSString *)name nickname:(NSString *)nickname age:(NSInteger)age
{
    NSLog(@"我叫%@,小名%@,今天%@歲了", name,nickname,@(age));
}
  • 這樣, 我在控制器的 viewDidLoad 方法中,創(chuàng)建 YFStudent 對(duì)象, 然后就可以調(diào)協(xié)議中的任何方法了
- (void)viewDidLoad {
    [super viewDidLoad];
    
    YFStudent *student = [[YFStudent alloc]init];
    student.name = @"小龍女";
    [student eat];
    [student sleep];
    [student introduceMyselfWithName:student.name nickname:student.nickname age:student.age];
}

關(guān)鍵字和系統(tǒng)提供的類(lèi)

簡(jiǎn)述__kindof關(guān)鍵字

  • 表示當(dāng)前類(lèi)或者他的子類(lèi)(iOS9推出的瞄沙,一般用于消除警告)
  • 聲明數(shù)組存貯指定UIView類(lèi)型的元素
  • 如果元素被賦值為UIWebView或UIButton這樣的子類(lèi)型己沛,編譯器就會(huì)報(bào)警告
  • 為了解決這個(gè)問(wèn)題,__kindof就應(yīng)運(yùn)而生距境。
@property (nonatomic, strong) NSMutableArray<UIView *> *viewList;
??
@property (nonatomic, strong) NSMutableArray <__kindof UIView *> * viewList;

新版Xcode這個(gè)問(wèn)題已經(jīng)優(yōu)化申尼,例子屬于老實(shí)例,理解這個(gè)意思就行了

關(guān)于NSProxy

  • 消除NSTimer循環(huán)引用
  • 多繼承

@interface LYBird ()
@property (nonatomic, copy) NSString *bridName;
@end

@implementation LYBird
- (void)onFly {
    NSLog(@"%@正在飛翔", self.bridName);
}
@end



@interface LYFish ()
@property (nonatomic, copy) NSString *fishName;
@end

@implementation LYFish
- (void)onSwimming {
    NSLog(@"%@正在游泳", self.fishName);
}
@end



- (void)viewDidLoad {
    [super viewDidLoad];
    
    LYBird *bird = [[LYBird alloc] init];
    LYFish *fish = [[LYFish alloc] init];
    
    LYProxy *proxy = [LYProxy alloc];
    
    [proxy transformToObject:bird];
    [proxy performSelector:@selector(setBridName:) withObject:@"鷹隼"];
    [proxy performSelector:@selector(onFly)];
    
    [proxy transformToObject:fish];
    [proxy performSelector:@selector(setFishName:) withObject:@"??"];
    [proxy performSelector:@selector(onSwimming)];
}

id和NSObject ,instancetype的區(qū)別垫桂?

  • id和instancetype都可以做方法的返回值师幕。
  • id類(lèi)型的返回值在編譯期不能判斷對(duì)象的真實(shí)類(lèi)型,即非關(guān)聯(lián)返回類(lèi)型
  • instancetype類(lèi)型的返回值在編譯期可以判斷對(duì)象的真實(shí)類(lèi)型诬滩,即關(guān)聯(lián)返回類(lèi)型霹粥。
  • id可以用來(lái)定義變量, 可以作為返回值, 可以作為形參
  • instancetype只能用于作為返回值。

nil疼鸟、Nil后控、NULL、NSNull的區(qū)別空镜?

  • nil:指向一個(gè)對(duì)象的空指針
  • Nil:指向一個(gè)類(lèi)的空指針,
  • NULL:指向其他類(lèi)型(如:基本類(lèi)型浩淘、C類(lèi)型)的空指針, 用于對(duì)非對(duì)象指針賦空值.
  • NSNull:在集合對(duì)象中,表示空值的對(duì)象.

NSNull在Objective-C中是一個(gè)類(lèi) .NSNull有 + (NSNull *)null; 單例方法.多用于集合(NSArray,NSDictionary)中值為空的對(duì)象.

NSArray *array = [NSArray arrayWithObjects: [[NSObject alloc] init], [NSNull null], @"aaa", nil, [[NSObject alloc] init], [[NSObject alloc] init], nil];
NSLog(@"%ld", array.count);// 輸出 3吴攒,NSArray以nil結(jié)尾

十四张抄、Swift

語(yǔ)言基礎(chǔ)

對(duì)比 Objective-C,Swift 有什么優(yōu)勢(shì)舶斧?

  • 對(duì)比 Objective-C欣鳖,Swift 優(yōu)勢(shì)有如下幾點(diǎn):
    • 語(yǔ)法簡(jiǎn)單易讀、代碼編寫(xiě)更加簡(jiǎn)潔茴厉、清晰泽台、易于維護(hù);
    • 速度更快矾缓,運(yùn)算性能更高怀酷;
    • 是一門(mén)類(lèi)型安全語(yǔ)言,代碼里值的類(lèi)型非常明確嗜闻;
    • 泛型蜕依、結(jié)構(gòu)體、枚舉都很強(qiáng)大;
    • 擁有便捷的函數(shù)式編程样眠。

在 Swift 中友瘤,為什么 map 函數(shù)必不可少?該在什么情況下使用它檐束?

  • map 函數(shù)屬于高階函數(shù)辫秧,使用高階函數(shù)編程不僅可以優(yōu)化代碼,還能夠并行執(zhí)行被丧。map 函數(shù)通常用于數(shù)組中的每個(gè)元素盟戏,通過(guò)指定的方法進(jìn)行轉(zhuǎn)換,返回一個(gè)泛型的數(shù)組甥桂。

在 Swift 中使用擴(kuò)展可以完成什么任務(wù)柿究?

  • Swift 中的擴(kuò)展可以動(dòng)態(tài)地給類(lèi)增加功能。在 Swift 中使用擴(kuò)展可以完成的任務(wù)有如下幾點(diǎn):
    • 添加計(jì)算型屬性和計(jì)算靜態(tài)屬性黄选;
    • 定義實(shí)例方法和類(lèi)型方法蝇摸;
    • 提供新的構(gòu)造器;
    • 定義下標(biāo)糕簿;
    • 定義和使用新的嵌套類(lèi)型探入;
    • 使一個(gè)已有類(lèi)型符合某個(gè)接口狡孔。

在 Swift 中懂诗,什么時(shí)候用結(jié)構(gòu)體,什么時(shí)候用類(lèi)苗膝?

  • 在 Swift 開(kāi)發(fā)環(huán)境中殃恒,結(jié)構(gòu)體和類(lèi)都是構(gòu)建代碼所用的一種通用且靈活的構(gòu)造體,都擁有屬性辱揭,都可以定義方法离唐,但兩者存在很多不同的特性,類(lèi)是引用類(lèi)型问窃、支持繼承亥鬓,而結(jié)構(gòu)體是值類(lèi)型、不支持繼承域庇。此外嵌戈,由于結(jié)構(gòu)體的方法調(diào)用是靜態(tài)綁定,而類(lèi)的方法調(diào)用是動(dòng)態(tài)實(shí)現(xiàn)的听皿,所以在運(yùn)行時(shí)熟呛,結(jié)構(gòu)體的性能更優(yōu)。按照通用準(zhǔn)則尉姨,當(dāng)符合以下一條或多條情形時(shí)應(yīng)考慮創(chuàng)建一個(gè)結(jié)構(gòu)體:
    • 結(jié)構(gòu)體的主要目的是為了封裝一些相關(guān)的簡(jiǎn)單數(shù)據(jù)值庵朝;
    • 在賦予或者傳遞結(jié)構(gòu)實(shí)例時(shí),需要封裝的數(shù)據(jù)值被拷貝而不是引用;
  • 在其他的情況下九府,定義一個(gè)類(lèi)椎瘟,并創(chuàng)建這個(gè)類(lèi)的實(shí)例,通過(guò)引用來(lái)管理和傳遞侄旬。

十五降传、App優(yōu)化等

系統(tǒng)架構(gòu)

iOS 系統(tǒng)可分為四級(jí)結(jié)構(gòu),由上至下分別為可觸摸層(Cocoa Touch Layer)勾怒、媒體層(Media Layer)婆排、核心服務(wù)層(Core Services Layer)、核心操作系統(tǒng)層(Core OS Layer)笔链,每個(gè)層級(jí)提供不同的服務(wù)段只。

低層級(jí)結(jié)構(gòu)提供基礎(chǔ)服務(wù)如文件系統(tǒng)、內(nèi)存管理鉴扫、I/O 操作等赞枕,高層級(jí)結(jié)構(gòu)建立在低層級(jí)結(jié)構(gòu)之上提供具體服務(wù)如 UI 控件、文件訪問(wèn)等坪创。

可觸摸層主要提供用戶交互相關(guān)的服務(wù)如界面控件炕婶、事件管理、通知中心莱预、地圖柠掂,包含的框架主要有 UIKit Framework、Event Kit UI Framework依沮、Notification Center Framework涯贞、Map Kit Framework等。

媒體層主要提供圖像引擎危喉、音頻引擎宋渔、視頻引擎框架。

核心服務(wù)層為程序提供基礎(chǔ)的系統(tǒng)服務(wù)例如網(wǎng)絡(luò)訪問(wèn)辜限、瀏覽器引擎皇拣、定位、文件訪問(wèn)薄嫡、數(shù)據(jù)庫(kù)訪問(wèn)等氧急,主要包含框架有 CFNetwork Framework、Core Data Framework岂座、Core Location Framework 和 Web Kit Framework 等态蒂。

核心系統(tǒng)層為上層結(jié)構(gòu)提供最基礎(chǔ)的服務(wù)如操作系統(tǒng)內(nèi)核服務(wù)、本地認(rèn)證费什、安全钾恢、加速等手素,主要包含框架有 Accelerate Framework、External Accessory Framework 和 Security Framework 等瘩蚪。

簡(jiǎn)述一下 iOS 的系統(tǒng)架構(gòu)

iOS 系統(tǒng)可分為四級(jí)結(jié)構(gòu)泉懦,由上至下分別為 可觸摸層(Cocoa Touch Layer)、媒體層(Media Layer)疹瘦、核心服務(wù)層(Core Services Layer)崩哩、核心操作系統(tǒng)層(Core OS Layer),每個(gè)層級(jí)提供不同的服務(wù)言沐〉肃冢可觸摸層主要提供用戶交互相關(guān)的服務(wù)如界面控件、事件管理险胰、通知中心汹押、地圖;媒體層主要提供圖像引擎起便、音頻引擎棚贾、視頻引擎框架;核心服務(wù)層為程序提供基礎(chǔ)的系統(tǒng)服務(wù)例如網(wǎng)絡(luò)訪問(wèn)榆综、瀏覽器引擎妙痹、定位、文件訪問(wèn)鼻疮、數(shù)據(jù)庫(kù)訪問(wèn)等怯伊;核心系統(tǒng)層為上層結(jié)構(gòu)提供最基礎(chǔ)的服務(wù)如操作系統(tǒng)內(nèi)核服務(wù)、本地認(rèn)證陋守、安全震贵、加速等。

Crash

Crash監(jiān)控

  • NSSetUncaughtExceptionHandler可以統(tǒng)計(jì)閃退

App啟動(dòng)

  • App啟動(dòng)分為兩種:
    • 冷啟動(dòng)(Cold Launch):從零開(kāi)始啟動(dòng)app
    • 熱啟動(dòng)(Warm Launch):app已在內(nèi)存中水评,在后臺(tái)存活,再次點(diǎn)擊圖標(biāo)啟動(dòng)app
  • 啟動(dòng)時(shí)間的優(yōu)化媚送,主要是針對(duì)冷啟動(dòng)進(jìn)行優(yōu)化
  • 通過(guò)添加環(huán)境變量可以打印app的啟動(dòng)時(shí)間分析
    • DYLD_PRINT_STATISTICS
    • DYLD_PRINT_STATISTICS_DETAILS(比上一個(gè)詳細(xì))
    • 一般400毫秒以內(nèi)正常
打印結(jié)果:
Total pre-main time: 238.05 milliseconds (100.0%)              // main函數(shù)調(diào)用之前(pre-main)總耗時(shí)
         dylib loading time: 249.65 milliseconds (104.8%)      // 動(dòng)態(tài)庫(kù)耗時(shí) 
        rebase/binding time: 126687488.8 seconds (18128259.6%) 
            ObjC setup time:  10.67 milliseconds (4.4%)        // OC結(jié)構(gòu)體準(zhǔn)備耗時(shí) 
           initializer time:  52.83 milliseconds (22.1%)       // 初始化耗時(shí) 
           slowest intializers :                               // 比較慢的加載 
             libSystem.B.dylib :   6.63 milliseconds (2.7%)
   libBacktraceRecording.dylib :   6.61 milliseconds (2.7%)
    libMainThreadChecker.dylib :  31.82 milliseconds (13.3%)
  • 冷啟動(dòng)可以概括為3大階段
    • dyld
    • runtime
    • main
  • dyld(dynamic link editor)中燥,Apple的動(dòng)態(tài)連接器,可以裝載Mach-O(可執(zhí)行文件塘偎、動(dòng)態(tài)庫(kù)等)
    • 裝載app的可執(zhí)行文件疗涉,同時(shí)遞歸加載所有依賴的動(dòng)態(tài)庫(kù)
    • 當(dāng)dyld把可執(zhí)行文件、動(dòng)態(tài)庫(kù)都裝載完成后吟秩,會(huì)通知runtime進(jìn)行下一步處理
  • runtime所做的事情
    • 調(diào)用map_images函數(shù)中調(diào)用call_load_methods咱扣,調(diào)用所有Class和Category的+load方法
    • 進(jìn)行各種objc結(jié)構(gòu)的初始化(注冊(cè)objc類(lèi)、初始化類(lèi)對(duì)象等等)
    • 調(diào)用C++靜態(tài)初始化器和attribure((constructor))修飾的函數(shù)(JSONKit中存在具體應(yīng)用)
    • 到此為止涵防,可執(zhí)行文件和動(dòng)態(tài)庫(kù)中所有的符號(hào)(Class, Protocol, Selector, IMP...)都已按格式成功加載到內(nèi)存中闹伪,被runtime所管理
  • 總結(jié)
    • app的啟動(dòng)由dylb主導(dǎo),將可執(zhí)行文件加載到內(nèi)存,順便加載所有依賴的動(dòng)態(tài)庫(kù)
    • 并由runtime負(fù)責(zé)加載成objc定義的結(jié)構(gòu)
    • 所有初始化工作結(jié)束后偏瓤,dyld就會(huì)調(diào)用main函數(shù)
    • 接下來(lái)就是ApplicationMain函數(shù)杀怠,AppDelegate的application:didFinishLaunchingWithOptions:方法

項(xiàng)目的優(yōu)化、性能優(yōu)化

App啟動(dòng)優(yōu)化

  • 依據(jù)App冷啟動(dòng)階段進(jìn)行優(yōu)化
  • dyld
    • 減少動(dòng)態(tài)庫(kù)厅克、合并一些動(dòng)態(tài)庫(kù)(定期清理不必要的動(dòng)態(tài)庫(kù))
    • 減少objc類(lèi)赔退、分類(lèi)的數(shù)量、減少selector數(shù)量(定期清理不必要的類(lèi)证舟、分類(lèi))
    • 減少C++虛構(gòu)函數(shù)
    • Swift盡量使用struct
  • runtime
    • 使用+initialize方法和dispatch_once取代所有的attribute((constructor))硕旗、C++靜態(tài)構(gòu)造器、Objc的+load方法
  • main
    • 在不影響用戶體驗(yàn)的前提下女责,盡可能將一些操作延遲卵渴,不要全部都放在finishLaunching方法中
    • 按需加載

App 包體積優(yōu)化

  • 安裝包瘦身(ipa):資源文件、可執(zhí)行文件

  • 資源文件(圖片鲤竹、音頻描滔、視頻等)

  • 可執(zhí)行文件瘦身:

    • 編譯器優(yōu)化(Xcode相關(guān)配置)
    • 利用AppCode(https://www.jetbrains.com/objc/) 檢測(cè)未使用的代碼:菜單欄 -> Code -> Inspect Code
    • 生成LinkMap蚊俺,可以查看可執(zhí)行文件的具體組成
    • 可借助第三方工具解析LinkMap文件:http://github.com/huanxsd/LinkMap

頁(yè)面瀏覽速度優(yōu)化

  • json的處理(iOS 自帶的NSJSONSerialization,Jsonkit,SBJson)
  • 數(shù)據(jù)的分頁(yè)(后端數(shù)據(jù)多的話涩哟,就要分頁(yè)返回,例如網(wǎng)易新聞驹马,或者 微博記錄)
  • 數(shù)據(jù)壓縮(大數(shù)據(jù)也可以壓縮返回卷拘,減少流量,加快反應(yīng)速度)
  • 內(nèi)容緩存(例如網(wǎng)易新聞的最新新聞列表都是要緩存到本地氮墨,從本地加載纺蛆,可以緩存到內(nèi)存,或者數(shù)據(jù)庫(kù)规揪,根據(jù)情況而定)
  • 延時(shí)加載tab(比如app有5個(gè)tab桥氏,可以先加載第一個(gè)要顯示的tab,其他的在顯示時(shí)候加載猛铅,按需加載
  • 算法的優(yōu)化(核心算法的優(yōu)化字支,例如有些app 有個(gè) 聯(lián)系人姓名用漢語(yǔ)拼音的首字母排序)

操作流暢度優(yōu)化

  • Tableview 優(yōu)化(tableview cell的加載優(yōu)化)
  • ViewController加載優(yōu)化(不同view之間的跳轉(zhuǎn),可以提前準(zhǔn)備好數(shù)據(jù))

數(shù)據(jù)持久化

SQLite

SQLite 數(shù)據(jù)庫(kù)的特點(diǎn)有哪些奸忽?

  • SQLite 數(shù)據(jù)庫(kù)有以下特點(diǎn):
    • 輕量級(jí)堕伪;
    • 獨(dú)立性,沒(méi)有依賴栗菜,無(wú)需安裝和管理欠雌;
    • 隔離性,全部在一個(gè)文件夾系統(tǒng)疙筹;
    • 跨平臺(tái)性富俄, 支持眾多操作系統(tǒng)禁炒;
    • 多語(yǔ)言接口,支持眾多編程語(yǔ)言蛙酪;
  • SQLite 事務(wù)是完全兼容 ACID 的齐苛,通過(guò)獨(dú)占性和共享鎖來(lái)實(shí)現(xiàn)獨(dú)立事務(wù)的處理,允許從多個(gè)進(jìn)程或線程安全訪問(wèn)桂塞。

請(qǐng)簡(jiǎn)述幾種 SQLite 命令

  • SQLite 的常用命令和其操作屬性分為以下幾種:
    • CREATE : 創(chuàng)建一個(gè)新的表凹蜂,一個(gè)表的視圖,或者數(shù)據(jù)庫(kù)中的其他對(duì)象;
    • ALTER: 修改數(shù)據(jù)庫(kù)中的某個(gè)已有的數(shù)據(jù)庫(kù)對(duì)象阁危,比如一個(gè)表玛痊;
    • DROP: 刪除整個(gè)表,或者表的視圖狂打,或者數(shù)據(jù)庫(kù)中的其他對(duì)象擂煞;
    • INSERT:創(chuàng)建一條記錄;
    • UPDATE: 修改記錄趴乡;
    • DELETE : 刪除記錄对省;
    • SELECT:從一個(gè)或多個(gè)表中檢索某些記錄。

Core Data

Core Data 是什么晾捏?

  • Core Data 是一個(gè)框架蒿涎,提供了對(duì)象—關(guān)系映射(ORM)的功能,既能夠?qū)?duì)象轉(zhuǎn)化成數(shù)據(jù)惦辛,保存在 SQLite 數(shù)據(jù)庫(kù)文件中劳秋,也可以按照需求將數(shù)據(jù)庫(kù)中的數(shù)據(jù)還原成對(duì)象。

Core Data 是一個(gè)關(guān)系型數(shù)據(jù)庫(kù)嗎胖齐?

  • Core Data 不是一個(gè)關(guān)系型數(shù)據(jù)庫(kù)玻淑,也不是關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng),只是為數(shù)據(jù)變更管理呀伙、對(duì)象存儲(chǔ)补履、對(duì)象讀取恢復(fù)的功能提供了支持。

Core Data 與 SQLite 有無(wú)必然聯(lián)系区匠?

Core Data 與 SQLite 是有聯(lián)系的干像,Core Data 是基于 SQLite 數(shù)據(jù)庫(kù)的一個(gè)封裝,底層仍然是使用 SQLite 進(jìn)行存儲(chǔ)數(shù)據(jù)的驰弄。

對(duì)比分析 Core Data 和 SQLite 的優(yōu)缺點(diǎn)

  • SQLite 是使用最多的 輕量級(jí)開(kāi)源數(shù)據(jù)庫(kù)引擎,無(wú)配置速客、無(wú)服務(wù)要求的事務(wù)數(shù)據(jù)庫(kù)引擎戚篙。其優(yōu)點(diǎn)有 獨(dú)立于服務(wù)器、零配置溺职、多進(jìn)程和線程下安全訪問(wèn)等岔擂。Core Data 更加關(guān)注于對(duì)象而不是傳統(tǒng)的表數(shù)據(jù)庫(kù)方法位喂,相比于 SQLite,CoreData 集成化乱灵,數(shù)據(jù)動(dòng)態(tài)加載塑崖,在取數(shù)據(jù)方面更快,不需要書(shū)寫(xiě)大量的 sql 語(yǔ)句痛倚,且還具備其他 sql 所不具備的優(yōu)點(diǎn)规婆,比如對(duì) undo 的支持、多個(gè) context 實(shí)現(xiàn)sketchbook 類(lèi)似的功能等蝉稳,但是 Core Data 數(shù)據(jù)模型升級(jí)兼容性較差 較差抒蚜,不便于處理多對(duì)多的關(guān)系,自定義升級(jí)麻煩耘戚,并且效率低下嗡髓。

測(cè)試

單元測(cè)試

  • 測(cè)試可以分為黑盒測(cè)試和白盒測(cè)試,單元測(cè)試可以算是一種白盒測(cè)試的類(lèi)型收津,其目的是提高軟件開(kāi)發(fā)的效率饿这,維持代碼的健康性,助力開(kāi)發(fā)人員開(kāi)發(fā)健壯撞秋、安全的應(yīng)用程序长捧。
    應(yīng)用程序的開(kāi)發(fā)者對(duì)現(xiàn)有的模塊編寫(xiě)相應(yīng)的測(cè)試代碼進(jìn)行測(cè)試,包含測(cè)試用例的設(shè)計(jì)部服。
    又程序的開(kāi)發(fā)者自己編寫(xiě)測(cè)試代碼并進(jìn)行白盒測(cè)試唆姐,之后再交給測(cè)試團(tuán)隊(duì)進(jìn)行相應(yīng)的黑盒測(cè)試,這種方式有利于提升測(cè)試流程的完整性廓八,從而保證應(yīng)用程序的質(zhì)量奉芦。
    單元測(cè)試比較適合進(jìn)行應(yīng)用程序的業(yè)務(wù)邏輯和網(wǎng)絡(luò)請(qǐng)求接口方面的測(cè)試。
    單元測(cè)試的關(guān)鍵組件是測(cè)試用例剧蹂,測(cè)試用例可以在最低可測(cè)試的單元對(duì)代碼進(jìn)行測(cè)試声功。

什么是單元測(cè)試?

單元測(cè)試是針對(duì)程序的最小單元進(jìn)行正確性檢驗(yàn)的測(cè)試工作宠叼。

本文參考文章及部分內(nèi)容來(lái)源

作者:力扣 (LeetCode)
鏈接:https://leetcode-cn.com/leetbook/read/ios-interview/r1rxtj/

作者:強(qiáng)子ly
鏈接:http://www.reibang.com/p/24a9447d70f8

百度百科
https://baike.baidu.com/item/IPv6/172297?fr=aladdin

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末先巴,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子冒冬,更是在濱河造成了極大的恐慌伸蚯,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件简烤,死亡現(xiàn)場(chǎng)離奇詭異剂邮,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)横侦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)挥萌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)绰姻,“玉大人,你說(shuō)我怎么就攤上這事引瀑】裼螅” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵憨栽,是天一觀的道長(zhǎng)帜矾。 經(jīng)常有香客問(wèn)我,道長(zhǎng)徒像,這世上最難降的妖魔是什么黍特? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮锯蛀,結(jié)果婚禮上灭衷,老公的妹妹穿的比我還像新娘。我一直安慰自己旁涤,他們只是感情好翔曲,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著劈愚,像睡著了一般瞳遍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上菌羽,一...
    開(kāi)封第一講書(shū)人閱讀 52,441評(píng)論 1 310
  • 那天掠械,我揣著相機(jī)與錄音,去河邊找鬼注祖。 笑死猾蒂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的是晨。 我是一名探鬼主播肚菠,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼罩缴!你這毒婦竟也來(lái)了蚊逢?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤箫章,失蹤者是張志新(化名)和其女友劉穎烙荷,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體檬寂,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡奢讨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了焰薄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拿诸。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖塞茅,靈堂內(nèi)的尸體忽然破棺而出亩码,到底是詐尸還是另有隱情,我是刑警寧澤野瘦,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布描沟,位于F島的核電站,受9級(jí)特大地震影響鞭光,放射性物質(zhì)發(fā)生泄漏吏廉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一惰许、第九天 我趴在偏房一處隱蔽的房頂上張望席覆。 院中可真熱鬧,春花似錦汹买、人聲如沸佩伤。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)生巡。三九已至,卻和暖如春见妒,著一層夾襖步出監(jiān)牢的瞬間孤荣,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工须揣, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盐股,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓返敬,卻偏偏與公主長(zhǎng)得像遂庄,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子劲赠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容