面试亲历一些题 | 五

一,如何通过View找到其所在的Controller。

根据响应链去查找,响应链中的对象有View,Controller,Window,UIApplication,都是继承于UIResponder。
UIView和UIViewController的基类都是UIResponder,UIResponder用于处理事件响应。
1,首先,UIView 的 nextResponder指向它的 SuperView;如果 UIView 已经是其所在的 UIViewController 的 self.view,也就是top View,那么 UIView 的 nextResponder 就是 UIViewController;
所以UIViewController.view.nextResponder = UIViewController;
2,UIWindow 的 nextResponder 是 UIApplication;
3,UIApplication 的 nextResponder 是 AppDelegate;

//UIView类别中方法
-(UIViewController *)viewController{
    UIViewController *viewController = nil;
    UIResponder *next = self.nextResponder;
    while (next) {
        if ([next isKindOfClass:[UIViewController class]]) {
            viewController = (UIViewController *)next;
            break;
        }
        next = next.nextResponder;
    }
    return viewController;
}

二,给定一个排序数组,删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度,要求不是用额外数组空间,并且空间复杂度为O(1)下完成。

    NSInteger array[] = {0,0,1,1,1,2,2,3,3,4};
    NSInteger length=sizeof(array)/sizeof(array[0]);
    NSInteger k = 0;
    for (NSInteger i = 1; i < length; i++) {
        if(array[i] != array[k]){
            k++;
            array[k] = array[i];
        }
    }
    NSLog(@"%ld",k + 1);
    //K + 1就是去重后的元素
    for(NSInteger j = 0 ; j < length ; j++ ){
        printf("元素 - - %ld\n",(long)array[j]);
    }

空间复杂度为O(1),也就是当前计算方法时长应该是O(1),随着数据的增加,算法的使用空间不会增加,因为已经给定了当前数组的控件,不需要开辟别的。

三,以下代码会怎么执行,为什么?

@interface NSObject (Chain)
+ (void)runChainTest;
@end
@implementation NSObject (Chain)
- (void)runChainTest
{
    NSLog(@"runChainTest");
}
@end
- (void)viewDidLoad {
    [super viewDidLoad];
    [NSObject runChainTest];
  //objc_msgSend(objc_getClass("NSObject"),@selector(runChainTest));
}

NSObject -> 元类 寻找
元类 -> 他的父类父类(根元类) 寻找
根元类就是自己(NSObject)
那么NSObject对象的实例方法和类方法都是在NSObject中去寻找,所以发消息的NSObject就是当前NSObject的对象,直接调用了实例方法。
1,首先NSObject会在自己的元类的方法列表中去寻找此方法,如果没有找到;
2,那么就回去元类的父类,也就是根元类中去寻找此方法,NSObject的根元类找,也就是自己;
3,那么NSObject对象的实例方法和类方法都是在NSObject中去寻找,所以发消息的NSObject就是当前NSObject的对象,也就是NSObject是一个实例,会去调用它本身实例方法列表中注册的实例方法,也就是减号方法。

拓展:
方法只在.h中进行了声明,.m没有去实现,runtime是没有注册到当前的实例结构体中的方法列表中,所以runtime找不到此方法。
只有实现了方法才能被runtime找到,因为实现了的方法已经被注册到了实例结构体中的方法列表中。

分析:

+Method,调用者是类,调用方法存在元类结构体的方法列表中;
-Method,调用者是实例,调用方法存在当前对象类的结构体方法列表中;
+Method;   去NSObject的元类;
+Method;   去NSObject的元类的父类(根元类)(指向自己NSObject);
+Method;   又去NSObject的元类;

四,@synchronized与dispatch_once创建单例时的区别?

@synchronized会创建一个递归(recursive)互斥(mutex)的锁与 object参数进行关联。对当前绑定的对象进行了转换等递归操作,进行一系列的查询和创建工作,比较耗费系统资源。
dispatch_once,内部则使用了很多原子操作来替代锁,以及通过信号量来实现线程同步,而且有很多针对处理器优化的地方,甚至在if判断语句上也做了优化,使得其效率有很大的提升(相对于@synchronized来说)。

五,UIButton的继承关系是什么样的,他们的职能分别是什么?

继承关系:
UIButton -> UIControl -> UIView -> UIResponder -> NSObject

UIControl控制管理当前UIButton的各种事件,首先接受UIButton传递过来的事件,进行处理之后交付给UIResponder进行事件传递处理。
UIView管理和显示UIButton上的视图内容。
UIResponder去处理UIButton的事件,真正处理类。

六,atomic内部原理,那么它一定是安全的吗?

后续:

dispatch_once 和synchronized区别。

蒙版,水印,遮罩。

自动释放池和runloop的搭配关系。

UIButton的继承关系;

UIButton下UIControl和UIResponder,UIView之间的区别和职能。

单向链表倒数第K个数的实现。

weak底层原理

自动释放池和runloop的搭配关系。

@syncronized底层原理。

Leave a Reply

Required fields are marked *