【iOS】最新面試題

KVO 內部實現原理

  1. KVO是基于runtime機制實現的。
  2. 當某個類的對象第一次被觀察時,系統就會在運行期動態地創建該類的一個派生類,在這個派生類中重寫基類中任何被觀察屬性的setter 方法。
  3. 派生類在被重寫的 setter 方法實現真正的通知機制(Person?? NSKVONotifying_Person)

不用中間變量,用兩種方法交換 A 和 B 的值

A=A+B;
B=A-B;
A=A-B;
或者
A = A^B; 
B = A^B; 
A = A^B;

runtime 實現的機制是什么

runtime,運行時機制,它是一套C語言庫。
實際上我們編寫的所有OC代碼,最終都是轉成了runtime庫的東西,

比如:
類轉成了runtime庫里面的結構體等數據類型,
方法轉成了 runtime庫里面的C語言函數,平時調方法都是轉成了objc_msgSend 函數(所以說OC有個消息發送機制)

因此,可以說runtime是OC的底層實現,是OC的幕后執行者 有了runtime庫,能做什么事情呢?

runtime庫里面包含了跟類、成員 變量、方法相關的API,比如獲取類里面的所有成員變量,為類動態 添加成員變量,動態改變類的方法實現,為類動態添加新的方法等 因此,有了runtime,想怎么改就怎么改。

是否使用 Core Text 或者 Core Image

CoreText

  • 隨意修改文本的樣式
  • 圖文混排(純C語言)
  • 國外:Niumb

Core Image(濾鏡處理)

  • 能調節圖片的各種屬性(對比度, 色溫, 色差等)

NSNotification、KVO、delegate 的區別和用法是什么?

通知比較靈活:1個通知能被多個對象接收, 1個對象能接收多個通知

KVO性能不好(底層會動態產生新的類),只能監聽某個對象屬性的改 變, 不推薦使用(1個對象的屬性能被多個對象監聽, 1個 對象能監聽 多個對象的其他屬性)

代理比較規范,但是代碼多(默認是1對1)

delegate 代理的作用?

代理的目的是改變或傳遞控制鏈。
允許一個類在某些特定時刻通知到其他類,而不 需要獲取到那些類的指針。可以減少框架復雜度。
另外一點,代理可以理解為java 中的回調監聽機制的一種類似。

簡單說一下事件響應的流程?

  1. 一個 UIView 發出一個事件之后,首先上傳給其父視圖;
  2. 父視圖上傳給其所在的控制器;
  3. 如果其控制器對事件進行處理,事件傳遞將終止,否則繼續上傳父視圖;
  4. 直到遇到響應者才會停止,否則事件將一直上傳,直到 UIWindow。

用@property 聲明的 NSString(或 NSArray, NSDictionary)經常使用 copy 關鍵字,為什么? 如果改用 strong 關鍵字,可能造成什么問題?

因為父類指針可以指向子類對象,使用 copy 的目的是為了讓本對象的屬性不受外界影響,使用 copy 無論給我傳入是一個可變對象還是不可對象,我本身持有的就是一個 不可變的副本。

如果我們使用是 strong,那么這個屬性就有可能指向一個可變對象,如果這個可變對象在外部被修改了,那么會影響該屬性。

【復制詳解】
淺復制(shallow copy):在淺復制操作時,對于被復制對象的每一層都是指 針復制。
深復制(one-level-deep copy):在深復制操作時,對于被復制對象,至少有 一層是深復制。
完全復制(real-deep copy):在完全復制操作時,對于被復制對象的每一層都 是對象復制。
非集合類對象的 copy 與 mutableCopy [不可變對象 copy] // 淺復制 [不可變對象 mutableCopy] //深復制
[可變對象 copy] //深復制 [可變對象 mutableCopy] //深復制
集合類對象的 copy 與 mutableCopy [不可變對象 copy] // 淺復制 [不可變對象 mutableCopy] //單層深復制
[可變對象 copy] //單層深復制
[可變對象 mutableCopy] //單層深復制
這里需要注意的是集合對象的內容復制僅限于對象本身,對象元素仍然是 指針復制

這個寫法會出什么問題: @property (copy) NSMutableArray *array;

因為 copy 策略拷貝出來的是一個不可變對象,然而卻把它當成可變對象使用,很容 易造成程序奔潰。

這里還有一個問題,該屬性使用了同步鎖,會在創建時生成一些額外的代碼用于幫 助編寫多線程程序,這會帶來性能問題,通過聲明 nonatomic 可以節省這些雖然
很小但是不必要額外開銷,在 iOS 開發中應該使用 nonatomic 替代 atomic

如何讓自定義類可以用 copy 修飾符?如何重寫帶 copy 關鍵字的 setter?

若想令自己所寫的對象具有拷貝功能,則需實現 NSCopying 協議。如果自定義的對 象分為可變版本與不可變版本,那么就要同時實現 NSCopyiog 與 NSMutableCopying 協議,不過一般沒什么必要,實現 NSCopying 協議就夠了

// 實現不可變版本拷貝
- (id)copyWithZone:(NSZone *)zone; // 實現可變版本拷貝
- (id)mutableCopyWithZone:(NSZone *)zone;
// 重寫帶 copy 關鍵字的 setter
- (void)setName:(NSString *)name {
    _name = [name copy];
}

+(void)load; +(void)initialize;有什 么用處?

+(void)load; 當類對象被引入項目時, runtime 會向每一個類對象發送 load 消息。

load 方法會在每一個類甚至分類被引入時僅調用一次,調用的順序:父類優先于子 類, 子類優先于分類

由于 load 方法會在類被 import 時調用一次,而這時往往是改變類的行為的最佳時 機,在這里可以使用例如 method swizlling 來修改原有的方法

+(void)initialize;
也是在第一次使用這個類的時候會調用這個方法,也就是說 initialize 也是懶加載。

總結:
在 Objective-C 中,runtime 會自動調用每個類的這兩個方法
+load 會在類初始加載時調用
+initialize 會在第一次調用類的類方法或實例方法之前被調用

IBOutlet 連出來的視圖屬性為什么可以被設置成 weak?

因為父控件的 subViews 數組已經對它有一個強引用

1.首先要明確什么時候可以使用weak修飾?
一種情況是 : 在 ARC 中,在有可能出現循環引用的時候
另一種情況是 : 自身已經對它進行一次強引用,沒有必要再強引用一次

2.然后看IBOutlet連出來的視圖的引用關系
UIViewController →強引用→ UIView →強引用→ IBOutlet連出視圖


3.綜上所訴
所以問題場景滿足第二種使用weak的條件, 既UIViewController不需要對IBOutlet連出來的視圖再用strong修飾, 用weak即可。

什么情況使用 weak 關鍵字

在 ARC 中,在有可能出現循環引用的時候,往往要通過讓其中一端使用 weak 來解決,比如: delegate 代理屬性

自身已經對它進行一次強引用,沒有必要再強引用一次,此時也會使用 weak,自定義 IBOutlet 控件屬性一般也使用 weak;當然,也可以使用strong

weak修飾的屬性所指的對象遭到摧毀時,屬性值也會清空(nil)

請簡述 UITableView 的復用機制

每次創建 cell 的時候通過 dequeueReusableCellWithIdentifier:方法創建 cell,
它先到 緩存池中找指定標識的 cell,

如果沒有找到指定標識的 cell,就直接返回 nil,且會通過 initWithStyle:reuseIdentifier:創建一個 cell

當 cell 離開界面就會被放到緩存池中,以供下次復用

如何高性能的給 UIImageView 加個圓角?

不好的解決方案:
使用下面的方式會強制 Core Animation 提前渲染屏幕的離屏繪制, 而離 屏繪制就會給性能帶來負面影響,會有卡頓的現象出現

self.view.layer.cornerRadius = 5;
self.view.layer.masksToBounds = YES;

正確的解決方案:使用繪圖技術

- (UIImage *)circleImage {
  // NO 代表透明
  UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
  // 獲得上下文
  CGContextRef ctx = UIGraphicsGetCurrentContext();
  // 添加一個圓
  CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
  CGContextAddEllipseInRect(ctx, rect);
  // 裁剪 CGContextClip(ctx);
  // 將圖片畫上去
  [self drawInRect:rect];
  UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  // 關閉上下文 UIGraphicsEndImageContext();
  return image;
}

還有一種方案:使用了貝塞爾曲線"切割"個這個圖片, 給 UIImageView 添加了的圓 角,其實也是通過繪圖技術來實現的

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; 
UIImage *anotherImage = [UIImage imageNamed:@"image"];

UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:50] addClip]; 
[anotherImage drawInRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

[self.view addSubview:imageView];

描述下 SDWebImage 里面給 UIImageView 加 載圖片的邏輯

SDWebImage 中為 UIImageView 提供了一個分類 UIImageView+WebCache.h, 這個分類中有一個最常用的接口 sd_setImageWithURL:placeholderImage:,會在真實圖片出現前會先顯示占位圖片,當真實圖片被加載出來后在替換占位圖片 加載圖片的過程大致如下:

首先會在 SDWebImageCache 中尋找圖片是否有對應的緩存, 它會以 url 作為數據的索引先在內存中尋找是否有對應的緩存

如果緩存未找到就會利用通過 MD5 處理過的 key 來繼續在磁盤中查詢對應的數據,

如果找到了, 就會把磁盤中的數據加載到內存中,并將圖片顯示出來

如果在內存和磁盤緩存中都沒有找到,就會向遠程服務器發送請求,開始下載圖片下載后的圖片會加入緩存中,并寫入磁盤中 整個獲取圖片的過程都是在子線程中執行,獲取到圖片后回到主線程將圖片顯示出來。

你是怎么封裝一個 view 的

可以通過純代碼或者 xib 的方式來封裝子控件建立一個跟 view 相關的模型,然后將模型數據傳給 view,通過模型上的數據給 view 的子控件賦值。

/**
 * 純代碼初始化控件時一定會走這個方法
 */
- (instancetype)initWithFrame:(CGRect)frame {
  if(self = [super initWithFrame:frame]) {
      [self setup];
  }
    return self;
}

 /**
  * 通過 xib 初始化控件時一定會走這個方法 
  */
- (id)initWithCoder:(NSCoder *)aDecoder {
  if(self = [super initWithCoder:aDecoder]) {
      [self setup];
  }
      return self;
}
- (void)setup {
    // 初始化代碼 
}

觸摸事件的傳遞

觸摸事件的傳遞是從父控件傳遞到子控件 如果父控件不能接收觸摸事件,那么子控件就不可能接收到觸摸事件 不能接受觸摸事件的四種情況 不接收用戶交互,即:userInteractionEnabled = NO 隱藏,即:hidden = YES
透明,即:alpha <= 0.01
未啟用,即:enabled = NO
提示:UIImageView 的 userInteractionEnabled 默認就是 NO,因此 UIImageView 以 及它的子控件默認是不能接收觸摸事件的
如何找到最合適處理事件的控件:
首先,判斷自己能否接收觸摸事件 可以通過重寫 hitTest:withEvent:方法驗證 其次,判斷觸摸點是否在自己身上
對應方法 pointInside:withEvent: 從后往前(先遍歷最后添加的子控件)遍歷子控件,重復前面的兩個步驟 如果沒有符合條件的子控件,那么就自己處理

事件響應者鏈

如果當前 view 是控制器的 view,那么就傳遞給控制器;如果控制器不存在,則將其傳遞給它的父控件。

在視圖層次結構的最頂層視圖也不能處理接收到的事件或消息,則將事件或消息傳 遞給 UIWindow 對象進行處理。

如果 UIWindow 對象也不處理,則將事件或消息傳遞給 UIApplication 對象

如果 UIApplication 也不能處理該事件或消息,則將其丟棄

補充:如何判斷上一個響應者
如果當前這個 view 是控制器的 view,那么控制器就是上一個響應者 如果當前這個 view 不是控制器的 view,那么父控件就是上一個響應者

一個 objc 對象的 isa 的指針指向什么?有什 么作用?

每一個對象內部都有一個 isa 指針,這個指針是指向它的真實類型 根據這個指針就能知道將來調用哪個類的方法

下面的代碼輸出什么?

@implementation Son : Father 
- (id)init {
    if (self = [super init]) {
        NSLog(@"%@", NSStringFromClass([self class])); // Son
        NSLog(@"%@", NSStringFromClass([super class]));  //Son
    }
    return self; 
}
@end

// 答案:都輸出 Son

這個題目主要是考察關于 objc 中對 self 和 super 的理解:
self 是類的隱藏參數,指向當前調用方法的這個類的實例。而 super 本質是一個編譯器標示符,和 self 是指向的同一個消息接受者

當使用 self 調用方法時,會從當前類的方法列表中開始找,如果沒有,就從父類中 再找;
而當使用 super 時,則從父類的方法列表中開始找。然后調用父類的這個方法 調用[self class] 時,會轉化成 objc_msgSend 函數 id objc_msgSend(id self, SEL op, ...)

調 用 [super class] 時 , 會 轉 化 成 objc_msgSendSuper 函 數 id objc_msgSendSuper(struct objc_super *super, SEL op, ...)

第一個參數是 objc_super 這樣一個結構體,其定義如下 struct objc_super { __unsafe_unretained id receiver;
__unsafe_unretained Class super_class; };

第一個成員是 receiver, 類似于上面的 objc_msgSend 函數第一個參數 self 第二個成員是記錄當前類的父類是什么,告訴程序從父類中開始找方法,找到方法 后,最后內部是使用 objc_msgSend(objc_super->receiver, @selector(class))去調用, 此時已經和[self class]調用相同了,故上述輸出結果仍然返回 Son

objc Runtime 開源代碼對- (Class)class 方法的實現

  • (Class)class {
    return object_getClass(self);
    }

以下代碼運行結果如何?

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"1"); 
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"2"); 
    });
    NSLog(@"3"); 
}

// 答案:主線程死鎖

使用 block 時什么情況會發生引用循環,如 何解決?

系統的某些 block api 中,UIView 的 block 版本寫動畫時不需要考慮,但也有一 些 api 需要考慮

以下這些使用方式不會引起循環引用的問題

[UIView animateWithDuration:duration animations:^ {
    [self.superview layoutIfNeeded]; 
}];

[[NSOperationQueue mainQueue] addOperationWithBlock:^ {
    self.someProperty = xyz; 
}];

[[NSNotificationCenter defaultCenter] addObserverForName:@"someNotification" object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * notification)
{ 
    self.someProperty = xyz; 
}];

但如果方法中的一些參數是成員變量,那么可以造成循環引用,如:GCD 、 NSNotificationCenter 調用就要小心一點,比如 GCD 內部如果引用了 self,而且 GCD 的參數是 成員變量,則要考慮到循環引用,舉例如下:

// 【GCD】分析:self-->_operationsQueue-->block-->self 形成閉環,就造成了循環引用
__weak typeof(self) weakSelf = self;
dispatch_group_async(_operationsGroup, _operationsQueue, ^{
    [weakSelf doSomething];
    [weakSelf doSomethingElse]; 
});
// 【NSNotificationCenter】分析:self-->_observer-->block-->self 形成閉環,就造成了循環引用
__weak typeof(self) weakSelf = self;
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"testKey" object:nil queue:nil usingBlock:^(NSNotification *note) {
    [weakSelf dismissModalViewControllerAnimated:YES];
}];
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容