> 為大家總結一份完整的2020年《大廠最新常問iOS面試題+答案》,**面試題合集答案**、**復習資料**,均有**完整PDF版**,需要的小伙伴加iOS技術分享群:761407670,群檔案直接獲取!
題目來源自這里,筆者對知識類問題和經驗類問題做了解答,答案有遺漏的地方希望大家能補充,這是你能用到的面試題(一)
Push Notification 是如何作業的?
- 推送通知分為兩種,一個是本地推送,一個是遠程推送
- 本地推送:不需要聯網也可以推送,是開發人員在APP內設定特定的時間來提醒用戶干什么
- 遠程推送:需要聯網,用戶的設備會于蘋果APNS服務器形成一個長連接,用戶設備會發送uuid和Bundle idenidentifier給蘋果服務器,蘋果服務器會加密生成一個deviceToken給用戶設備,然后設備會將deviceToken發送給APP的服務器,服務器會將deviceToken存進他們的資料庫,這時候如果有人發送訊息給我,服務器端就會去查詢我的deviceToken,然后將deviceToken和要發送的資訊發送給蘋果服務器,蘋果服務器通過deviceToken找到我的設備并將訊息推送到我的設備上,這里還有個情況是如果APP在線,那么APP服務器會于APP產生一個長連接,這時候APPF服務器會直接通過deviceToken將訊息推送到設備上
什么是 Runloop?
是一個與執行緒相關的機制,可以理解為一個回圈,在這個回圈里面等待事件然后處理事件.而這個回圈是基于執行緒的,在Cocoa中每個執行緒都有它的runroop,通過他這樣的機制,執行緒可以在沒有事件要處理的時候休息,有事件運行,減輕CPU壓力,這題可以衍生出為什么在滑動時會導致定時器失敗,在下面有解答
Toll-Free Bridging 是什么?什么情況下會使用?
Toll-Free Bridging用于在Foundation物件與Core Foundation物件之間交換資料,俗稱橋接
- 在ARC環境下,Foundation物件轉成 Core Foundation物件
- 使用
__bridge橋接以后ARC會自動2個物件 - 使用
__bridge_retained橋接需要手動釋放Core Foundation物件
- 使用
- 在ARC環境下, Core Foundation物件轉成 Foundation物件
- 使用
__bridge橋接,如果Core Foundation物件被釋放,Foundation物件也同時不能使用了,需要手動管理Core Foundation物件 - 使用
__bridge_transfer橋接,系統會自動管理2個物件
- 使用
當系統出現記憶體警告時會發生什么?
- 會將不在當前視窗上的view暫時移除
- 如果放任記憶體警告,最侄訓導致軟體強制被系統關閉
什么是 Protocol,Delegate 一般是怎么用的?
- 協議是一個方法簽名的串列,在其中可以定義若干個方法,遵守該協議的類可以實作協議里的方法,在協議中使用
@property只會生成setter和getter方法的宣告 - delegate用法:成為一個類的代理,可以去實作協議里的方法
autorelease 物件在什么情況下會被釋放?
- 分兩種情況:手動干預釋放和系統自動釋放
- 手動干預釋放就是指定autoreleasepool,當前作用域大括號結束就立即釋放
- 系統自動去釋放:不手動指定autoreleasepool,Autorelease物件會在當前的 runloop 迭代結束時釋放
- kCFRunLoopEntry(1):第一次進入會自動創建一個autorelease
- kCFRunLoopBeforeWaiting(32):進入休眠狀態前會自動銷毀一個autorelease,然后重新創建一個新的autorelease
- kCFRunLoopExit(128):退出runloop時會自動銷毀最后一個創建的autorelease
為什么 NotificationCenter 要 removeObserver? 如何實作自動 remove?
- 如果不移除的話,萬一注冊通知的類被銷毀以后又發了通知,程式會崩潰.因為向野指標發送了訊息
- 實作自動remove:通過自釋放機制,通過動態屬性將remove轉移給第三者,解除耦合,達到自動實作remove
當 TableView 的 Cell 改變時,如何讓這些改變以影片的形式呈現?
這里舉個例子,點擊cell以后以影片形式改變cell高度
@interface ViewController ()
@property (nonatomic, strong) NSIndexPath *index;
@end
@implementation ViewController
static NSString *ID = @"cell";
- (void)viewDidLoad {
[super viewDidLoad];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
cell.textLabel.text = [NSString stringWithFormat:@"%ld",(long)indexPath.row];
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 20;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(self.index == indexPath){
return 120;
}
return 60;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.index = indexPath;
[tableView deselectRowAtIndexPath:indexPath animated:TRUE];
// 重點是這2句代碼實作的功能
[tableView beginUpdates];
[tableView endUpdates];
}
為什么 UIScrollView 的滾動會導致 NSTimer 失效?
定時器里面有個runoop mode,一般定時器是運行在defaultmode上但是如果滑動了這個頁面,主執行緒runloop會轉到UITrackingRunLoopMode中,這時候就不能處理定時器了,造成定時器失效,原因就是runroop mode選錯了,解決辦法有2個,一個是更改mode為NSRunLoopCommonModes(無論runloop運行在哪個mode,都能運行),還有種辦法是切換到主執行緒來更新UI界面的重繪
為什么當 Core Animation 完成時,layer 又會恢復到原先的狀態?
因為這些產生的影片只是假象,并沒有對layer進行改變.那么為什么會這樣呢,這里要講一下圖層樹里的呈現樹.呈現樹實際上是模型圖層的復制,但是它的屬性值表示了當前外觀效果,影片的程序實際上只是修改了呈現樹,并沒有對圖層的屬性進行改變,所以在影片結束以后圖層會恢復到原先狀態
你會如何存盤用戶的一些敏感資訊,如登錄的 token
- 使用keychain來存盤,也就是鑰匙串,使用keychain需要匯入
Security框架
自定義一個keychain的類
#import <Security/Security.h>
@implementation YCKKeyChain
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge_transfer id)kSecClassGenericPassword,(__bridge_transfer id)kSecClass,
service, (__bridge_transfer id)kSecAttrService,
service, (__bridge_transfer id)kSecAttrAccount,
(__bridge_transfer id)kSecAttrAccessibleAfterFirstUnlock,(__bridge_transfer id)kSecAttrAccessible,
nil];
}
+ (void)save:(NSString *)service data:(id)data {
// 獲得搜索字典
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
// 添加新的洗掉舊的
SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);
// 添加新的物件到字串
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge_transfer id)kSecValueData];
// 查詢鑰匙串
SecItemAdd((__bridge_retained CFDictionaryRef)keychainQuery, NULL);
}
+ (id)load:(NSString *)service {
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
// 配置搜索設定
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge_transfer id)kSecReturnData];
[keychainQuery setObject:(__bridge_transfer id)kSecMatchLimitOne forKey:(__bridge_transfer id)kSecMatchLimit];
CFDataRef keyData = NULL;
if (SecItemCopyMatching((__bridge_retained CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
@try {
ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge_transfer NSData *)keyData];
} @catch (NSException *e) {
NSLog(@"Unarchive of %@ failed: %@", service, e);
} @finally {
}
}
return ret;
}
+ (void)delete:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);
}
在別的類實作存盤,加載,洗掉敏感資訊方法
// 用來標識這個鑰匙串
static NSString * const KEY_IN_KEYCHAIN = @"com.yck.app.allinfo";
// 用來標識密碼
static NSString * const KEY_PASSWORD = @"com.yck.app.password";
+ (void)savePassWord:(NSString *)password
{
NSMutableDictionary *passwordDict = [NSMutableDictionary dictionary];
[passwordDict setObject:password forKey:KEY_PASSWORD];
[YCKKeyChain save:KEY_IN_KEYCHAIN data:passwordDict];
}
+ (id)readPassWord
{
NSMutableDictionary *passwordDict = (NSMutableDictionary *)[YCKKeyChain load:KEY_IN_KEYCHAIN];
return [passwordDict objectForKey:KEY_PASSWORD];
}
+ (void)deletePassWord
{
[YCKKeyChain delete:KEY_IN_KEYCHAIN];
}
有用過一些開源組件吧,能簡單說幾個么,大概說說它們的使用場景實作,
- AFN:網路請求
- FMDB:使用資料庫
- MJExtension: JSON與Model互轉
- SVProgressHUD:提示HUD
- Masonry:自動布局
- MJRefresh:下拉和上拉重繪
什么時候會發生 EXC BAD ACCESS 例外?
- 訪問一個僵尸物件,訪問僵尸物件的成員變數或者向其發訊息
- 死回圈
NSNotification 和 KVO 的使用場景?
- KVO使用場景:當一個物件的特定屬性改變的時候,需要被通知一個或者多個物件的時候
- NSNotification使用場景:跨層級傳遞值,多個物件通知多個物件
使用 Block 時需要注意哪些問題?
- 在block內部使用外部指標且會造成回圈參考情況下,需要用
__weak修飾外部指標__weak typeof(self) weakSelf = self; - 在block內部如果呼叫了延時函式還使用弱指標會取不到該指標,因為已經被銷毀了,需要在block內部再將弱指標重新強參考一下
__strong typeof(self) strongSelf = weakSelf; - 如果需要在block內部改變外部變數的話,需要在用
__block修飾外部變數
筆者也寫過一篇block博客
performSelector:withObject:afterDelay: 內部大概是怎么實作的,有什么注意事項么?
- 創建一個定時器,時間結束后系統會使用runtime通過方法名稱(Selector本質就是方法名稱)去方法串列中找到對應的方法實作并呼叫方法
- 注意事項
- 呼叫
performSelector:withObject:afterDelay:方法時,先判斷希望呼叫的方法是否存在respondsToSelector: - 這個方法是異步方法,必須在主執行緒呼叫,在子執行緒呼叫永遠不會呼叫到想呼叫的方法
- 呼叫
使用 NSUserDefaults 時,如何處理布爾的默認值?(比如回傳 NO,不知道是真的 NO 還是沒有設定過)
if([[NSUserDefaults standardUserDefaults] objectForKey:ID] == nil){
NSLog(@"沒有設定");
}
哪些途徑可以讓 ViewController 瘦下來?
- 把 Data Source 和其他 Protocols 分離出來(將UITableView或者UICollectionView的代碼提取出來放在其他類中)
- 將業務邏輯移到 Model 中(和模型有關的邏輯全部在model中寫)
- 把網路請求邏輯移到 Model 層(網路請求依靠模型)
- 把 View 代碼移到 View 層(自定義View)
有哪些常見的 Crash 場景?
- 訪問了僵尸物件
- 訪問了不存在的方法
- 陣列越界
- 在定時器下一次回呼前將定時器釋放,會Crash
點擊加入:iOS技術分享群
為大家總結一份完整的2020年《大廠最新常問iOS面試題+答案》,面試題合集答案、復習資料,均有完整PDF版,需要的小伙伴加iOS技術分享群:761407670,群檔案直接獲取!
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/173614.html
標籤:其他
