面试题总结

2019.12.04 途游

1. @property本质是什么?ivar、getter、setter是如何生成并添加到这个类中的?@synthesize与@dynamic的区别?
  1. 本质:
    @property其实就是在编译阶段由编译器自动帮我们生成ivar成员变量,getter方法,setter方法
  2. ivar、getter、setter是如何生成并添加到这个类中
  • 使用“自动合成”( autosynthesis)
  • 这个过程由编译器在编译阶段执行自动合成,所以编辑器里看不到这些“合成方法”(synthesized method)的源代码
  • 除了生成getter、setter方法之外,编译器还要自动向类中添加成员变量(在属性名前面加下划线,以此作为实例变量的名字)
  • 为了搞清属性是怎么实现的,反编译相关的代码,他大致生成了五个东西
    // 该属性的“偏移量” (offset),这个偏移量是“硬编码” (hardcode),表示该变量距离存放对象的内存区域的起始地址有多远
    OBJC_IVAR_$类名$属性名称
    // 方法对应的实现函数
    setter与getter
    // 成员变量列表
    ivar_list
    // 方法列表
    method_list
    // 属性列表
    prop_list
    
    • 每次增加一个属性,系统都会在ivar_list中添加一个成员变量的描述
    • 在method_list中增加setter与getter方法的描述
    • 在prop_list中增加一个属性的描述
    • 计算该属性在对象中的偏移量
    • 然后给出setter与getter方法对应的实现,在setter方法中从偏移量的位置开始赋值,在getter方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转
  1. @synthesize与@dynamic的区别
    • @property有两个对应的词,一个是@synthesize,一个是@dynamic。如果@synthesize和@dynamic都没写,那么默认的就是@syntheszie var = _var;
    • @synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法
    • @dynamic告诉编译器:属性的setter与getter方法由用户自己实现,不自动生成(当然对于readonly的属性只需提供getter即可)
      • 假如一个属性被声明为@dynamic var,然后你没有提供@setter方法和@getter方法,编译的时候没问题,但是当程序运行到instance.var = someVar,由于缺setter方法会导致程序崩溃;或者当运行到 someVar = instance.var时,由于缺getter方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定
2.什么情况使用weak关键字,相比assign有什么不同?
  • 首先明白什么情况使用weak关键字?
    1. 在ARC中,在有可能出现循环引用的时候,往往要通过让其中一端使用weak来解决,比如:delegate代理属性,代理属性也可使用assign
    2. 自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用weak,自定义IBOutlet控件属性一般也使用weak;当然,也可以使用strong,但是建议使用weak
  • weak 和 assign的不同点
    • weak策略在属性所指的对象遭到摧毁时,系统会将weak修饰的属性对象的指针指向nil,在OC给nil发消息是不会有什么问题的;如果使用assign策略在属性所指的对象遭到摧毁时,属性对象指针还指向原来的对象,由于对象已经被销毁,这时候就产生了野指针,如果这时候在给此对象发送消息,很容造成程序奔溃
    • assigin 可以用于修饰非OC对象,而weak必须用于OC对象
3.category和extension的区别?category中如何添加属性?
  • 区别:
    1. extension在编译的时候,他的数据就已经包含在类信息中,category是在运行时,才会将数据整合并到类信息中
    2. 分类有名字,类扩展没有分类名字,是一种特殊的分类
    3. 分类只能扩展方法(属性仅仅是声明,并没真正实现),类扩展可以扩展属性、成员变量和方法
  • category实现原理
    category编译之后的底层结构是struct category_t,里面存储着分类的对象方法,类方法,属性,协议信息
4.关键词__weak,__block,__strong分别有什么作用?
  • __block不管是ARC还是MRC模式下都可以使用,可以修饰对象,也可以修饰基本数据类型
  • __weak只能在ARC模式下使用,只能修饰对象(NSString),不能修饰基本数据类型
5.定时器的使用方法有哪些,更加精准的定时器应该怎么做?

NSTimer, CADisplayLink,dispatch_source_t
dispatch_source_t不依赖RunLoop,更加精准

6.Autoreleasepool什么时候释放,在什么场景下使用?
7.可变集合类和不可变集合类的copy和mutableCopy有什么区别?集合类型数据如何实现深层复制?如何让自己的类用copy修饰符?
  • 集合类对象的copy与mutableCopy
    [不可变对象 copy] // 浅复制
    [不可变对象 mutableCopy] //单层深复制
    [可变对象 copy] //单层深复制
    [可变对象 mutableCopy] //单层深复制
    
8.+load和+initialize的区别是什么?
  • +load方法:

    • +load方法会在runtime加载 类、分类时调用
    • 每个 类、分类的+load,在程序运行过程中只调用一次
    • 调用顺序:
      1. 先调用类的+load,
        • 按照编译先后顺序调用(先编译,先调用)
        • 调用子类的+load之前会先调用父类的+load
      2. 再调用分类的+load
        • 按照编译先后顺序调用(先编译,先调用)
  • +initialize方法

    • +initialize方法会在 第一次接受到消息时调用
      -调用顺序:
      1. 先调用父类的+initialize,再调用子类的+initialize
      2. (先初始化父类,再初始化子类,每个类只会初始化1次)
    • +load和+initialize的很大区别是,+initialize是通过objc_msgSend进行调用的,所以有以下特点:
      • 如果子类没有实现+initialize,会调用父类的+initialize(父类的+initialize可能会调用多次)
      • 如果分类实现了+initialize,就覆盖类本身的+initialize调用
  • 总结:

    • 在Objective-C中,runtime会自动调用每个类的这两个方法
    • +load会在类初始加载时调用
    • +initialize会在第一次调用类的类方法或实例方法之前被调用
    • 这两个方法是可选的,且只有在实现了它们时才会被调用
    • 两者的共同点:两个方法都只会被调用一次
9.列举GCD常用方法并说明其作用和应用场景
10.runloop和线程有什么关系?
  • 每条线程都有唯一的一个RunLoop对象与之对应的
  • RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
  • 线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建
  • RunLoop会在线程结束时销毁
  • 主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop
11.什么是method swizzling?

方法交换

12.Objective-C运行时(runtime)机制了解吗?简单的说说对象调用方法的过程。
  • runtime:
    • OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行
    • OC的动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数
    • 平时编写的OC代码,底层都是转换成了Runtime API进行调用
  • 调用方法的过程
    待补充
13.runtime如何实现weak变量的自动置nil ?
  • runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。

  • weak 修饰的指针默认值是 nil (在Objective-C中向nil发送消息是安全的)

14.在一个HTTPS连接的网站里,输入账号密码点击登录后,到服务器返回这个请求前,中间经历了什么?

待补充

你可能感兴趣的:(面试题总结)