ios面試題總結(jié)

談?wù)剬γ嫦驅(qū)ο蟮睦斫?,面向?qū)ο蟮娜?/h2>

對于多態(tài)的理解

category的原理,父類有category,子類沒有;子類有父類沒有,子類父類都有,有同一個(gè)方法,會調(diào)用哪個(gè),能不能調(diào)用到他本來的這個(gè)方法?category可以添加成員變量嘛?

分類的實(shí)現(xiàn)原理是將category中的方法,屬性,協(xié)議數(shù)據(jù)放在category_t結(jié)構(gòu)體中,然后將結(jié)構(gòu)體內(nèi)的方法列表拷貝到類對象的方法列表中。
某個(gè)類有多個(gè)category,會調(diào)用編譯順序(Build Phases下.m文件的順序)中的最后一個(gè)。編譯時(shí)通過壓棧的方式將多個(gè)分類壓棧,根據(jù)后進(jìn)先出的原則,后編譯的會被先調(diào)用。
category不能添加成員變量,會報(bào)錯,因?yàn)閏ategory_t結(jié)構(gòu)體中沒有成員變量,原理是成員變量在編譯時(shí)就決定好了,而category是運(yùn)行時(shí)加載的。
可以給category添加成員屬性,但是系統(tǒng)不會自動實(shí)現(xiàn)get、set方法,可以通過runtime.h中objc_getAssociatedObject / objc_setAssociatedObject來訪問和生成關(guān)聯(lián)對象

說一個(gè)熟悉的三方庫

三次握手 四次揮手

三次握手:
1、客戶端發(fā)送同步SYN和序列號seq請求建立連接,客戶端進(jìn)入SYN_SENT狀態(tài)
2、服務(wù)端收到報(bào)文,確認(rèn)客戶端的SYN,發(fā)送標(biāo)記位SYN、ACK和確認(rèn)ack,服務(wù)端進(jìn)入SYN_RECV狀態(tài)
3、客戶端收到服務(wù)端的報(bào)文,像服務(wù)端發(fā)送標(biāo)記位ACK、序列號seq和確認(rèn)號ack
四次揮手:
1、客戶端發(fā)出連接釋放報(bào)文,并停止發(fā)送數(shù)據(jù), FIN=1, 序列號seq=u,客戶端進(jìn)入FIN_WAIT_1 狀態(tài)
2、服務(wù)端收到報(bào)文后,發(fā)出確認(rèn)報(bào)文,并帶上自己的序列號,服務(wù)端進(jìn)入CLOSE_WAIT狀態(tài),客戶端收到確認(rèn)請求后,客戶端進(jìn)入FIN_WAIT_2狀態(tài),等待服務(wù)端發(fā)送連接釋放報(bào)文
3、服務(wù)端發(fā)送連接釋放報(bào)文,進(jìn)入LAST_ACK狀態(tài),等待客戶端的確認(rèn)
4、客戶端收到該報(bào)文后,發(fā)出確認(rèn),客戶端進(jìn)入TIME_WAIT狀態(tài), 服務(wù)端收到客戶端發(fā)出的確認(rèn),立即進(jìn)入CLOSED狀態(tài)

cocopods的podfile是做什么的,pod install 和pod update的區(qū)別

庫是從cocopods的官網(wǎng)上下載的 https://cocoapods.org
pod install : 執(zhí)行該命令時(shí),如果Podfile.lock文件存在, 則直接從此文件中讀取框架信息并且它會只下載Podfile.lock文件中指定的版本安裝。對于不在Podfile.lock文件中的pod庫,pod install命令會搜索這個(gè)pod庫在Podfile文件中指定的版本來安裝;如果Podfile.lock不存在, 則會讀取Podfile文件內(nèi)的框架信息,然后執(zhí)行下載并且根據(jù)下載好的框架信息, 生成Podfile.lock文件。
pod update : 只有當(dāng)你想要更新pod庫的版本時(shí)才使用pod update;它不管Podfile.lock是否存在, 都會讀取Podfile文件的的框架信息去下載安裝,下載好之后, 再根據(jù)下載好的框架信息, 生成Podfile.lock文件

單例為什么要用static修飾

靜態(tài)局部變量,雖然作用域沒變,但是可以整個(gè)程序生命周期都保持不被銷毀。

銷毀單例

-(void)clear {
    onceToken = 0;
    manager = nil;
}

assign,strong,copy,weak?為什么weak修飾的對象引用計(jì)數(shù)變?yōu)?,會指向nil

弱引用表只有在對象存在weak引用時(shí)創(chuàng)建,無弱引用的對象釋放時(shí)不觸發(fā)查表操作
runtime會維護(hù)一個(gè)weak表,weak表本質(zhì)是hash表,以指向的對象內(nèi)存地址做為key,value是weak指針的地址數(shù)組。
1、初始化時(shí),runtime會調(diào)用objc_initWeak,初始化一個(gè)新的weak指針指向?qū)ο蟮牡刂?br> 2、添加引用時(shí):objc_initWeak函數(shù)會調(diào)用 objc_storeWeak() 函數(shù), objc_storeWeak() 的作用是更新指針指向,創(chuàng)建對應(yīng)的弱引用表
3、釋放時(shí):當(dāng)一個(gè)對象的引用計(jì)數(shù)為0時(shí),系統(tǒng)會調(diào)用對象的dealloc方法進(jìn)行釋放,在dealloc方法底層實(shí)現(xiàn)中,會調(diào)用weak_clear_no_lock函數(shù)。首先根據(jù)對象地址獲取weak指針地址的數(shù)組,然后遍歷這個(gè)數(shù)組把其中的數(shù)據(jù)設(shè)為nil,最后把這個(gè)entry從weak表中刪除,最后清理對象的記錄。

使用 Weak 指針的時(shí)候,應(yīng)首先獲取一個(gè) Strong 指針再使用。為了防止在使用過程中,對象被回收,形成野指針。

NSTimer計(jì)時(shí)器會不會產(chǎn)生循環(huán)引用,怎么解決

不直接持有


image.png

NSTimer內(nèi)存泄漏的原因

調(diào)用invalidate,從runloop中移除,釋放對target對象的強(qiáng)引用

mutbaleCopy和copy的區(qū)別

不可變對象調(diào)用copy是淺拷貝,調(diào)用mutableCopy是深拷貝;可變都是深拷貝,但是調(diào)用copy拷貝出來的對象為不可變類型,數(shù)組是不完全深拷貝,只拷貝容器,item為淺拷貝,完全深拷貝需要調(diào)用copyItem

深拷貝和淺拷貝分別是什么

內(nèi)容拷貝 指針拷貝

響應(yīng)鏈和事件傳遞,給button添加手勢會響應(yīng)哪個(gè)(手勢),給button的父試圖添加手勢會響應(yīng)哪個(gè)(button

1.當(dāng)iOS程序中發(fā)生觸摸事件后,系統(tǒng)會將事件加入到UIApplication管理的一個(gè)任務(wù)隊(duì)列中
2.UIApplication將處于任務(wù)隊(duì)列最前端的事件向下分發(fā)。即UIWindow。
3.UIWindow將事件向下分發(fā),即UIView。
4.UIView首先看自己是否能處理事件,觸摸點(diǎn)是否在自己身上(hitTest:withEvent)。如果能,那么繼續(xù)尋找子視圖。

setvalue:forkey:和setobject:forkey:的區(qū)別,哪個(gè)值可為nil

setvalueforkey的value為nil時(shí),調(diào)用removeObject:forKey

動態(tài)庫和靜態(tài)庫的區(qū)別

編譯/運(yùn)行時(shí)加載

runtime,消息轉(zhuǎn)發(fā)機(jī)制

runtime

給一個(gè)類動態(tài)添加成員變量
class_addIvar

一個(gè)排好序的數(shù)組,怎么找到第一次出現(xiàn)的某個(gè)值的下標(biāo)

二分查找,找到后while循環(huán)看前一位是否相等

瀑布流效果

多代理

KVO的原理

isa_swizzing,A在運(yùn)行時(shí)會動態(tài)生成一個(gè)notify_A類,然后將A類的實(shí)例對象的isa指針指向notify_A類,
觀察A的name屬性:
1、重寫set方法,并在設(shè)置操作的前后分別調(diào)用 willChangeValueForKey: 和 didChangeValueForKey方法,這兩個(gè)方法用于通知系統(tǒng)該 key 的屬性值即將和已經(jīng)變更了
2、重寫class方法,返回原來的A類對象
3、重寫dealloc方法,再合適的時(shí)候銷毀這個(gè)運(yùn)行時(shí)創(chuàng)建的類

KVC的原理

當(dāng)調(diào)用setValue:forKey:時(shí) ,
1、程序會先通過setter(set:)方法,對屬性進(jìn)行設(shè)置;
2、如果沒有找到setKey:方法,KVC機(jī)制會檢查+(BOOL)accessInstanceVariablesDirectly方法有沒有返回YES,默認(rèn)該方法是返回YES的,如果重寫返回了NO,那么這一步會執(zhí)行setValue:forUndefinedKey:方法。若為YES,KVC機(jī)制會搜索該類中是否有名為key的成員變量,不管變量在類接口處定義沒有,只要存在以key命名的變量,KVC都可以對該成員變量賦值。
3、如果該類既沒有setKey方法,也沒有_key成員變量,KVC機(jī)制會搜索_isKey的成員變量
4、如果_isKey成員變量也沒有,KVC機(jī)制再會繼續(xù)搜索key和isKey給它們賦值,如果上面列出的方法或者成員變量都不存在,系統(tǒng)會執(zhí)行該對象的setValue:forUndefinedKey:方法
拋出異常(既 如果沒有找到Set方法的話,會按照_key,_iskey,key,iskey的順序搜索成員并進(jìn)行賦值操作)

重寫isEqual:方法必須重寫hash:方法

幾種定時(shí)器的區(qū)別

NSTimer:通過RunLoop來實(shí)現(xiàn);
CADisplayLink:基于屏幕刷新的周期,本質(zhì)也是通過RunLoop,度量單位是幀,可設(shè)置preferredFramesPerSecond 屬性,屏幕刷新多少幀時(shí)調(diào)用該方法;
GCD:使用了dispatch源,dispatch源使用內(nèi)核

算法:二叉樹的深度、鏈表是否有環(huán)、平衡二叉樹

runloop

方法調(diào)用的三個(gè)步驟

消息發(fā)送obj_sendMessage(),動態(tài)方法解析(是否動態(tài)添加該方法),和消息轉(zhuǎn)發(fā)

NSArray和NSSet的區(qū)別

1、NSArray內(nèi)存中存儲地址連續(xù),而NSSet不連續(xù)
2、在搜索一個(gè)元素時(shí)NSSet比NSArray效率高,主要是它用到了一個(gè)算法hash;NSArray查找需要遍歷
3、NSSet通過anyObject訪問元素,NSArray通過下標(biāo)訪問

NSCache的好處

緩存功能,提供了類似可變字典的使用方式,
1、NSCache是線程安全的
2、內(nèi)存不足時(shí)NSCache會自動釋放存儲的對象,不需要手動干預(yù)
3、NSCache的鍵key不會被復(fù)制,所以key不需要實(shí)現(xiàn)NSCopying協(xié)議

只用一個(gè)循環(huán)刪除 一個(gè)單項(xiàng)鏈表的倒數(shù)第n個(gè)值

tcp和udp的區(qū)別,tcp接收到數(shù)據(jù)后如何反饋,tcp丟包情況下怎么處理

block有幾種類型

block的本質(zhì)是oc對象,內(nèi)部有isa指針;類型:NSGlobalBlock,NSStackBlock,NSMallocBlock;變量捕獲機(jī)制:僅捕獲了值,沒有捕獲內(nèi)存地址,block禁止從棧區(qū)上修改自動變量,因?yàn)樽兞窟M(jìn)入了block實(shí)際就是修改了變量的作用域,在幾個(gè)作用域之間進(jìn)行切換時(shí),如果不加上這樣的限制,變量的可維護(hù)性將大大降低。 所以當(dāng)申明__block的時(shí)候,實(shí)際就是把變量的內(nèi)存地址從棧中的放到了堆中。進(jìn)而在block內(nèi)部也可以修改外部變量的值。
block在修改NSMutableArray時(shí),如果修改的是NSMutableArray的存儲內(nèi)容的話,是不需要添加__block修飾的;如果修改的是NSMutableArray對象的本身,那必須添加__block修飾。

NSMutableArray *mutArr = [[NSMutableArray alloc] initWithArray:@[@"1",@"2",@"3"]];
    void (^block)(void) = ^{
        NSLog(@"%@", mutArr);  1,2,3,4
        [mutArr addObject:@"5"];
        NSLog(@"%@", mutArr);  1,2,3,4,5
    };
    [mutArr addObject:@"4"];
    mutArr = nil;
    block();

棧上的block copy到堆上:
1、調(diào)用copy
2、block內(nèi)部用到了局部變量
3、外部調(diào)用block,self.block(); vc.block=^{};

ReactNative和Flutter的區(qū)別

https://zhuanlan.zhihu.com/p/70070316
reactnative是javaScript語言,是動態(tài)語言,代碼產(chǎn)物是JS Bundle文件,是一套UI框架,會在Activity下加載JS文件,然后運(yùn)行在JavaScriptCore中解析Bundle文件布局,最終堆疊出一系列的原生控件進(jìn)行渲染。如<View>標(biāo)簽對應(yīng)ViewGroup/UIView
Flutter是Dart語言,是偽動態(tài)語言的強(qiáng)類型語言(通過var語法糖聲明,在賦值時(shí)其實(shí)會通過自推導(dǎo)出類型;dynamic聲明的才是真的動態(tài)變量),代碼產(chǎn)物是二進(jìn)制文件,F(xiàn)lutter中大部分Widget都與平臺無關(guān),開發(fā)者基于Framework開發(fā)App,F(xiàn)ramework運(yùn)行在Engine之上,由Engine進(jìn)行適配和跨平臺支持,將Flutter UI中的Widget數(shù)據(jù)化,然后通過Engine的Skia直接繪制到屏幕上。Flutter的整體渲染脫離了原生層面,直接和GPU交互。
他們在一定程度上有很大的通識性:都支持var定義變量,支持asycn/await語法糖,支持Promise(Future)等鏈?zhǔn)疆惒教幚?/p>

Flutter熱更新

MRC和ARC的區(qū)別

http://www.itdecent.cn/p/5eac83471b23

NSURLSession和NSURLConnection的區(qū)別

NSURLConnection是基于HTTP1.0
NSURLSession是基于HTTP2.0
NSURLSession是iOS7中新的網(wǎng)絡(luò)接口,最直接的改進(jìn)就是可以配置每個(gè)session的緩存,協(xié)議,cookie,以及證書,甚至跨進(jìn)程共享這些信息。
NSURLSession的另一重要組成部分是會話任務(wù),它負(fù)責(zé)處理數(shù)據(jù)的加載,以及客戶端與服務(wù)器之間的文件和數(shù)據(jù)的上傳下載服務(wù)。NSURLSessionTask與NSURLConnection是及其相似的,因?yàn)樗?fù)責(zé)加載數(shù)據(jù),而主要的區(qū)別在于,任務(wù)共享它們父類NSURLSession的共同委托(common delegate)。
https://blog.csdn.net/ioswcc/article/details/49183013

程序啟動的過程

main 函數(shù)執(zhí)行前,程序會做一系列的初始化工作,動態(tài)加載依賴庫:
1、系統(tǒng)會讀取程序的可執(zhí)行文件mach-o,從里面獲取動態(tài)加載器(dynamic link editor)的路徑
2、加載dyld,dyld會初始化運(yùn)行環(huán)境,配合ImageLoader將二進(jìn)制文件加載到內(nèi)存中去
3、動態(tài)鏈接依賴庫,初始化依賴庫,初始化runtime
4、runtime會對項(xiàng)目中所有的類進(jìn)行類結(jié)構(gòu)初始化,調(diào)用所有的load方法(再調(diào)用main函數(shù)前打印信息,可在分類的load方法中實(shí)現(xiàn))
5、最后dyld會返回main函數(shù)地址,main函數(shù)被調(diào)用,進(jìn)入程序入口
main函數(shù)執(zhí)行后:
1、內(nèi)部調(diào)用UIApplicationMain,創(chuàng)建一個(gè)UIApplication對象和它的代理,就是項(xiàng)目中的Appdelegate類
2、開始一個(gè)時(shí)間循環(huán)(main runLoop),監(jiān)聽系統(tǒng)事件
3、程序啟動完畢時(shí),通知代理Appdelegate,調(diào)用didFinishLaunching代理方法,在這里會創(chuàng)建UIWindow,設(shè)置它的rootViewController,
4、最后調(diào)用makeKeyAndVisable顯示窗口

組件化插件化

CPU的度量單位

CPU的單位是Hz(赫茲)。
主頻也叫時(shí)鐘頻率,單位是兆赫(MHz)或千兆赫(GHz),用來表示CPU的運(yùn)算、處理數(shù)據(jù)的速度。通常,主頻越高,CPU處理數(shù)據(jù)的速度就越快。

什么會導(dǎo)致視頻卡頓

購物車倒計(jì)時(shí)頁面怎樣實(shí)現(xiàn)

Controller中創(chuàng)建一個(gè)timer,遍歷數(shù)組,時(shí)間-1,刷新tableView(優(yōu)化點(diǎn):數(shù)組數(shù)據(jù)過大,遍歷數(shù)組,刷新tableView有什么問題???)

block中添加一個(gè)通知,會不會產(chǎn)生循環(huán)引用,怎么避免

系統(tǒng)block為什么不會產(chǎn)生循環(huán)引用

GCD下暫停線程,取消線程

暫停

dispatch_suspend(queue)
dispatch_resume(queue);

取消
NSOperation有cancel可以取消還未執(zhí)行的線程。但是沒辦法做到取消一個(gè)正在執(zhí)行的線程。
GCD通過dispatch_block_cancel可以cancel掉dispatch_block_t,需要注意的是,未執(zhí)行的可以用此方法cancel掉,若已經(jīng)執(zhí)行則cancel不掉;
iOS8以后, 如果想中斷(interrupt)線程,也就是取消一個(gè)正在執(zhí)行的線程,可以使用 dispatch_block_testcancel方法

線程間的通信

NSThread

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

GCD

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    dispatch_async(dispatch_get_main_queue(), ^{
    });
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});

FMDB中怎么保證線程安全

序列化(歸解檔)

需要實(shí)現(xiàn)NSCoding以及NSCopying(非必須)協(xié)議

- (void)encodeWithCoder:(NSCoder *)aCoder {
 
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeInteger:self.age forKey:@"age"];
}
//解檔
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder {
 
    self = [super init];
    if (self) {
        self.name = [aDecoder decodeObjectForKey:@"name"];
        self.age  = [aDecoder decodeIntegerForKey:@"age"];
    }
    return self;
}

      Person *person = [[Person alloc] init];
    person.name = @"Frank";
    person.age  = 18;
        //這里以temp路徑為例,存到temp路徑下
    NSString *temp = NSTemporaryDirectory();
    NSString *filePath = [temp stringByAppendingPathComponent:@"obj.data"]; //注:保存文件的擴(kuò)展名可以任意取,不影響。
    NSLog(@"%@", filePath);
    //歸檔
    BOOL isSuccess = [NSKeyedArchiver archiveRootObject:person toFile:filePath];
    if(isSuccess) {
        NSLog(@"歸檔成功");
    }else{
        NSLog(@"歸檔失敗");
        }

      // 解檔
    Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]

序列化只保存對象的狀態(tài),而不管對象的方法。
當(dāng)一個(gè)父類實(shí)現(xiàn)了序列化,它的子類也自動實(shí)現(xiàn)序列化,不用顯示進(jìn)行實(shí)現(xiàn)了。
當(dāng)一個(gè)實(shí)例對象引用其他對象,當(dāng)序列化該對象時(shí)也把引用的對象進(jìn)行了實(shí)例化。

架構(gòu)

i386是針對intel通用微處理器32位處理器
x86_64是針對x86架構(gòu)的64位處理器

模擬器32位處理器測試需要i386架構(gòu),
模擬器64位處理器測試需要x86_64架構(gòu),
真機(jī)32位處理器需要armv7,或者armv7s架構(gòu),
真機(jī)64位處理器需要arm64架構(gòu)。

DNS劫持問題

域名請求時(shí)會先向DNS服務(wù)器發(fā)出請求,把域名轉(zhuǎn)換成機(jī)器能夠識別的ip地址
通過httpDNS,使用DNS協(xié)議向DNS服務(wù)器的53端口進(jìn)行請求,運(yùn)營商通過當(dāng)前地址返回一個(gè)最近的ip地址,通過NSURLProtocol類即可實(shí)現(xiàn)。

消息轉(zhuǎn)發(fā)的第一部,如果沒有動態(tài)添加該方法,返回了YES,會有什么后果

返回YES后,會繼續(xù)去方法列表中查找,沒有找到的話進(jìn)入消息轉(zhuǎn)發(fā),但是并不會導(dǎo)致死循環(huán),會有一個(gè)標(biāo)記

卡頓的計(jì)算

用 CADisplayLinker 來計(jì)數(shù):
CADisplayLink可以以屏幕刷新的頻率調(diào)用指定selector,iOS系統(tǒng)中正常的屏幕刷新率為60次/秒,只要在這個(gè)方法里面統(tǒng)計(jì)每秒這個(gè)方法執(zhí)行的次數(shù),通過次數(shù)/時(shí)間就可以得出當(dāng)前屏幕的刷新率了。
通過子線程監(jiān)測主線程的RunLoop:
開啟子線程,實(shí)時(shí)計(jì)算這兩個(gè)狀態(tài)區(qū)域之間的耗時(shí)是否到達(dá)某個(gè)閥值,便能揪出這些性能殺手,假定連續(xù)6次超時(shí)50ms認(rèn)為卡頓(當(dāng)然也包含了單次超時(shí)300ms)

工廠設(shè)計(jì)模式

  • 簡單工廠模式
    由一個(gè)工廠對象決定創(chuàng)建出哪一種產(chǎn)品類的示例
    create方法通常是靜態(tài)的,所以稱之為靜態(tài)工廠方法
    缺點(diǎn):集中了所有實(shí)例的創(chuàng)建邏輯,違反了高內(nèi)聚的職責(zé)分配原則,擴(kuò)展性差
 public ICar GetCar(CarType carType)
        {
            switch (carType)
            {
                case CarType.SportCarType:
                    return new SportCar();
                case CarType.JeepCarType:
                    return new JeepCar();
                case CarType.HatchbackCarType:
                    return new HatchbackCar();
                default:
                    throw new Exception("愛上一匹野馬,可我的家里沒有草原. 你走吧!");
            }
        }
  • 工廠方法模式
    動態(tài)性工廠模式,核心的工廠類不再負(fù)責(zé)所有的產(chǎn)品的創(chuàng)建,而是將具體創(chuàng)建的工作交給子類去做。該核心類成為一個(gè)抽象工廠角色,僅負(fù)責(zé)給出具體工廠子類必須實(shí)現(xiàn)的接口,完全實(shí)現(xiàn)開閉原則
  • 抽象工廠模式
    抽象工廠模式是所有形態(tài)的工廠模式中最為抽象和最具一般性的一種形態(tài)。為創(chuàng)建一組相關(guān)或相互依賴的對象提供一個(gè)接口,而且無需制定他們的具體類。工廠方法模式針對的是一個(gè)產(chǎn)品等級結(jié)構(gòu),而抽象工廠模式則是針對多個(gè)產(chǎn)品等級結(jié)構(gòu)
    缺點(diǎn):產(chǎn)品族的擴(kuò)展將是一件十分費(fèi)力的事情。

layoutIfNeed和setNeedsLayout

layoutIfNeed:立即更新布局
setNeedsLayout:異步繪制的方法。會調(diào)用layoutSubViews
setNeedsDisplay:異步繪制的方法。會自動調(diào)用drawRect方法

HTTPS的過程

HTTP請求都是明文傳輸?shù)?,沒有經(jīng)過加密的信息,導(dǎo)致數(shù)據(jù)不安全;HTTPS可以將數(shù)據(jù)加密傳輸,也就是傳輸?shù)拿芪?,保證了網(wǎng)絡(luò)通信的安全。
HTTPS協(xié)議=HTTP協(xié)議+SSL/TLS協(xié)議(安全套接層協(xié)議),再HTTPS數(shù)據(jù)傳輸過程中,需要用SSL/TLS對數(shù)據(jù)進(jìn)行加密和解密,需要用HTTP對加密的數(shù)據(jù)進(jìn)行傳輸。
1、客戶端向服務(wù)端發(fā)起請求
2、服務(wù)端把自己的證書(包含公鑰)發(fā)送給客戶端
3、客戶端對證書進(jìn)行驗(yàn)證,通過,客戶端通過公鑰對自己隨機(jī)生成的字符串(對稱加密中使用的密鑰)進(jìn)行加密傳給服務(wù)端,服務(wù)端用私鑰進(jìn)行解密(非對稱加密),三次握手完成。
4、客戶端對數(shù)據(jù)進(jìn)行對稱加密給到服務(wù)端,服務(wù)端解密

紅黑二叉樹

是一種特殊的二叉查找樹。紅黑樹的每個(gè)結(jié)點(diǎn)上都有存儲位表示結(jié)點(diǎn)的顏色,可以是紅(Red)或黑(Black)。
紅黑樹的特性:
每個(gè)結(jié)點(diǎn)是黑色或者紅色。
根結(jié)點(diǎn)是黑色。
每個(gè)葉子結(jié)點(diǎn)(NIL)是黑色。 [注意:這里葉子結(jié)點(diǎn),是指為空(NIL或NULL)的葉子結(jié)點(diǎn)!]
如果一個(gè)結(jié)點(diǎn)是紅色的,則它的子結(jié)點(diǎn)必須是黑色的。
每個(gè)結(jié)點(diǎn)到葉子結(jié)點(diǎn)NIL所經(jīng)過的黑色結(jié)點(diǎn)的個(gè)數(shù)一樣的。[確保沒有一條路徑會比其他路徑長出倆倍,所以紅黑樹是相對接近平衡的二叉樹的!]

NSArray、NSDictionary應(yīng)該如何選關(guān)鍵詞

同NSString,如果用MutableString給String賦值的話,對MutableString進(jìn)行copy操作是深拷貝,開辟了新的內(nèi)存,MutableString修改的話不會影響string,如果用strong修飾,只是引用計(jì)數(shù)+1,地址還是源字符串的,會影響string的值。

如何實(shí)現(xiàn)自定義類的深拷貝

相當(dāng)于創(chuàng)建一個(gè)新的對象,把源對象的成員屬性值付給新對象的成員屬性

- (id)copyWithZone:(NSZone *)zone {
    CustomModel *copy = [[[self class] alloc] init];
    unsigned int propertyCount = 0;
    objc_property_t *propertyList = class_copyPropertyList([self class], &propertyCount);
    for (int i = 0; i < propertyCount; i++ ) {
        objc_property_t thisProperty = propertyList[I];
        const char* propertyCName = property_getName(thisProperty);
        NSString *propertyName = [NSString stringWithCString:propertyCName encoding:NSUTF8StringEncoding];
        id value = [self valueForKey:propertyName];
        [copy setValue:value forKey:propertyName];
    }
    return copy;
}
// 注意此處需要實(shí)現(xiàn)這個(gè)函數(shù),因?yàn)樵谕ㄟ^Runtime獲取屬性列表時(shí),會獲取到一個(gè)名字為hash的屬性名,這個(gè)是系統(tǒng)幫你生成的一個(gè)屬性
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {}

父類實(shí)現(xiàn)深拷貝之后,子類只要重寫copyWithZone方法,在方法內(nèi)部調(diào)用父類的copyWithZone方法,之后實(shí)現(xiàn)自己的屬性的處理;父類沒有實(shí)現(xiàn)深拷貝,子類除了需要對自己的屬性進(jìn)行處理,還要對父類的屬性進(jìn)行處理

CoreAnimation如何在動畫過程中獲取當(dāng)前的frame

 [[v.layer presentationLayer] frame]

__block原理

相比于不用__block修飾,結(jié)構(gòu)體中多保存了一個(gè)指針變量,block體內(nèi)修改的實(shí)際是a指向的堆中的內(nèi)容,所以任何對這個(gè)指針的操作,是可以影響到原來的變量的。
進(jìn)一步,我們考慮截獲的自動變量是Objective-C的對象的情況。在開啟ARC的情況下,將會強(qiáng)引用這個(gè)對象一次。這也保證了原對象不被銷毀,但與此同時(shí),也會導(dǎo)致循環(huán)引用問題。

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0 *Desc;
    __Block_byref_val_0 *val;   //指針變量

    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc,__Block_byref_val_0 *_val, int flags=0) : val(_val->__forwrding){
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

dispatch_once原理

dispatch_once

void dispatch_once(dispatch_once_t *val, void (^block)(void)){
    struct Block_basic *bb = (void *)block;
    dispatch_once_f(val, block, (void *)bb->Block_invoke);
}
void dispatch_once_f(dispatch_once_t *val, void *ctxt, void (*func)(void *)){
    volatile long *vval = val;
    if (dispatch_atomic_cmpxchg(val, 0l, 1l)) {
        func(ctxt); // block真正執(zhí)行
        dispatch_atomic_barrier();
        *val = ~0l;
    } else {
        do {
            _dispatch_hardware_pause();
        } while (*vval != ~0l);
        dispatch_atomic_barrier();
    }
}

1、dispatch_atomic_cmpxchg,它是一個(gè)宏定義,原型為__sync_bool_compare_and_swap((p), (o), (n)) ,這是LockFree給予CAS的一種原子操作機(jī)制,原理就是 如果p==o,那么將p設(shè)置為n,然后返回true;否則,不做任何處理返回false

2、在多線程環(huán)境中,如果某一個(gè)線程A首次進(jìn)入dispatch_once_f,val==0,這個(gè)時(shí)候直接將其原子操作設(shè)為1,然后執(zhí)行傳入dispatch_once_f的block,然后調(diào)用dispatch_atomic_barrier,最后將val的值修改為~0。

3、dispatch_atomic_barrier是一種內(nèi)存屏障,所謂內(nèi)存屏障,從處理器角度來說,是用來串行化讀寫操作的,從軟件角度來講,就是用來解決順序一致性問題的。編譯器不是要打亂代碼執(zhí)行順序嗎,處理器不是要亂序執(zhí)行嗎,你插入一個(gè)內(nèi)存屏障,就相當(dāng)于告訴編譯器,屏障前后的指令順序不能顛倒,告訴處理器,只有等屏障前的指令執(zhí)行完了,屏障后的指令才能開始執(zhí)行。所以這里dispatch_atomic_barrier能保證只有在block執(zhí)行完畢后才能修改*val的值。

4、在首個(gè)線程A執(zhí)行block的過程中,如果其它的線程也進(jìn)入dispatch_once_f,那么這個(gè)時(shí)候if的原子判斷一定是返回false,于是走到了else分支,于是執(zhí)行了do while循環(huán),其中調(diào)用了_dispatch_hardware_pause,這有助于提高性能和節(jié)省CPU耗電,pause就像nop,干的事情就是延遲空等的事情。直到首個(gè)線程已經(jīng)將block執(zhí)行完畢且將*val修改為~0,調(diào)用dispatch_atomic_barrier后退出。這么看來其它的線程是無法執(zhí)行block的,這就保證了在dispatch_once_f的block的執(zhí)行的唯一性,生成的單例也是唯一的。

NSDictionary原理,重復(fù)key是怎么處理的?

NSDictionary是通過Hash表來實(shí)現(xiàn)key和value之間的映射和存儲的
哈希表的本質(zhì)是一個(gè)數(shù)組,數(shù)組中的每一個(gè)元素成為箱子(bin),箱子中存放著鍵值對。
哈希表的存儲過程:
1、根據(jù)key計(jì)算出hash值h
2、假設(shè)有n個(gè)箱子,那么這個(gè)鍵值對放在h%n個(gè)箱子中
3、如果該箱子已經(jīng)有了鍵值對,就使用開放尋址法或者拉鏈法解決沖突
重復(fù)的key,會覆蓋value值

二叉樹的先序、中序、后序遍歷

image.png

前序:先遍歷根節(jié)點(diǎn),然后是左節(jié)點(diǎn),最后是右節(jié)點(diǎn)(A B D H E I C F J K G)
中序:先遍歷左節(jié)點(diǎn),然后是根節(jié)點(diǎn),最后是右節(jié)點(diǎn)(D H B E I A J F K C G)
后序:先左節(jié)點(diǎn),然后是右節(jié)點(diǎn),最后是根節(jié)點(diǎn)(H D I E B J K F G C A)

圖片編解碼

靜態(tài)圖的解碼,基本可以分為以下步驟:
1、創(chuàng)建CGImageSource

CGImageSourceCreateWithData:
CGImageSourceCreateWithURL:
CGImageSourceCreateWithDataProvider:

2、讀取圖像格式元數(shù)據(jù)(可選)
需要獲取一些相關(guān)的圖像信息,包括圖像的格式,圖像數(shù)量,EXIF元數(shù)據(jù)

圖像格式:CGImageSourceGetType
圖像數(shù)量(動圖):CGImageSourceGetCount
CGImageSourceCopyPropertiesAtIndex:
NSDictionary *imageProperties = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source, 0, NULL); //靜態(tài)圖都為0
NSUInteger width = [imageProperties[(__bridge NSString *)kCGImagePropertyPixelWidth] unsignedIntegerValue]; //寬度,像素值
NSUInteger height = [imageProperties[(__bridge NSString *)kCGImagePropertyPixelHeight] unsignedIntegerValue]; //高度,像素值
BOOL hasAlpha = [imageProperties[(__bridge NSString *)kCGImagePropertyHasAlpha] boolValue]; //是否含有Alpha通道
CGImagePropertyOrientation exifOrientation = [imageProperties[(__bridge NSString *)kCGImagePropertyOrientation] integerValue]; // 這里也能直接拿到EXIF方向信息,和前面的一樣。如果是iOS 7,就用NSInteger取吧 :)

3、解碼得到CGImage

CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, 0, NULL);

4、生成上層的UIImage,清理

// UIImageOrientation和CGImagePropertyOrientation枚舉定義順序不同,封裝一個(gè)方法搞一個(gè)switch case就行
UIImageOrientation imageOrientation = [self imageOrientationFromExifOrientation:exifOrientation];
UIImage *image = [[UIImage imageWithCGImage:imageRef scale:[UIScreen mainScreen].scale orientation:imageOrientation];

// 清理,都是C指針,避免內(nèi)存泄漏
CGImageRelease(imageRef);
CFRelease(source)

用戶態(tài)和內(nèi)核態(tài)

當(dāng)一個(gè)任務(wù)(進(jìn)程)執(zhí)行系統(tǒng)調(diào)用而陷入內(nèi)核代碼中執(zhí)行時(shí),我們就稱進(jìn)程處于內(nèi)核運(yùn)行態(tài)(或簡稱為內(nèi)核態(tài))。此時(shí)處理器處于特權(quán)級最高的(0級)內(nèi)核代碼中執(zhí)行。當(dāng)進(jìn)程處于內(nèi)核態(tài)時(shí),執(zhí)行的內(nèi)核代碼會使用當(dāng)前進(jìn)程的內(nèi)核棧。每個(gè)進(jìn)程都有自己的內(nèi)核棧。當(dāng)進(jìn)程在執(zhí)行用戶自己的代碼時(shí),則稱其處于用戶運(yùn)行態(tài)(用戶態(tài))。即此時(shí)處理器在特權(quán)級最低的(3級)用戶代碼中運(yùn)行。當(dāng)正在執(zhí)行用戶程序而突然被中斷程序中斷時(shí),此時(shí)用戶程序也可以象征性地稱為處于進(jìn)程的內(nèi)核態(tài)。因?yàn)橹袛嗵幚沓绦驅(qū)⑹褂卯?dāng)前進(jìn)程的內(nèi)核棧。這與處于內(nèi)核態(tài)的進(jìn)程的狀態(tài)有些類似。
用戶態(tài)切換到內(nèi)核態(tài)的3種方式:
1、系統(tǒng)調(diào)用
2、異常
3、外圍設(shè)備的中斷

LRU緩存

刪除最近使用最少的數(shù)據(jù)
算法:
去維護(hù)一個(gè)有序的單向鏈表,越靠近鏈表尾部的結(jié)點(diǎn)是越早之前訪問的,當(dāng)有一個(gè)新的數(shù)據(jù)被訪問的時(shí)候,我們從頭開始遍歷鏈表
如果此數(shù)據(jù)之前已經(jīng)被緩存在鏈表中了,我們遍歷得到這個(gè)數(shù)據(jù)對應(yīng)的結(jié)點(diǎn),并將其從原來的位置刪除,再插入到鏈表頭部。
如果此數(shù)據(jù)沒有被緩存在鏈表中,如果此時(shí)緩存未滿,直接將此結(jié)點(diǎn)插入到鏈表的頭部;如果緩存已滿,則刪除鏈表尾部結(jié)點(diǎn),將新數(shù)據(jù)插入到鏈表的頭部。

RunLoop中source0和source1的區(qū)別

source0:需要手動喚醒線程
source1:具有喚醒線程的能力

load和initialize的區(qū)別

1、+load方法當(dāng)類或分類添加到object-c runtime時(shí)被調(diào)用,子類的+load方法會在它所有父類的+load方法之后執(zhí)行,而分類的+load方法會在它的主類的+load方法之后執(zhí)行。但不同的類之間的+load方法的調(diào)用順序是不確定的,所以不要在此方法中用另一個(gè)類。

2、+load方法不像普通方法一樣,它不遵循那套繼承規(guī)則。如果某個(gè)類本身沒有實(shí)現(xiàn)+load方法,那么不管其它各級超類是否實(shí)現(xiàn)此方法,系統(tǒng)都不會調(diào)用。+load方法調(diào)用順序是:SuperClass -->SubClass --> CategaryClass。

3、+initialize是在類或者它的子類接受第一條消息前被調(diào)用,但是在它的超類接收到initialize之后。也就是說+initialize是以懶加載的方式被調(diào)用的,如果程序一直沒有給某個(gè)類或它的子類發(fā)送消息,那么這個(gè)類的+initialize方法是不會被調(diào)用的。

4、+initialize方法和+load方法還有個(gè)區(qū)別,就是運(yùn)行期系統(tǒng)完整度上來講,此時(shí)可以安全使用并調(diào)用任意類中的任意方法。而且,運(yùn)行期系統(tǒng)也能確保+initialize方法一定會在“線程安全的環(huán)境”中執(zhí)行,這就是說,只有執(zhí)行+initialize的那個(gè)線程可以操作類或類實(shí)例,其他線程都要阻塞等著+initialize執(zhí)行完。

5、+initialize方法和其他類一樣,如果某個(gè)類未實(shí)現(xiàn)它,而其超類實(shí)現(xiàn)了,那么就會運(yùn)行超類的實(shí)現(xiàn)代碼。如果本身和超類都沒有實(shí)現(xiàn),超類的分類實(shí)現(xiàn)了,就會去調(diào)用分類的initialize方法。如果本身沒有實(shí)現(xiàn),超類和父類的分類實(shí)現(xiàn)了就會去調(diào)分類的initialize方法。不管是在超類中還是分類中實(shí)現(xiàn)initialize方法都會被調(diào)多次,調(diào)用順序是SuperClass -->SubClass。


image.png

tcp和ip協(xié)議的關(guān)系

TCP/IP提供點(diǎn)對點(diǎn)的鏈接機(jī)制,將數(shù)據(jù)應(yīng)該如何封裝、定址、傳輸、路由以及在目的地如何接收,都加以標(biāo)準(zhǔn)化。

TCP負(fù)責(zé)發(fā)現(xiàn)傳輸?shù)膯栴},一有問題就發(fā)出信號,要求重新傳輸,直到所有數(shù)據(jù)安全正確地傳輸?shù)侥康牡亍?br> 而IP是給因特網(wǎng)的每一臺電腦規(guī)定一個(gè)地址,并解決如何發(fā)現(xiàn)和找到這個(gè)地址。

weak原理中怎么解決哈希沖突

Method-Swizzling的原理

交換的是指針指向的地址(存放方法實(shí)現(xiàn)的)

frame和autoLayout

AutoReleasePool釋放的時(shí)機(jī)

當(dāng)前RunLoop周期結(jié)束前進(jìn)行出棧操作

每個(gè)對象銷毀前都會去判斷有沒有弱引用表嘛

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

    if (fastpath(isa.nonpointer  &&  
                 !isa.weakly_referenced  &&  
                 !isa.has_assoc  &&  
                 !isa.has_cxx_dtor  &&  
                 !isa.has_sidetable_rc))
    {
        assert(!sidetable_present());
        free(this);
    } 
    else {
        object_dispose((id)this);
    }
}
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

對數(shù)據(jù)庫的優(yōu)化 FMDB

arc下NSNumber *num = [NSNumber numberWithInt:1];引用計(jì)數(shù)

通常情況下我們使用NSNumber都是通過 [NSNumbernumberWithInt:1] 建立自動釋放的對象
在官方的解釋中,retainCount對于某些特殊情況下的對象并不可靠
而NSNumber一般創(chuàng)建的是自動釋放的對象,自動釋放的對象的retainCount也是不可靠的
所以你使用init之后,記得release即可,不用關(guān)心它的retaincount

NSRunLoopCommonModes為什么可以在兩個(gè)Mode下都運(yùn)行

源碼注解


void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {    
    CHECK_FOR_FORK();

    //如果要添加timer的Runloop對象已經(jīng)正在釋放了,就不要添加了,直接返回
    if (__CFRunLoopIsDeallocating(rl)) return;

    //判斷timer對象是否存在,timer關(guān)聯(lián)的runloop是否存在,
    //并且timer當(dāng)前關(guān)聯(lián)的runloop不能是要添加它的runloop,如果是的話直接返回,不需要添加了
    if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return;

    //給當(dāng)前的runloop加鎖,防止在其他地方操作
    __CFRunLoopLock(rl);

    //如果當(dāng)前要將timer添加到Runloop的commonModes集合下的話
    if (modeName == kCFRunLoopCommonModes) {
        //先判斷Runloop對象是否有commonModes集合
        //如果有 : 則直接拿到集合,否則set為NULL
        CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
        //如果RunLoop對象沒有commonModesItems
        if (NULL == rl->_commonModeItems) {
            //創(chuàng)建一個(gè)RunLoop的commonModes集合
            rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
        }
        //將timer添加到RunLoop的commonModeItems集合里面
        CFSetAddValue(rl->_commonModeItems, rlt);
        //如果RunLoop的commonModes集合不為空
        if (NULL != set) {
            //把runloop對象和timer包裝成數(shù)組
            CFTypeRef context[2] = {rl, rlt};
            //添加一個(gè)新的commonModesItems,也就是添加一個(gè)新的事件到RunLoop里面
            /* add new item to all common-modes */
            //這里就是遍歷commonModes集合,然后對每一個(gè)標(biāo)示(defaultMode和trackingMode)調(diào)用
            //第二個(gè)參數(shù)那個(gè)函數(shù),也就是在每一個(gè)commonModes的mode對象中都注冊了一個(gè)timer的事件源
            CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
            CFRelease(set);
        }
    } else {
      
    }
    __CFRunLoopUnlock(rl);
}

就是在每一個(gè)commonModes的mode對象中都注冊了一個(gè)timer的事件源

HTTP2.0

用Layer方法設(shè)置圓角時(shí),為什么會產(chǎn)生離屏渲染

根據(jù)Profile監(jiān)測幀率,發(fā)現(xiàn)大量的設(shè)置圓角,掉幀很嚴(yán)重。

UIViewController的生命周期

initWithCoder:(NSCoder *)aDecoder:(如果使用storyboard或者xib)
loadView:加載view
viewDidLoad: view加載完畢
viewWillAppear:控制器的view將要顯示
viewWillLayoutSubviews:控制器的view將要布局子控件(可能會多次調(diào)用)
viewDidLayoutSubviews:
viewDidAppear:
viewWillDisappear
viewDidDisappear

nil和null的區(qū)別

nil:用于表示指向oc中對象的指針為空
null:表示c指針為空
Nil:表示OC類(class)類型的變量值為空

點(diǎn)擊App icon到啟動App有什么過程

內(nèi)存管理問題

分別用assign、weak、strong修飾會輸出什么
assign警告Assigning retained object to weak property; object will be released after assignment運(yùn)行會崩潰,因?yàn)閍ssign修飾self.subView引用計(jì)數(shù)不會+1,創(chuàng)建完會自動釋放,變成野指針
weak 0 self.subView為null,同assign,但是weak修飾的對象釋放后會置成nil
strong 1

@property (nonatomic, assign) UIView *subView;

self.subView = [[UIView alloc] init];
NSLog(@"%f", self.subView.alpha);

NSProxy和NSObject設(shè)計(jì)代理類的差異

NSProxy不是NSObject的子類,但是實(shí)現(xiàn)了NSObject協(xié)議

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

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

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