美文网首页
面试题一

面试题一

作者: a6750108cb56 | 来源:发表于2018-05-04 11:04 被阅读65次

    1、介绍下内存的几大区域?

    1. 栈区(stack)由编译器自动分配并释放,存放函数的参数值、局部变量等。栈是系统数据结构,对应线程/进程是唯一的。优点是快速高效,缺点是有限制,数据不灵活(先进后出)
      • 栈空间分静态分配和动态分配两种。

    静态分配是编译器完成的,比如总动变量(auto)的分配。
    动态分配是由alloca 函数完成。
    栈的动态分配无需释放(是自动的),也就是说没有释放函数
    为可移植的程序期间,栈的动态分配操作是不被鼓励的!

    1. 堆区(heap)由程序员分配和释放,如果程序员不释放,程序结束时,可能会由操作系统回收、比如在iOS中alloc都是存放在堆中。
      • 优点是灵活方便,数据适用面广泛,但是效率有一点降低。

    堆是函数库内部数据结构,不一定唯一。
    不同堆分配的内存无法互相操作
    堆空间的分配总是动态的。
    虽然程序结束时所有的数据空间都会被释放会系统,但是精确的申请内存、释放内存匹配是良好程序的基本要素。

    1. 全局区(静态区)(static)全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量存放在一块区域,未初始化的全局变量和静态变量在相邻的另一块区域,程序结束后由系统释放。

    注意:全局区又可分为未初始化全局区:.bss段 和初始化全局区:data段
    举例: int a;未初始化。
    int a = 10;已初始化的

    1. 文字常量区: 存放常量字符串,程序结束后由系统释放
    2. 代码区: 存放函数的二进制代码
    • 代码例子

    int a = 10; 全局初始化区
    char *p; 全局未初始化区

    main{
    int b; 栈
    char s[] = “abc”; 栈
    char *p1; 栈
    char *p2 = “123456”; p2在栈上,123456在常量区
    static int c = 0; 全局(静态)初始化区

    w1 = (char *)malloc(10);
    w2 = (char *)malloc(10);
    分配得来的10和20字节的区域都在堆区。
    }

    2、你是如何组件化解耦的?

    3、runtime如何通过selector找到对应的IMP地址?

    概述
    类对象中有类方法和实例方法的类表,列表中记录着方法的名字、参数和实现,而selector本质就是方法名称,runtime通过这个方法名称就可以在列表中找到该方法对应的实现。
    这里声明了一个指向struct objc_method_list 指针的指针,可以包含类方法类表和实例方法列表
    具体实现
    在寻找IMP的地址时,runtime提供了两种方法
    IMP class_getMethodImplementation(Class cla, SEL name);
    IMP method_getImplementation(Method m)
    类方法(假设有一个类A)
    class_getMethodImplementation(objc_getMetaClass(“A”),@selector(methodName));
    实例方法
    class_getMethodImplementation([A class],@selector(methodName));

    通过传入的参数不同,找到不同的方法列表,方法列表中保存着下面方法的结构体,机构体中包含着方法的实现,selector本质就是方法的名称,通过该方法名称,即可在结构体中找到相应的实现。

    struct objc_method {SEL method_namechar *method_typesIMP method_IMP}

    而对于第二种方法,传入的参数只有method,区分类方法和实例方法在于封装method的函数
    类方法
    Method class_getClassMethod(Class cls, SEL name)
    实例方法
    Method class_getInstanceMethod(Class cls, SEL name)
    最后调用IMP method_Implementation(Method m) 获取IMP地址

    实验

    - (void)viewDidLoad {
        [super viewDidLoad];
        Test *test1 = [[Test alloc] init];
        Test *test2 = [[Test alloc] init];
    }
    - (instancetype)init {
        self = [super init];
        if (self) {
            [self getIMPFromSelector:@selector(aaa)];
            [self getIMPFromSelector:@selector(test1)];
            [self getIMPFromSelector:@selector(test2)];
        }
        return self;
    }
    - (void)test1 {
        NSLog(@"test1");
    }
    + (void)test2 {
        NSLog(@"test2");
    }
    - (void)getIMPFromSelector:(SEL)aselector {
        
        //first method
        IMP instanceIMP1 = class_getMethodImplementation(objc_getClass("Test"), aselector);//实例方法
        IMP classIMP1 = class_getMethodImplementation(objc_getMetaClass("Test"), aselector);//类方法
        
        //second method
        Method instanceMethod = class_getInstanceMethod(objc_getClass("Test"), aselector);
        IMP instanceIMP2 = method_getImplementation(instanceMethod);
        
        Method classMethod1 = class_getClassMethod(objc_getClass("Test"), aselector);
        IMP classIMP2 = method_getImplementation(classMethod1);
        
        Method classMethod2 = class_getClassMethod(objc_getMetaClass("Test"), aselector);
        IMP classIMP3 = method_getImplementation(classMethod2);
        
          Method instanceMethod3 = class_getInstanceMethod(objc_getMetaClass("Test"), aselector);
        IMP classIMP4 = method_getImplementation(instanceMethod3);
    
        NSLog(@"instance1 = %p  instance2 = %p class1 = %p class2 = %p class3 = %p class4 = %p",instanceIMP1,instanceIMP2,classIMP1,classIMP2,classIMP3,classIMP4);
    

    这里有一个叫Test的类,在初始化方法中,调用了两次getIMPFromSelector方法,第一个aaa方法是不存在的,test1是实例方法,test2是类方法。
    然后实例化两个Test对象,打印数据如下:

    instance1 = 0x10a185600  instance2 = 0x0 class1 = 0x10a185600 class2 = 0x0 class3 = 0x0 class4 = 0x0
    instance1 = 0x10986c210  instance2 = 0x10986c210 class1 = 0x10a185600 class2 = 0x0 class3 = 0x0 class4 = 0x0
    instance1 = 0x10a185600  instance2 = 0x0 class1 = 0x10986c240 class2 = 0x10986c240 class3 = 0x10986c240 class4 = 0x10986c240
    instance1 = 0x10a185600  instance2 = 0x0 class1 = 0x10a185600 class2 = 0x0 class3 = 0x0 class4 = 0x0
    instance1 = 0x10986c210  instance2 = 0x10986c210 class1 = 0x10a185600 class2 = 0x0 class3 = 0x0 class4 = 0x0
    instance1 = 0x10a185600  instance2 = 0x0 class1 = 0x10986c240 class2 = 0x10986c240 class3 = 0x10986c240 class4 = 0x10986c240
    

    该地址0x10a185600一共出现了8次,这是因为在调用class_getMethodImpeletation()方法时,无法找到对应实现时返回的相同的一个地址,无论该方法是类方法还是实例方法,返回的地址是相同的,但是每次运行时返回的地址不相同。
    对于另一种方法,如果找不到对应实现,会返回0;

    还有一点:class_getClassMethod()方法的第一个参数无论传入objc_getClass()还是objc_getMetaClass(),最终调用method_getImplementation()都可以成功的找到类方法的实现;
    而class_getInstanceMethod()的第一个参数如果传入objc_getMetaClass(),再调用method_getImplementation()时无法找到实例方法的实现却可以找到类方法的实现。

    4、runloop内部实现逻辑? runLoop详解

    官方文档

    A run loop receices events from tso different types of sources.input sources deliver asynchoronous events,usually messages from another thread or from a different application. Timer sources deliver synchronous events,occurring at a scheduled time or repeating interval.Both types of source use an application-specific handler routine to process the event when it arrives.

    shows the conceptual structure of a run loop and a variety of sources.The input sources deliver asynchronous events to the corresponding handlers and cause the runUntiDate:method(called on the thread's associated NSRunLoop object) to exit. Timer sources deliver events to their handler routines but do not cause the run loop to exit.


    runloop.jpg

    翻译

    运行循环接收来自不同类型的源的事件。输入源提供异步事件,通常来自另一线程或来自不同应用程序的消息。 计时器源提供同步事件,发生在计划时间或重复间隔。两种类型的源使用特定于应用程序的处理程序来处理事件到达时。

    下图显示了运行循环和各种源的概念结构。输入源将异步事件传递给相应的处理程序,并导致runUntiDate:方法(在线程的关联NSRunLoop对象上调用)退出。 计时器源将事件传递到其处理程序例程,但不会导致运行循环退出。

    实际上 RunLoop 就是这样一个函数,其内部是一个 do-while 循环。当你调用 CFRunLoopRun() 时,线程就会一直停留在这个循环里;直到超时或被手动停止,该函数才会返回。
    苹果用 RunLoop 实现的功能。

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

    相关文章

      网友评论

          本文标题:面试题一

          本文链接:https://www.haomeiwen.com/subject/sgtflftx.html