談?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)引用,怎么解決
不直接持有

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ī)制
給一個(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值
二叉樹的先序、中序、后序遍歷

前序:先遍歷根節(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。

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é)議