关于ios:iOS开发你面试能用到的iOS面试题二

33次阅读

共计 6576 个字符,预计需要花费 17 分钟才能阅读完成。

题目起源自这里, 笔者对常识类问题和教训类问题做了解答, 答案有脱漏的中央心愿大家能补充, 这是你能用到的面试题(一)

为大家总结一份残缺的 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,群文件间接获取!

正文完
 0