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對象
- 使用
橋接以後ARC會自動2個對象__bridge
- 使用
橋接需要手動釋放Core Foundation對象__bridge_retained
- 使用
- 在ARC環境下, Core Foundation對象轉成 Foundation對象
- 使用
橋接,如果Core Foundation對象被釋放,Foundation對象也同時不能使用了,需要手動管理Core Foundation對象__bridge
- 使用
橋接,系統會自動管理2個對象__bridge_transfer
- 使用
當系統出現記憶體警告時會發生什麼?
- 會将不在目前視窗上的view暫時移除
- 如果放任記憶體警告,最終會導緻軟體強制被系統關閉
什麼是 Protocol,Delegate 一般是怎麼用的?
- 協定是一個方法簽名的清單,在其中可以定義若幹個方法,遵守該協定的類可以實作協定裡的方法,在協定中使用
隻會生成setter和getter方法的聲明@property
- 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交流群681503716,請備注編号朝拜,大牛歡迎入駐,正在求職的也可以加入,大家一起交流學習,話糙理不糙,互相學習,共同進步,一起加油吧。)