将来跳槽用iOS面试资料搜集

iOS | 面试知识整理 - OC底层 (三)

2019-10-15  本文已影响0人  一意孤行的程序猿

前言:

最近公司项目不怎么忙, 闲暇时间把iOS 在面试中可能会遇到的问题整理了一番, 一部分题目是自己面试遇到的,一部分题目则是网上收录的, 方便自己巩固复习, 也分享给大家! 知识点比较多,比较杂,这里做了分类,下面是分类链接地址;

面试知识点整理 - 目录:

iOS | 面试知识整理 - OC基础 (一)
iOS | 面试知识整理 - OC基础 (二)
iOS | 面试知识整理 - OC基础 (三)
iOS | 面试知识整理 - UI 相 关 (四)
iOS | 面试知识整理 - 内存管理 (五)
iOS | 面试知识整理 - 多 线 程 (六)
iOS | 面试知识整理 - 网络相关 (七)
iOS | 面试知识整理 - 数据持久化 (八)
iOS | 面试知识整理 - Swift 基础 (九)

iOS | 面试知识整理 - OC底层 (三)

1. 一个OC对象占用多少内存

2. 对象的isa指针指向哪里?

3.OC的类信息存放在哪里?

4.iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)

5.如何手动触发KVO?

手动调用willChangeValueForKey:和didChangeValueForKey:

- (void)viewDidLoad {
[super viewDidLoad];

    Person *person = [[Person alloc]init];;
    [p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    [p willChangeValueForKey:@"name"];
    [p didChangeValueForKey:@"name"];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    NSLog(@"被观测对象:%@, 被观测的属性:%@, 值的改变: %@\n, 携带信息:%@", object, keyPath, change, context);
}

6.直接修改成员变量会触发KVO么?

7.通过KVC修改属性会触发KVO么?

8.KVC的赋值和取值过程是怎样的?原理是什么?

9.Category的使用场合是什么?

使用场合:

10.Category的实现原理

11.Category和Class Extension的区别是什么?

12.Category中有load方法吗?load方法是什么时候调用的?load 方法能继承吗?

13. initialize方法如何调用,以及调用时机

13. load、initialize方法的区别什么?它们在category中的调用的顺序?以及出现继承时他们之间的调用过程?

14. Category能否添加成员变量?如果可以,如何给Category添加成员变量?

15. block的原理是怎样的?本质是什么?

16. __block的作用是什么?有什么使用注意点?

17. block的属性修饰词为什么是copy?使用block有哪些使用注意?

17. block在修改NSMutableArray,需不需要添加__block?

18. Block 内部为什么不能修改局部变量,需要加__block

19.讲一下 OC 的消息机制

20. 消息发送流程

21. 动态方法解析机制

当我们发送消息未找到方法实现,就会进入第二步,动态方法解析: 代码实现如下

//  动态方法绑定- 实例法法调用
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(run)) {
        Method method = class_getInstanceMethod(self, @selector(test));
        class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
// 类方法调用
+(BOOL) resolveClassMethod:(SEL)sel....

22.消息转发机制流程

未找到动态方法绑定,就会进行消息转发阶段

// 快速消息转发- 指定消息处理对象
- (id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(run)) {
        return [Student new];
    }
    return  [super forwardingTargetForSelector:aSelector];
} 

// 标准消息转发-消息签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if(aSelector == @selector(run))
    {
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
   //内部逻辑自己处理 
}

23. 什么是Runtime?平时项目中有用过么?

22.runtime具体应用

23.unrecognized selector sent to instance 错误

该错误是基于OC的消息机制:

  1. 在方法列表中未找到方法实现
  2. 尝试动态方法解析,也未绑定犯法
  3. 进行消息转发,也未处理
  4. 最后进行报错

24.如果向一个nil对象发消息不会crash的话,那么message sent to deallocated instance的错误是怎么回事?

25. 向一个nill对象发送消息会发生什么?

26.代码打印结果:

@interface Person : NSObject
@end
@implementation Person
@end

@interface Student : Person
@end
@implementation Student
- (instancetype)init{
    if (self= [super init]) {
        NSLog(@"%@", [self class]);
        NSLog(@"%@", [super class]);
        NSLog(@"%@", [self superclass]);
        NSLog(@"%@", [super superclass]);
    }
}
[self class] 和 [super class] 都是给当前类返送消息,spuer 表示在父类中查找
[self superClass]  和 [super superclass] 也是也当前类发消息,返回父类
第一个打印:
Student / Student/ Person / Person

27.代码运行结果?

BOOL res1 = [[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [[Person class] isKindOfClass:[Person class]];
BOOL res4 = [[Person class] isMemberOfClass:[Person class]];
NSLog(@"%d-%d-%d-%d",res1, res2, res3, res4);

打印结果: 1 ,0, 0, 0

28.讲讲 RunLoop,项目中有用到吗?

29.runloop内部实现逻辑?

30.runloop和线程的关系?

31.timer 与 runloop 的关系?

32.程序中添加每3秒响应一次的NSTimer,当拖动tableview时timer可能无法响应要怎么解决?

 NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:nil];
 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

33. runloop的mode作用是什么?

runloop 只能在一种 mode 下运行, 做不同的事情,runloop 会切换到对应的 model 下来执行,默认是 kCFRunLoopDefaultMode 如果视图滑动再回切换到 UITrackingRunLoopMode,如果需要在多种 mode 下运行则需要手动设置 kCFRunLoopCommonModes;

  1. kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
  2. UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
  3. UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用,会切换到kCFRunLoopDefaultMode
  4. GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
  5. kCFRunLoopCommonModes: 这是一个占位用的Mode,作为标记kCFRunLoopDefaultMode和UITrackingRunLoopMode用,并不是一种真正的Mode

34.使用method swizzling要注意什么?

  1. 方式无限循环
  2. 进行版本迭代的时候需要进行一些检验,防止系统库的函数发生了变化

35. 一个系统方法被 多次交换,会有什么影响吗?以及调用顺序?原理

都会执行,后交换的会先调用.

第一次交换   viewwillAppAppear 和 test1 的指向的方法实现地址发生变化
第二次交换   viewwillAppAppear 和 test2 实际上等于是 test2 和 test1 进行了交换,因为 viewwillAppAppear 已经变为了 test1了.

调用 --> viewwillAppAppear
实际调用顺序 -->test2--->test1-->viewwillAppAppear
形成一个闭环:viewwillAppAppear 也只会调用一次

36.runloop 主线程监听卡顿

- (void)start
{
    if (observer)
        return;

    // // 创建信号
    semaphore = dispatch_semaphore_create(0);

    // 注册RunLoop状态观察
    CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL};
    observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
                                       kCFRunLoopAllActivities,
                                       YES,
                                       0,
                                       &runLoopObserverCallBack,
                                       &context);
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);

    // 在子线程监控时长
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (YES)
        {
            // 假定连续5次超时50ms认为卡顿(当然也包含了单次超时250ms)
            long st = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 50*NSEC_PER_MSEC));
            // Returns zero on success, or non-zero if the timeout occurred.
            if (st != 0)
            {
                if (!observer)
                {
                    timeoutCount = 0;
                    semaphore = 0;
                    activity = 0;
                    return;
                }

                // kCFRunLoopBeforeSources 即将处理source kCFRunLoopAfterWaiting 刚从睡眠中唤醒
                // RunLoop会一直循环检测,从线程start到线程end,检测检测到事件源(CFRunLoopSourceRef)执行处理函数,首先会产生通知,corefunction向线程添加runloopObservers来监听事件,并控制NSRunLoop里面线程的执行和休眠,在有事情做的时候使当前NSRunLoop控制的线程工作,没有事情做让当前NSRunLoop的控制的线程休眠。

                if (activity == kCFRunLoopBeforeSources || activity == kCFRunLoopAfterWaiting)
                {

                    if (++timeoutCount < 3)
                        continue;

                     NSLog(@"有点儿卡");
                }
            }
            timeoutCount = 0;
        }
    });
}

37. _objc_msgForward 函数是做什么的?直接 调用它将会发生什么?

38. 如何打印一个类中的所有实例变量

OC的类实际上是一个objc_class类型的结构体,包含了实例变量列表: (objc_ivar_list),可以通过 runtime 函数来获取这个列表:
OBJC_EXPORT Ivar _Nonnull * _Nullable class_copyIvarList(Class _Nullable cls, unsigned int * _Nullable outCount)

例子:

Student *stu = [[Student alloc]init];
stu.stu_name = @"alex";
stu.stu_age = 10;

unsigned int count = 0;
Ivar *list = class_copyIvarList([stu class], &count);
NSMutableDictionary * dict = [NSMutableDictionary dictionary];
for (int i = 0; i< count; i++){
    id iVarName = [NSString stringWithUTF8String:ivar_getName(list[i])];
    dict[iVarName] = [stu valueForKey:iVarName];
}

NSLog(@"%@",dict);

39. 如何使用 rumtime 动态添加一个类

runtime 很强大.可以动态的创建一个全新的类或对象

// 添加一个继承NSObject的类 类名是MyClass
Class MyClass = objc_allocateClassPair([NSObject class], "MyClass", 0);
// 增加实例变量
class_addIvar(MyClass, "_age", sizeof(NSString *), 0, "@");
//注册这个类到runtime系统中就可以使用他了
objc_registerClassPair(MyClass);
//生成了一个实例化对象
id myobj = [[MyClass alloc] init];
//给刚刚添加的变量赋值
[myobj setValue:@30 forKey:@"age"];
// 打印
NSLog(@"age= %@",[myobj valueForKey:@"age"]);

下一篇入口:

iOS | 面试知识整理 - UI 相 关 (四)

欢迎加入iOS开发交流学习群,我们一起共同学习,共同成长
上一篇 下一篇

猜你喜欢

热点阅读