美文网首页面试题
iOS面试题-基础篇

iOS面试题-基础篇

作者: Peter杰 | 来源:发表于2019-11-20 13:51 被阅读0次

category 和 extension 的区别

  • 分类有名字,类扩展没有分类名字,是一种特殊的分类
  • 分类只能扩展方法(属性仅仅是声明,并没真正实现),类扩展可以扩展属性、成员变量和方法

define 和 const常量有什么区别?

  • define在预处理阶段进行替换,const常量在编译阶段使用
  • 宏不做类型检查,仅仅进行替换,const常量有数据类型,会执行类型检查
  • define不能调试,const常量可以调试
  • define定义的常量在替换后运行过程中会不断地占用内存,而const定义的常量存储在数据段只有一份copy,效率更高
  • define可以定义一些简单的函数,const不可以

static关键字的作用

  • 函数(方法)体内 static 变量的作用范围为该函数体,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
  • 在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
  • 在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明 它的模块内;
  • 在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
  • 在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量

+(void)load; +(void)initialize;有什么用处?

  • +(void)load;
    • 当类对象被引入项目时, runtime 会向每一个类对象发送 load 消息
    • load 方法会在每一个类甚至分类被引入时仅调用一次,调用的顺序:父类优先于子类, 子类优先于分类
    • 由于 load 方法会在类被import 时调用一次,而这时往往是改变类的行为的最佳时机,在这里可以使用例如method swizlling 来修改原有的方法
    • load 方法不会被类自动继承
  • +(void)initialize;
    • 也是在第一次使用这个类的时候会调用这个方法,也就是说 initialize也是懒加载
  • 总结:
    • 在Objective-C中,runtime会自动调用每个类的这两个方法
    • +load会在类初始加载时调用
    • +initialize会在第一次调用类的类方法或实例方法之前被调用
    • 这两个方法是可选的,且只有在实现了它们时才会被调用
    • 两者的共同点:两个方法都只会被调用一次

Objective-C使用什么机制管理对象内存?

  1. MRC 手动引用计数
  2. ARC 自动引用计数,现在通常ARC
  3. 通过 retainCount 的机制来决定对象是否需要释放。 每次 runloop 的时候,都会检查对象的 retainCount,如果retainCount 为 0,说明该对象没有地方需要继续使用了,可以释放掉了

ARC下还会存在内存泄露吗?

  1. 循环引用会导致内存泄露
  2. Objective-C对象与CoreFoundation对象进行桥接的时候如果管理不当也会造成内存泄露
  3. CoreFoundation中的对象不受ARC管理,需要开发者手动释放

ARC通过什么方式帮助开发者管理内存?

  • 通过编译器在编译的时候,插入类似内存管理的代码

ARC是为了解决什么问题诞生的?

  • 首先解释ARC: automatic reference counting自动引用计数
  • 了解MRC的缺点
    1. 在MRC时代当我们要释放一个堆内存时,首先要确定指向这个堆空间的指针都被release了
    2. 释放指针指向的堆空间,首先要确定哪些指针指向同一个堆,这些指针只能释放一次(MRC下即谁创建,谁释放,避免重复释放)
    3. 模块化操作时,对象可能被多个模块创建和使用,不能确定最后由谁去释放
    4. 多线程操作时,不确定哪个线程最后使用完毕
  • 综上所述,MRC有诸多缺点,很容易造成内存泄露和坏内存的问题,这时苹果为尽量解决这个问题,从而诞生了ARC

ARC下还会存在内存泄露吗?

  • 循环引用会导致内存泄露
  • Objective-C对象与CoreFoundation对象进行桥接的时候如果管理不当也会造成内存泄露
  • CoreFoundation中的对象不受ARC管理,需要开发者手动释放

什么情况使用weak关键字,相比assign有什么不同?

  • 首先明白什么情况使用weak关键字?
    • 在ARC中,在有可能出现循环引用的时候,往往要通过让其中一端使用weak来解决,比如:delegate代理属性,代理属性也可使用assign
    • 自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用weak,自定义IBOutlet控件属性一般也使用weak;当然,也可以使用strong,但是建议使用weak
  • weak 和 assign的不同点
    • weak策略在属性所指的对象遭到摧毁时,系统会将weak修饰的属性对象的指针指向nil,在OC给nil发消息是不会有什么问题的;如果使用assign策略在属性所指的对象遭到摧毁时,属性对象指针还指向原来的对象,由于对象已经被销毁,这时候就产生了野指针,如果这时候在给此对象发送消息,很容造成程序奔溃
    • assigin 可以用于修饰非OC对象,而weak必须用于OC对象

@property 的本质是什么?

  • @property其实就是在编译阶段由编译器自动帮我们生成ivar成员变量,getter方法,setter方法

ivar、getter、setter是如何生成并添加到这个类中的?

  • 使用“自动合成”( autosynthesis)
  • 这个过程由编译器在编译阶段执行自动合成,所以编辑器里看不到这些“合成方法”(synthesized method)的源代码
  • 除了生成getter、setter方法之外,编译器还要自动向类中添加成员变量(在属性名前面加下划线,以此作为实例变量的名字)
  • 为了搞清属性是怎么实现的,反编译相关的代码,他大致生成了五个东西
  • 每次增加一个属性,系统都会在ivar_list中添加一个成员变量的描述
    • 在method_list中增加setter与getter方法的描述
    • 在prop_list中增加一个属性的描述
    • 计算该属性在对象中的偏移量
    • 然后给出setter与getter方法对应的实现,在setter方法中从偏移量的位置开始赋值,在getter方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转
  // 该属性的“偏移量” (offset),这个偏移量是“硬编码” (hardcode),表示该变量距离存放对象的内存区域的起始地址有多远
  OBJC_IVAR_$类名$属性名称

  // 方法对应的实现函数
  setter与getter

  // 成员变量列表
  ivar_list

  // 方法列表
  method_list

  // 属性列表
  prop_list

@protocol 和 category 中如何使用 @property

  • 在protocol中使用property只会生成setter和getter方法声明,我们使用属性的目的,是希望遵守我协议的对象能实现该属性
  • category 使用 @property也是只会生成setter和getter方法声明,如果我们真的需要给category增加属性的实现,需要借助于运行时的两个函数
objc_setAssociatedObject
objc_getAssociatedObject

@property后面可以有哪些修饰符?

  • 原子性---nonatomic特质
    • 如果不写默认情况为atomic(系统会自动加上同步锁,影响性能)
    • 在iOS开发中尽量指定为nonatomic,这样有助于提高程序的性能
  • 读/写权限---readwrite(读写)、readooly (只读)
  • 内存管理语义---assign、strong、 weak、unsafe_unretained、copy
  • 方法名---getter=、setter=
@property (nonatomic, getter=isOn) BOOL on;

// setter=<name>这种不常用,也**不推荐**使用。故不在这里给出写法
  • 不常用的:nonnull,null_resettable,nullable

使用atomic一定是线程安全的吗?

  • 不是,atomic的本意是指属性的存取方法是线程安全的,并不保证整个对象是线程安全的。
  • 举例:声明一个NSMutableArray的原子属性stuff,此时self.stuff 和self.stuff = othersulf都是线程安全的。但是,使用[self.stuff objectAtIndex:index]就不是线程安全的,需要用互斥锁来保证线程安全性

@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方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定

ARC下,不显式指定任何属性关键字时,默认的关键字都有哪些?

  • 基本数据:atomic,readwrite,assign
  • 普通的OC对象:atomic,readwrite,strong

用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?

  • 因为父类指针可以指向子类对象,使用copy的目的是为了让本对象的属性不受外界影响,使用copy无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本.
  • 如果我们使用是strong,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性.
复制详解
  • 浅复制(shallow copy):在浅复制操作时,对于被复制对象的每一层都是指针复制。
  • 深复制(one-level-deep copy):在深复制操作时,对于被复制对象,至少有一层是深复制。
  • 完全复制(real-deep copy):在完全复制操作时,对于被复制对象的每一层都是对象复制。
  • 非集合类对象的copy与mutableCopy
[不可变对象 copy] // 浅复制
[不可变对象 mutableCopy] //深复制
[可变对象 copy] //深复制
[可变对象 mutableCopy] //深复制
  • 集合类对象的copy与mutableCopy
[不可变对象 copy] // 浅复制
[不可变对象 mutableCopy] //单层深复制
[可变对象 copy] //单层深复制
[可变对象 mutableCopy] //单层深复制
  • 这里需要注意的是集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制
copy mutableCopy
\color{RED}{NSString} NSString
浅拷贝
NSMutableString
深拷贝
\color{RED}{NSMutableString} NSString
深拷贝
NSMutableString
深拷贝
\color{RED}{NSArray} NSArray
浅拷贝
NSMutableArray
深拷贝
\color{RED}{NSMutableArray} NSArray
深拷贝
NSMutableArray
深拷贝
\color{RED}{NSDictionary} NSDictionary
浅拷贝
NSMutableDictionary
深拷贝
\color{RED}{NSMutableDictionary} NSDictionary
深拷贝
NSMutableDictionary
深拷贝

KVO内部实现原理?

  • 利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类
  • 当修改instance对象的属性时,会调用Foundation的_NSSetXXXValueAndNotify函数
    1. willChangeValueForKey:
    2. 父类原来的setter
    3. didChangeValueForKey:
    4. 内部会触发监听器(Oberser)的监听方法( observeValueForKeyPath:ofObject:change:context:)

如何手动触发KVO?

手动调用willChangeValueForKey:和didChangeValueForKey:

@property (nonatomic, strong) NSDate *now;

- (void)viewDidLoad
{
    [super viewDidLoad];

    // “手动触发self.now的KVO”,必写。
    [self willChangeValueForKey:@"now"];

    // “手动触发self.now的KVO”,必写。
    [self didChangeValueForKey:@"now"];
}

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

  • 不会触发KVO

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

  • setValue:forKey:的原理


  • valueForKey:的原理


相关文章

网友评论

    本文标题:iOS面试题-基础篇

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