2018年五月份面试整理

2018-05-18  本文已影响49人  走着走着就会敲代码了

先扯点废话,五月份面试的不多,就面试一周哈,差不多面试了五六家的样子,拿了两个offer(其中一个是技术过了,因为模式的问题本人直接拒接了),当然也拿到一个满意的offer了。面试题整理一些是自己碰到的,一些是百度上的,个人觉得可以算上是复习一下,也顺便整理进来了。

1、OC的面向对象

面向对象:封装、继承、多态。
ps:被问懵过(第一个想法就是OC中不都是面向对象开发吗?对象不就是创建一个嘛...特么有毒),想想就觉得没脸见人...初学者都会的东西,做了几年开发后竟然回答不上来。

什么是多态

一个方法,不同的实现,主要体现:重写以及方法重载,OC中没有严格的方法重载。

2、block和 weak修饰符的区别?

__block不管是 ARC 还是MRC模式下都可以使用,可以修饰对象,也可以修饰基本数据类型;
__weak只能在 ARC 模式下使用,只能修饰对象(NSString),不能修饰基本数据类型;
__block修饰的对象可以在block中被重新赋值,中被重新赋值,__weak修饰的对象不可以。
归结
__weak只能修饰对象,在block中不可被修改,可用于解决block的循环引用
__block既可以修饰对象也可以修饰基本数据类型,主要用于修饰在block外部创建的变量,可以在block里面进行修改变量

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

weak的使用情况

4、category 和 extension 的区别
5、define 和 const常量有什么区别?
6、static关键字的作用
7、Objective-C使用什么机制管理对象内存?
8、ARC下还会存在内存泄露吗?

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

9、常用什么设计模式,如何使用?

常用几种设计模式比如:工厂模式、单例模式、观察者模式等等。

10、KVO原理

如何手动触发一个 value的 KVO
自动触发的场景:在注册 KVO 之前设置一个初始值,注册之后,设置一个不一样的值,就可以触发了。
想知道如何手动触发,必须知道自动触发 KVO 的原理,见上面的描述手动触发演示。

@property (nonatomic, strong) NSDate *now;

- (void)viewDidLoad
{
[super viewDidLoad];

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

// “手动触发 self.now的 KVO”,必写。
[self didChangeValueForKey:@"now"];
}
11、UIView和CALayer是什么关系?

UIView显示在屏幕上归功于CALayer,通过调用drawRect方法来渲染自身的内容,调节CALayer属性可以调整UIView的外观,UIView继承自UlResponder,比起CALayer可以响应用户事件,Xcode 6之后可以方便的通过视图调试功能查看图层之间的关系。
UIView是iOS系统中界面元素的基础,所有的界面元素都继承自它。它内部是由Core Animation来实现的,它真正的绘图部分是由一个叫CALayer(Core AnimationLayer)的类来管理。UlView 本身更像是一个CALayer的管理器,访问它的绘图和坐标有关的属性,如frame, bounds 等,实际上内部都是访问它所在CALayer的相关属性。
UIView有个layer属性,可以返回它的主CALayer实例, UlView有一个layerClass方法,返回layer所有使用的类,UIView的子类,可以通过重载这个方法,来让UIView使用不同的CALayer来显示。

12、OC的反射机制
Class class = NSClassFromString(@"student");
Student *stu = [ [class alloc] init];
SEL selector = NSSelectorFromString(@"setName");
[stu performSelector:selector withObject:@"Mike"];
// 将方法变成字符串
NSStringFromSelector(@selector*(setName:));
13、KVC和 KVO的 keyPath一定是属性么?

不一定,可以是成员变量。

14、@synthesize 和 @dynamic分别有什么作用?

@property 有两个对应的词,一个是 @synthesize ,一个是@dynamic。如果 @synthesize 和@dynamic 都没写,那么默认的就是@syntheszie var = _var;

15、@synthesize合成实例变量的规则是什么?假如 property名为 foo,存在一个名为_foo的实例变量,那么还会自动合成新变量么?

先回答第二个问题:不会
@synthesize 合成成员变量的规则,有以下几点:
如果指定了成员变量的名称,会生成一个指定的名称的成员变量
如果这个成员已经存在了就不再生成了
如果指定@synthesize foo;就会生成一个名称为 foo 的成员变量,也就是说:会自动生成一个属性同名的成员变量 @interfaceXMGPerson:NSObject
@property (nonatomic, assign) int age;
@end
@implementation XMGPerson
// 不加这语句默认生成的成员变量名为_age
// 如果加上这一句就会生成一个跟属性名同名的成员变量
@synthesize age;
@end
如果是 @synthesize foo = _foo; 就不会生成成员变量了。

16、在有了自动合成属性实例变量之后 ,@synthesize还有哪些使用场景?

不会 autosynthesis(自动合成):

应用场景:
当你同时重写了 setter 和 getter 时,系统就不会生成 ivar)。这时候有两种选择:
1.手动创建 ivar
2.使用@synthesize foo = _foo;,关联@property 与 ivar
可以用来修改成员变量名,一般不建议这么做,建议使用系统自动生成的成员变量

17、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 方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转。

18、怎么用 copy 关键字?

NSString、NSArray、NSDictionary 等等经常使用 copy 关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,为确保对象中的属性值不会无意间变动,应该在设置新属性值时拷贝一份,保护其封装性
block 也经常使用 copy 关键字
block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的block 是在栈区的,使用 copy 可以把它放到堆区.
在 ARC 中写不写都行:对于 block 使用 copy 还是 strong 效果是一样的,但是建议写上 copy,因为这样显示告知调用者“编译器会自动对 block 进行了 copy 操作”

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

因为父类指针可以指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本.如果我们使用是 strong,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性.
复制详解:

非集合类对象的 copy 与 mutableCopy

集合类对象的 copy 与 mutableCopy

这里需要注意的是集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制。

20、如何让自定义类可以用 copy 修饰符?如何重写带 copy 关键字的 setter?

若想令自己所写的对象具有拷贝功能,则需实现 NSCopying 协议。如果自定义的对象分为可变版本与不可变版本 , 那么就要同时实现 NSCopyiog 与NSMutableCopying 协议,不过一般没什么必要,实现 NSCopying 协议就够了。

// 实现不可变版本拷贝
- (id)copyWithZone:(NSZone *)zone;
// 实现可变版本拷贝
- (id)mutableCopyWithZone:(NSZone *)zone;
// 重写带 copy 关键字的 setter
- (void)setName:(NSString *)name
{
    _name = [name copy];
}
21、@property后面可以有哪些修饰符?
22、使用 atomic一定是线程安全的吗?

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

23、@protocol 和 category 中如何使用

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

PS:这边可以去了解一下runtime

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

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

两者的共同点:两个方法都只会被调用一次。

25、id 与instancetype
26、Foundation对象与 Core Foundation对象有什么区别

Foundation 框架是使用 OC 实现的,Core Foundation 是使用 C 实现的。
Foundation 对象 和 Core Foundation 对象间的转换:俗称桥接
ARC 环境桥接关键字:

    // 可用于 Foundation对象 和 Core Foundation对象间的转换
    __bridge

    // 用于Foundation对象 转成 Core Foundation对象
    __bridge_retained

    // Core Foundation对象 转成 Foundation对象
    __bridge_transfer
__bridge

如果使用__bridge桥接,它仅仅是将 strOC 的地址给了 strC, 并没有转移对象的所有权,也就是说, 如果使用__bridge 桥接, 那么如果 strOC 释放了,strC 也不能用了。

NSString *strOC1 = [NSString stringWithFormat:@"abcdefg"];
CFStringRef strC1 = (__bridge CFStringRef)strOC1;
NSLog(@"%@ %@", strOC1, strC1);
__bridge_retained

如果使用__bridge_retained 桥接,它会将对象的所有权转移给 strC, 也就是说, 即便 strOC被释放了, strC也可以使用。
注意:在 ARC 条件下,如果是使用__bridge_retained 桥接,那么 strC 必须自己手动释放,因为桥接的时候已经将对象的所有权转移给了 strC,而 C 语言的东西不是不归ARC 管理的 。

NSString*strOC2=[NSStringstringWithFormat:@"abcdefg"];
//  CFStringRef strC2   =   (__bridge_retained
CFStringRef)strOC2;
CFStringRef strC2 = CFBridgingRetain(strOC2);// 这一句, 就
等同于上一句
CFRelease(strC2);
__bridge_transfer

如果使用__bridge_transfer 桥接,它会将对象的所有权转移给 strOC, 也就是说, 即便 strC被释放了, strOC也可以使用 。
如果使用__bridge_transfer 桥接, 他会自动释放 strC, 也就是以后我们不用手动释放 strC。

CFStringRef  strC4  =
CFStringCreateWithCString(CFAllocatorGetDefault(),
"12345678", kCFStringEncodingASCII);
//  NSString *strOC = (__bridge_transfer NSString
*)strC;
NSString *strOC4 = CFBridgingRelease(strC4); // 这一句, 就
等同于上一句
27、runloop和线程有什么关系?

runloop,正如其名,loop表示循环,和run放在一起就表示一直在运行着的循环。实际上,runloop和线程是紧密相连的,可以这么说runloop是为了线程而生,没有线程,runloop就没有存在的必要。runloop是线程的基础架构部分,Cocoa和CoreFundation都提供了runloop对象方便配置和管理线程的runloop。
具体的可以参考这篇博客

28、runloop的mode作用

model主要是用来指定事件在runloop中的优先级:

29、多线程,线程锁,队列

总的就先这些吧,东西也算是不少了,如果有说的不清楚的地方可以自行百度,也可以留言哈。
当然,也可以参考大佬的总结:
2018 iOS面试题系列
iOS面试题09- 基础篇
.
.
.

上一篇 下一篇

猜你喜欢

热点阅读