iOS面试题-选择题

1.定义协议时,在协议名称后面加上<NBObject>的作用是:A、B、C

A.新定义的协议遵守 <NBObject> 协议
B.新定义的协议是 <NBObject> 协议的子协议
C.遵守新定义的协议的对象,也必须遵守 <NBObject> 协议
D.遵守新定义的协议的对象,必须是 NBObject 的子类

解析:如果一个协议继承于另外一个协议,声明方法为尖括号里面填入父协议名称。
@protocol SonProtocol<BaseProtocol> @end
既然代表是继承关系,那么A、B、C都符合

2.以下代码存在的问题是:C

dispatch_queue_t queue = dispatch_queue_create("com.apple.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(actionTime:) userInfo:nil repeats:YES];
});

- (void)actionTime:(NSTimer *)time {
    NSLog(@"------ %@",[NSDate date]);
}

A.运行时崩溃
B.死锁
C.定时器不执行
D.定时器只执行一次

解析:首先看 DISPATCH_QUEUE_CONCURRENT 并发队列 和 dispatch_async 异步执行,应该不会产生死锁问题
所以此处考察的是 NSTimer 的用法,确切说是 NSTimer 在子线程中的应用
主线程的 RunLoop 是默认开启的,但是在子线程中需要手动启动 RunLoop 对象才能进入运行循环,从而发挥定时器的作用

代码修改如下可以正常运行:

dispatch_queue_t queue = dispatch_queue_create("com.apple.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(actionTime:) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    // 这里需要注意不要使用[[NSRunLoop currentRunLoop] run],因为会无法终止这个 RunLoop
    // 而且这里必须是 NSDefaultRunLoopMode,使用 NSRunLoopCommonModes 的话定时器不执行
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
});

- (void)actionTime:(NSTimer *)time {
    NSLog(@"------ %@",[NSDate date]);
}
// 必要的时候使用  CFRunLoopStop(CFRunLoopGetCurrent()); 停止RunLoop

或者是改成这样:在主线程中设置定时器

dispatch_async(dispatch_get_main_queue(), ^{
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(actionTime:) userInfo:nil repeats:YES];
});

- (void)actionTime:(NSTimer *)time {
    NSLog(@"------ %@",[NSDate date]);
}

参考文章:如何正确的使用NSTimer
延伸阅读:iOS趣味篇:NSTimer到底准不准?

3.以下哪些情况容易造成循环引用:A、B、C、D

A. dispatch_async 的 block 中使用 self
B. 使用 strong 修饰的 delegate 属性
C. self 作为 NSNotificationCenter 的 observer
D. self 作为所持有的 NSTimer 的 target
延伸小测试:以下情景中有哪些会造成循环引用?

4.以下哪些类跟 UIView 有继承关系(直接、间接的父类或子类):A、D

A. UIControl
B. UIGestureRecognizer
C. CALayer
D. UIResponder

解析:
UIControl : UIView
UIGestureRecognizer : NSObject
CALayer : NSObject <NSSecureCoding, CAMediaTiming>
UIResponder : NSObject <UIResponderStandardEditActions>
UIView : UIResponder <NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace, UIFocusItem, UIFocusItemContainer, CALayerDelegate>
所以 UIControl : UIView : UIResponder 答案:A、D
UIGestureRecognizer、CALayer 是 UIView 的属性类,但不存在继承关系

5.以下关于 OC 消息机制说法不正确的是:B、F

A. OC 的实例对象的 isa 指针指向它的类对象,类对象的 isa 指针指向它的元类对象
B. OC 调用一个未实现的实例方法,我们可以在 NSObject 的 resolveClassMethod 中进行添加方法进行补救
C. OC 可以运用运行时特性向 Class 的对象中动态添加方法
D. OC 的实例方法是存放在相应实例对象的 methodList 中,静态方法存在相应类对象的 methodList 中
E. OC 可以运用运行时特性向对象的 Category 中实现动态绑定属性,达到 Category 添加成员变量的效果
F. OC 可以运用运行时特性通过 class_addIvar() 向编译好的 Class 添加成员变量

解析:A 正确,可以参考下方大图


类、元类 isa 指向

解析:B 错误, resolveClassMethod 方法是针对类方法的决议,如果是实例方法,需要通过 resolveInstanceMethod,示例代码如下

+ (BOOL)resolveInstanceMethod:(SEL)sel{
     
      if (sel == @selector(test)) {
          IMP imp = [self instanceMethodForSelector:@selector(myClassMethod)];
          class_addMethod(self, sel, imp, "v@:");
          return YES;
      }
    return YES;
}
- (void)myClassMethod {
    NSLog(@"resolveInstanceMethod");
}

解析 E 正确,实现代码如下:

@interface UIImageView (test)
@property(nonatomic, strong) NSObject *style;
@end

@implementation UIImageView (test)

static char styleKey;
- (void) setStyle:(NSObject *) style
{
    objc_setAssociatedObject(self, &styleKey, style, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSObject *) style
{
   return objc_getAssociatedObject(self, &styleKey);
}
@end

解析 F 错误
不能向编译后得到的类中增加实例变量,只能向运行时创建的类中添加实例变量。
因为编译后的类已经注册在 runtime 中,类结构体中的 objc_ivar_list 实例变量的链表和instance_size 实例变量的内存大小已经确定,同时 runtime 会调用 class_setIvarLayout 或 class_setWeakIvarLayout 来处理 strong weak 引用。所以不能向存在的类中添加实例变量。
运行时创建的类是可以添加实例变量,调用 class_addIvar 函数。但是得在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上。

参考链接:resolveInstanceMethod和resolveClassMethod
参考链接:iOS开发-Runtime运行时特性详解
参考链接:iOS (Category)runtime动态添加属性

6.在HTTP请求中,返回代码401和500代表下列哪两种状态:B、C

A:请求网页不存在
B:未授权
C:服务器内部错误
D禁止访问
参考:http协议的状态码

7.以下代码输出的日志是:E

dispatch_sync(dispatch_get_main_queue(), ^{
    printf("1");
});
printf("2");

A. 12
B. 21
C.1
D.2
E. 无输出

解析:dispatch_sync在等待block语句执行完成,而block语句需要在主线程里执行,所以dispatch_sync如果在主线程调用就会造成死锁;dispatch_sync是同步的,本身就会阻塞当前线程,也即主线程。而又往主线程里塞进去一个block,所以就会发生死锁。

8.在开发过程中,block的使用很容易造成内存泄漏,对于下面代码会不会造成内存问题? 需要如何处理?:B

[UIView animateWithDuration:0.25 animations:^{
        self.alpha = 0;
    }];

A.会造成内存问题
B. 不会造成内存问题
会的话, 要如何处理_______

解析:此处block的使用不会造成循环引用,也不存在内存泄漏

9.以下代码第二行编译不通过,修改不正确的是:D

NSError * error = nil;
NSError ** plError = &error;

A. __strong NSError ** pError = &error;
B. NSError __strong ** pError = &error ;
C. NSError * __strong * pError = &error;
D. NSError ** __strong pError = &error;

解析:pError实例对象跟前一定有*修饰

10.runloop Observer 可以监听的事件为:A、D

A、进入runloop和退出runloop
B、执行完timer 事件
C、执行source事件之前和执行完source事件
D、等待前和唤醒后

解析:
kCFRunLoopEntry = (1UL << 0), 进入工作
kCFRunLoopBeforeTimers = (1UL << 1), 即将处理Timers事件
kCFRunLoopBeforeSources = (1UL << 2), 即将处理Source事件
kCFRunLoopBeforeWaiting = (1UL << 5), 即将休眠
kCFRunLoopAfterWaiting = (1UL << 6), 被唤醒
kCFRunLoopExit = (1UL << 7), 退出RunLoop
kCFRunLoopAllActivities = 0x0FFFFFFFU 监听所有事件

推荐阅读更多精彩内容

  • 专业考题类型管理运行工作负责人一般作业考题内容选项A选项B选项C选项D选项E选项F正确答案 变电单选GYSZ本规程...
    小白兔去钓鱼阅读 6,951评论 0 13
  • 最全的iOS面试题及答案 iOS面试小贴士 ———————————————回答好下面的足够了-----------...
    编程新视野阅读 1,018评论 4 13
  • __block和__weak修饰符的区别其实是挺明显的:1.__block不管是ARC还是MRC模式下都可以使用,...
    LZM轮回阅读 2,020评论 0 5
  • 1.埋点是做什么的 2.如何进行埋点 3.埋点方案的设计 近期常被问到这个问题,我担心我的答案会将一些天真烂漫的孩...
    lxg阅读 1,215评论 0 1
  • 上一节Objective-C的对象——实例对象、类对象、元类对象我们总结出:OC中的对象分成三类,分别包括: in...
    鲸鱼座的男孩阅读 142评论 0 5