iOS 面試總結(jié)
UI相關(guān)面試題
系統(tǒng)的UI事件傳遞機(jī)制是怎樣的?
hidtest 和hidpoint 內(nèi)部實(shí)現(xiàn)
先判斷自己是否隱藏或者支持用戶響應(yīng)或者是否具有透明度,然后從頂部倒序開(kāi)始尋找相應(yīng)視圖
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if (!self.userInteractionEnabled ||
[self isHidden] ||
self.alpha <= 0.01) {
return nil;
}
if ([self pointInside:point withEvent:event]) {
//遍歷當(dāng)前對(duì)象的子視圖
__block UIView *hit = nil;
[self.subviews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// 坐標(biāo)轉(zhuǎn)換
CGPoint vonvertPoint = [self convertPoint:point toView:obj];
//調(diào)用子視圖的hittest方法
hit = [obj hitTest:vonvertPoint withEvent:event];
// 如果找到了接受事件的對(duì)象推捐,則停止遍歷
if (hit) {
*stop = YES;
}
}];
if (hit) {
return hit;
}
else{
return self;
}
}
else{
return nil;
}
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
CGFloat x1 = point.x;
CGFloat y1 = point.y;
CGFloat x2 = self.frame.size.width / 2;
CGFloat y2 = self.frame.size.height / 2;
double dis = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
// 67.923
if (dis <= self.frame.size.width / 2) {
return YES;
}
else{
return NO;
}
}
使UITableView滾動(dòng)更流暢得方案和思路都有哪些?
cpu+GPU
cpu 采用子線程進(jìn)行對(duì)象創(chuàng)建銷毀,包括進(jìn)行預(yù)排版,以及圖片的解碼,
Gpu 和異步繪制方案
kvo實(shí)現(xiàn)原理,kvc實(shí)現(xiàn)原理,通知實(shí)現(xiàn)原理
用分類做什么
1聲明私有方法
2分解體積龐大的類文件
3把framework的私有方法進(jìn)行公開(kāi)
分類的特點(diǎn)
運(yùn)行時(shí)決議
可以為系統(tǒng)類添加分類
分類可以添加哪些內(nèi)容
實(shí)例方法
類方法
協(xié)議
屬性
成員變量(使用關(guān)聯(lián)對(duì)象)
擴(kuò)展
編譯時(shí)決議
只以聲明的形式存在,多數(shù)情況下寄生于宿主類的.m中
不能為系統(tǒng)添加擴(kuò)展
代理
代理是一種軟件設(shè)計(jì)模式
代理是用代理模式實(shí)現(xiàn)
傳遞一對(duì)一
通知
使用觀察者模式來(lái)實(shí)現(xiàn)的用于跨層傳遞消息的機(jī)制
傳遞方式為一對(duì)多
kvo
kvo是Objiective-C對(duì)觀察者設(shè)計(jì)模式的又一種實(shí)現(xiàn)
Apple 使用了isa混寫(xiě)(is-swizzling)來(lái)實(shí)現(xiàn)kvo
runtime
blcok
對(duì)象、函數(shù)封裝筒愚、執(zhí)行上下文
不需要__blcok 的情況靜態(tài)局部變量寡喝、全局變量、靜態(tài)全局變量
該解決循環(huán)引用有一個(gè)問(wèn)題,就是,如果不調(diào)用該方法,那么這個(gè)環(huán)會(huì)一直存在
什么是blcok
是一個(gè)對(duì)象扔役、對(duì)函數(shù)封裝爽丹、執(zhí)行上下文
為什么block會(huì)產(chǎn)生循環(huán)引用
自循環(huán)引用,多循環(huán)引用
1乳附、如果說(shuō)當(dāng)前blcok對(duì)當(dāng)前某一成員變量進(jìn)行截獲,block會(huì)對(duì)對(duì)應(yīng)變量,有一個(gè)強(qiáng)引用,當(dāng)前block由于當(dāng)前對(duì)象有一個(gè)強(qiáng)引用,產(chǎn)生一個(gè)自循環(huán)引用,可以通過(guò)聲明為_(kāi)_weak 變量來(lái)進(jìn)行循環(huán)引用的消除
2、如果說(shuō)我們定義一個(gè)__blcok說(shuō)明符,也會(huì)產(chǎn)生循環(huán)引用,但是是區(qū)分場(chǎng)景的,一種是在arc下面是會(huì)產(chǎn)生循環(huán)引用的,而在mrc下面不會(huì).在arc下面可以通過(guò)斷環(huán)的方式去解除對(duì)應(yīng)的循環(huán)引用,但是有一個(gè)弊端色罚、如果block一直得不到調(diào)用,這個(gè)循環(huán)引用是沒(méi)有辦法解除的
怎么理解截獲變量的特性
1對(duì)于基本類型的局部變量,是對(duì)其值進(jìn)行截獲,
2對(duì)于對(duì)象類型的局部變量,是對(duì)其進(jìn)行強(qiáng)引用,連通全所有修飾符共同進(jìn)行截獲
3對(duì)于靜態(tài)的局部變量,是對(duì)其指針進(jìn)行截獲
4對(duì)于全局變量碰缔、局部、靜態(tài)全局變量不產(chǎn)生截獲
多線程
iOS 提供哪幾種多線程
1 GCD
2 NSOperation 網(wǎng)絡(luò)請(qǐng)求,圖片下載 ,可以進(jìn)行狀態(tài)改變
3 NSThread 可以實(shí)現(xiàn)常駐線程
GCD
同步/異步 和串行/并發(fā)
dispatch_barrier_async 異步柵欄調(diào)用 解決多讀單寫(xiě)的問(wèn)題
dispathc_group
performSelector 方法調(diào)用必須保證當(dāng)前線程有runLoop,否則該方法會(huì)失效;
NSOperation 優(yōu)勢(shì)
添加任務(wù)依賴
任務(wù)執(zhí)行狀態(tài)控制
控制最大并發(fā)量
iOS 當(dāng)中都有哪些鎖
@synchronized 一般在創(chuàng)建單利對(duì)象的時(shí)候使用,保證在多線程環(huán)境中是唯一的
atomic 修飾屬性的關(guān)鍵字,對(duì)被修飾的對(duì)象進(jìn)行原子操作
OSSpinLock 自旋鎖,循環(huán)等待詢問(wèn),不釋放當(dāng)前資源,用于輕量級(jí)數(shù)據(jù)訪問(wèn),簡(jiǎn)單的int值 +1/-1操作
NSRecursiveLock 遞歸鎖 特點(diǎn)可以重入
NSLock 用于解決細(xì)密度的線程同步問(wèn)題,用于解決線程互斥和進(jìn)入自己的臨界區(qū)
dispatch_semaphore_t 信號(hào)量
runLoop
什么是runloop
RunLoop是通過(guò)內(nèi)部維護(hù)的事件循環(huán)來(lái)對(duì)事件/消息進(jìn)行管理的對(duì)象
沒(méi)有消息處理時(shí),休眠以避免資源占用
有消息處理時(shí)立刻被喚醒
在main函數(shù)中
滑動(dòng)tableview的時(shí)候我們的定時(shí)器還會(huì)生效嗎?
kcfRunLoopDefaultModel 滑動(dòng)tableView 會(huì)切換 UITrackingRunLoopMode 就不會(huì)生效了
解決方案,將Nstimer加入到CommonMode 中
RunLoop與線程關(guān)系
RunLoop與線程是一一對(duì)應(yīng)的
實(shí)現(xiàn)常駐線程
為當(dāng)前線程開(kāi)啟一個(gè)RunLoop
向該RunLoop中添加一個(gè)Port/Source等維持RunLoop的事件循環(huán)
啟動(dòng)該RunLoop
代碼實(shí)現(xiàn)
怎樣保證子線程數(shù)據(jù)來(lái)回更新UI的時(shí)候不打斷用戶滑動(dòng)操作?
用戶進(jìn)行滑動(dòng)的時(shí)候,runLoop在model 會(huì)被切換為 UITrackingRunLoopMode
網(wǎng)絡(luò)請(qǐng)求時(shí),是在子線程進(jìn)行操作,請(qǐng)求完成之后回來(lái)在主線程更新UI
在主線程邏輯進(jìn)行包裝,將他包裝在DefautModel下執(zhí)行.
HTTP 協(xié)議
get用于獲取資源,安全的,冪等的,可緩存
post 處理資源,非安全的,非冪等的,不可緩存
Http 無(wú)連接的,無(wú)狀態(tài)的
UDP 復(fù)用戳护、分用,差錯(cuò)檢驗(yàn)
DNS
域名到IP地址的映射,DNS解析請(qǐng)求采用UDP數(shù)據(jù)報(bào),且明文
DNS解析查詢方式,遞歸查詢,迭代查詢
DNS解析存在哪些常見(jiàn)問(wèn)題?
DNS劫持問(wèn)題:
DNS解析轉(zhuǎn)發(fā)問(wèn)題
DNS劫持與HTTP的關(guān)系是怎么樣的?
兩者之間是沒(méi)有關(guān)系的,第一解析是在客戶端本地的,是發(fā)生在與HTTP建立連接之前進(jìn)行的,2DNS解析請(qǐng)求使用UDP數(shù)據(jù)報(bào),端口號(hào)為53
DNS解析轉(zhuǎn)發(fā)
設(shè)計(jì)模式:6種
責(zé)任鏈:事件響應(yīng)機(jī)制
僑界模式
適配器模式
對(duì)象適配器和被適配器,
單例模式:
命令模式:行為參數(shù)化,降低代碼重合度
設(shè)計(jì)原則
單一職責(zé)原則:一個(gè)類只負(fù)責(zé)一件事,如:view 負(fù)責(zé)視圖展示,clayer 負(fù)責(zé)動(dòng)畫(huà)
開(kāi)關(guān)原則: 對(duì)修改關(guān)閉金抡、對(duì)擴(kuò)展開(kāi)放
接口隔離原則:使用多個(gè)專門(mén)的協(xié)議、而不是一個(gè)龐大臃腫的協(xié)議.例如:tableView ,數(shù)據(jù)返回和事件處理
依賴倒置原則:抽象不應(yīng)該依賴具體實(shí)現(xiàn),具體實(shí)現(xiàn)依賴抽象
里氏替換原則:父類可以被子類無(wú)縫替換,切原有的功能不受任何影響 例如:kvo
迪米特法制:一個(gè)對(duì)象應(yīng)當(dāng)對(duì)其他對(duì)象盡可能少的了解
高內(nèi)聚腌且、低耦合
文件緩存:
淘汰原則:1先進(jìn)先出,
LRU算法進(jìn)行淘汰,時(shí)間訪問(wèn)優(yōu)先選擇,訪問(wèn)頻率算法
網(wǎng)絡(luò)設(shè)計(jì)
網(wǎng)絡(luò)部分設(shè)計(jì)需要考慮哪些問(wèn)題?
圖片請(qǐng)求最大并發(fā)量,
請(qǐng)求超時(shí)策略:對(duì)一個(gè)數(shù)據(jù)進(jìn)行2次或者3次請(qǐng)求,失敗后,
請(qǐng)求優(yōu)先級(jí)
算法
鏈表反轉(zhuǎn)
//
// ReversList.h
// GCD
//
// Created by 龔魁華 on 2020/4/23.
// Copyright ? 2020 龔魁華. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
//定義一個(gè)鏈表
struct Node {
int data;
struct Node *next;
};
@interface ReversList : NSObject
//鏈表反轉(zhuǎn)
struct Node* reverseList(struct Node *head);
//構(gòu)造一個(gè)鏈表
struct Node* contructList(void);
//打印鏈表中的數(shù)據(jù)
void printList(struct Node *head);
@end
NS_ASSUME_NONNULL_END
//
// ReversList.m
// GCD
//
// Created by 龔魁華 on 2020/4/23.
// Copyright ? 2020 龔魁華. All rights reserved.
//
#import "ReversList.h"
@implementation ReversList
//鏈表反轉(zhuǎn)
struct Node* reverseList(struct Node *head){
//定義遍歷指針,初始化為頭節(jié)點(diǎn)
struct Node *p = head;
//反轉(zhuǎn)后的鏈表頭部
struct Node *newH = NULL;
//遍歷鏈表
while (p != NULL) {
//記錄下一個(gè)結(jié)點(diǎn)
struct Node *temp = p->next;
//當(dāng)前結(jié)點(diǎn)的next指向鏈表頭部
p->next = newH;
//更改新鏈表頭部為當(dāng)前結(jié)點(diǎn)
newH = p;
//移動(dòng)p指針
p = temp;
}
//返回反轉(zhuǎn)后的鏈表頭結(jié)點(diǎn)
return newH;
}
//構(gòu)造一個(gè)鏈表
struct Node* contructList(void){
//頭結(jié)點(diǎn)定義
struct Node *head = NULL;
//記錄當(dāng)前尾結(jié)點(diǎn)
struct Node *cur = NULL;
for (int i = 1; i < 5; i++) {
struct Node *node = malloc(sizeof(struct Node));
node->data = i;
//頭結(jié)點(diǎn)為空,新結(jié)點(diǎn)即為頭結(jié)點(diǎn)
if (head == NULL) {
head = node;
}
//當(dāng)前結(jié)點(diǎn)的next為新結(jié)點(diǎn)
else{
cur->next = node;
}
//設(shè)置當(dāng)前結(jié)點(diǎn)為新結(jié)點(diǎn)
cur = node;
}
return head;
}
//打印鏈表中的數(shù)據(jù)
void printList(struct Node *head){
struct Node *temp = head;
while (temp != NULL) {
printf("node is %d\n",temp->data);
temp = temp->next;
}
}
@end
字符串交換
//字符串交換
void char_reverse(char *cha){
//指向第一個(gè)字符
char *begin = cha;
char *end = cha + strlen(cha) -1;
while (begin<end) {
char temp = *begin;
*(begin ++) = *end;
*(end--) = temp;
}
}
有序數(shù)組合并
/// 有序數(shù)組合并
/// @param a 第一個(gè)數(shù)組
/// @param aLen 數(shù)組長(zhǎng)度
/// @param b 第二個(gè)數(shù)組
/// @param bLen <#bLen description#>
/// @param result 合并后結(jié)果
void mergeList(int a[],int aLen,int b[],int bLen,int result[]){
int p = 0;//遍歷數(shù)組a的指針
int q = 0;//遍歷數(shù)組b的指針
int i = 0;//記錄當(dāng)前存儲(chǔ)位置
//任意數(shù)組沒(méi)有達(dá)到邊界則進(jìn)行遍歷
while (p<aLen&&q<bLen) {
//如果啊數(shù)組對(duì)應(yīng)位置的值小于b數(shù)組對(duì)應(yīng)位置的值
if (a[p] <= b[q]) {
//存儲(chǔ)a數(shù)組的值
result[i] = a[p];
//移動(dòng)a數(shù)組的遍歷指針
p++;
}else{
//存儲(chǔ)b數(shù)組的值
result[i] = b[q];
q++;
}
//指向合并結(jié)果的下一個(gè)存儲(chǔ)位置
i++;
}
//如果a數(shù)組有剩余
while (p < aLen) {
//將a數(shù)組剩余部分拼接到合并結(jié)果的后面
result[i] = a[p++];
i++;
}
//如果b數(shù)組有剩余
while (q < bLen) {
//將b數(shù)組剩余部分拼接到合并結(jié)果的后面
result[i] = b[q++];
i++;
}
}
尋找2個(gè)view的共同父視圖
- (NSArray <UIView *> *)finCommonSuperView:(UIView *)viewOne other:(UIView *)viewOther{
NSMutableArray *result = [NSMutableArray array];
//查找第一個(gè)父視圖的所有父視圖
NSArray *arrayOne = [self findSuperViews:viewOne];
//查找第二個(gè)父視圖的所有父視圖
NSArray *arrayOther = [self findSuperViews:viewOther];
int i = 0;
//越界限制條件
while (i<MIN(arrayOne.count, arrayOther.count)) {
//倒序方式獲取各個(gè)視圖的父視圖
UIView *superOne = [arrayOne objectAtIndex:arrayOne.count -i -1];
UIView *superOther = [arrayOther objectAtIndex:arrayOther.count -i -1];
//比較如果相等,則為共同父視圖
if (superOne == superOther) {
[result addObject:superOne];
i++;
}
//如果不相等,則結(jié)束遍歷
else{
break;
}
}
return result;
}
- (NSArray <UIView *> *)findSuperViews:(UIView *)view{
//初始化為第一父視圖
UIView *temp = view.superview;
//保存結(jié)果數(shù)組
NSMutableArray *result = [NSMutableArray array];
while (temp) {
[result addObject:temp];
//順著superview指針一直向上查找
temp = temp.superview;
}
return result;
}
無(wú)序數(shù)組中位數(shù)查找
int findMedian(int a[],int aLen){
int low = 0;
int high = aLen -1;
int mid = (aLen - 1)/2;
int div = PartSort(a,low,high);
while (div != mid) {
if (mid < div) {
//左半?yún)^(qū)間查找
div = PartSort(a,low,div - 1);
}else{
//右半?yún)^(qū)間查找
div = PartSort(a,div+1,high);
}
}
//找到了
return a[mid];
}
int PartSort(int a[], int start,int end){
int low = start;
int high = end;
//選取關(guān)鍵字
int key = a[end];
while (low<high) {
//左邊找比key大的值
while (low<high && a[low] <= key) {
++low;
}
//右邊著比key小的值
while (low<high&&a[high]>=key) {
--high;
}
if (low<high) {
//找到之后交換左右的值
int temp = a[low];
a[low] = a[high];
a[high] = temp;
}
}
int temp = a[high];
a[high] = a[end];
a[end] = temp;
return low;
}