关于ios:iOS今日头条第3轮面试回忆

3次阅读

共计 4467 个字符,预计需要花费 12 分钟才能阅读完成。

今日头条的 iOS 高级开发岗第三面,上面记录这次面试的回顾以作日后温习。

一、自我介绍

简略介绍一下你本人吧

  • 解析:简略介绍下本人的名字,教育背景,当初的工作,做过的我的项目

二、自我介绍衍生的口头问题

讲讲下你在你我的项目中做过的优化或者技术难点

  • 解析:介绍了本人封装的一个集 picker,文本域的灵便开展的表视图。这个视图的数据源是 json,怎么转成模型数组的?这个 cell 有哪些类型?展现的怎么辨别这些 cell?这外面有用过复用机制吗?这些 cell 有实现过多重继承吗?
  • 题外话:这种问题最好各人本人找问题讲讲,不多,提前准备一个你我的项目中十分善于并相熟的点,即可。

三、编程题:实现以下性能

1) 编写一个自定义类:Person,父类为 NSObject

  • 解析:头文件这样写 @interface Person:NSObject

2) 该类有两个属性,内部只读的属性name,还有一个属性age

  • 解析:name的修饰符 nonatomicstrongreadonlyage 的修饰符nonatomiccopy

3) 为该类编写一个初始化办法 initWithName:(NSString *)nameStr,并根据该办法参数初始化 name 属性。

  • 解析:头文件申明该办法,实现文件实现该办法

4) 如果两个 Person 类的 name 相等,则认为两个 Person 相等

  • 解析:重写isEqual,这外面波及到了哈希函数在 iOS 中的利用。

四、由编程题衍生的口头题目

4.1

题目: 怎么实现内部只读的属性,让它不被内部篡改

解析:

  • 头文件用 readonly 润饰并申明该属性。失常状况下,属性默认是 readwrite,可读写,如果咱们设置了只读属性,就表明不能应用 setter 办法。在.m 文件中不能应用self.ivar = @"aa"; 只能应用实例变量_ivar = @"aa";,而外界想要批改只读属性的值,须要用到 kvc 赋值[object setValue:@"mm" forKey:@"ivar"];
  • 实现文件外面申明公有属性,并在头文件在 protocol 外面规定该属性就能够了,内部通过 protocol 获取,这样还能够达到暗藏成员的成果。

4.2

题目: nonatomic 是非原子操作符,为什么要这样,atomic 为什么不行?有人说能 atomic 耗内存,你感觉呢?保读写平安吗,能保障线程平安吗?有的人说 atomic 并不能保障线程平安,你感觉他们的出发点是什么,你认同这个说法吗?

  • 对于为什么用 nonatomic

如果该对象无需思考多线程的状况,请退出这个属性润饰,这样会让编译器少生成一些互斥加锁代码,能够提高效率。

而 atomic 这个属性是为了保障程序在多线程状况下,编译器会主动生成一些互斥加锁代码,防止该变量的读写不同步问题。

atomic 和 nonatomic 的区别在于,零碎主动生成的 getter/setter 办法不一样。如果你本人写 getter/setter,那 atomic/nonatomic/retain/assign/copy 这些关键字只起提醒作用,写不写都一样。

  • 对于 atomic 语 nonatomic 的实现

苹果的官网文档 有解释,上面咱们举例子解释一下背地的原理。

  • 至于 nonatomic 的实现
//@property(nonatomic, retain) UITextField *userName;
// 系统生成的代码如下:- (UITextField *) userName {return userName;}

- (void) setUserName:(UITextField *)userName_ {[userName_ retain];
    [userName release];
    userName = userName_;
}
  • 而 atomic 版本的要简单一些:
//@property(retain) UITextField *userName;
// 系统生成的代码如下:- (UITextField *) userName {
    UITextField *retval = nil;
    @synchronized(self) {retval = [[userName retain] autorelease];
    }
    return retval;
}

- (void) setUserName:(UITextField *)userName_ {@synchronized(self) {[userName release];
      userName = [userName_ retain];
    }
}

简略来说,就是 atomic 会加一个锁来保障多线程的读写平安,并且援用计数会 +1,来向调用者保障这个对象会始终存在。如果不这样做,如有另一个线程调 setter,可能会呈现线程竞态,导致援用计数降到 0,原来那个对象就开释掉了。

  • 对于 atomic 和线程平安

atomic 润饰的属性只能说是 读 / 写平安 的,但并不是 线程平安 的,因为别的线程还能进行读写之外的其余操作。线程平安须要开发者本人来保障。

  • 对于修饰符生效

因为 atomic 润饰的属性靠编译器主动生成的 get 和 set 办法实现原子操作,如果重写了任意一个,atomic 关键字的个性将生效

4.3

题目: 你在初始化的办法中为什么将参数赋给_name,为什么这样写就能拜访到属性申明的示例变量?

  • xcode4 之后,编辑器增加了主动同步补全性能,只须要在 h 文件中定义 property,在编译期 m 文件会主动补全出 @synthesize name = _name 的代码,不再须要手写,防止了“膂力代码”的手动编码

4.4

题目: 初始化办法中的_name 是在什么时候生成的? 分配内存的时候吗?还是初始化的时候?

  • 成员变量存储在堆中(以后对象对应的堆得存储空间中),不会被零碎主动开释,只能有程序员手动开释。
  • 编译的时候主动的为 name 属性生成一个实例变量_name
  • 如果 m 中什么都不写,xcode 会默认在编译期为 market 属性,补全成 @synthesize market = _market,实例变量名为 _market;
  • 如果 m 中指定了 @synthesize market,xcode 会认为你手动指定了实例变量名为 market,编译期补全成:@synthesize market = market,实例变量名为 market。

4.5

题目: 作为 return 的 self 是在下面时候生成的?

  • 是在 alloc 时候分配内存,在 init 初始化的。
  • 一种典型带成员变量初始化参数的代码为:
- (instancetype)initWithDistance:(float)distance maskAlpha:(float)alpha scaleY:(float)scaleY direction:(CWDrawerTransitionDirection)direction backImage:(UIImage *)backImage {if (self = [super init]) {
        _distance = distance;
        _maskAlpha = alpha;
        _direction = direction;
        _backImage = backImage;
        _scaleY = scaleY;
    }
    return self;
}

4.6

题目: 为什么用 copy,哪些状况下用 copy,为什么用 copy?

  • 可变的类,例如 NSArray、NSDictionary、NSString 最好用 copy 来润饰,它们都有对应的 Mutable 类型。
  • copy 润饰属性的实质是为了专门设置属性的 setter 办法,例如,setName:传进一个 nameStr 参数,那么有了 copy 修饰词后,传给对应的成员变量_name 的其实是[nameStr copy];
  • 为什么要这样?如果不必 copy 会有什么问题?例如,strong润饰的 NSString 类型的 name 属性,传一个 NSMutableString:
NSMutableString *mutableString = [NSMutableString stringWithFormat:@"111"];
self.myString = mutableString;

strong 润饰下,把可变字符串 mutableString 赋值给 myString 后,扭转 mutableString 的值导致了 myString 值的扭转。而 copy 润饰下,却不会有这种变动。

strong 润饰下,可变字符串赋值给 myString 后,两个对象都指向了雷同的地址。而 copy 润饰下,myString 和 mutableString 指向了不同地址。这也是为什么 strong 润饰下,批改 mutableString 引起 myString 变动,而 copy 润饰下则不会。

  • 总之,当润饰可变类型的属性时,如 NSMutableArray、NSMutableDictionary、NSMutableString,用 strong。当润饰不可变类型的属性时,如 NSArray、NSDictionary、NSString,用 copy。

4.7

题目: 分类中增加实例变量和属性别离会产生什么,编译时就报错吗,还是什么时候会产生问题?为什么

  • 编译的时候,不能增加实例变量,否则报错。
  • 编译的时候能够增加属性,然而一旦在创建对象后为属性赋值或者应用这个属性的时候,程序就解体了,奔溃的起因也很简略,就是找不到属性的 set/get 办法。
  • 那咱们就依照这个流程来,在类别中为属性增加 set/get 办法,在 set 办法外面赋值的时候找不到赋值的对象,也就是说零碎没有为咱们生成带下划线的成员变量,没生成咱们就本人加。然而通过传统实例变量的形式,一加就报错。看来这才是类别不能扩大属性的根本原因。

  • 那么怎么办?通过 runtime 的关联对象。

五、另外聊到的理论开发问题

1) 你平时有做过优化内存的哪些工作?怎么防止内存耗费的小户?

  • 能够参考这个 https://www.2cto.com/kf/201505/401059.html
  • 对于 TableView 的优化能够参考 https://www.jianshu.com/p/9cd9382c0a5b

2) 你怎么实现线程平安的?这些线程平安的方法和 atomic 有什么不一样?atomic 的实现机制是怎么

  • 能够参考 YYKit 的多线程平安机制,它是用 MUTEX 实现线程锁的 https://github.com/ibireme/YYKit
  • 对于锁的实现原理可参考 https://www.jianshu.com/p/a33959324cc7
  • 其它方法,例如队列
  • 对于 atomic 的实现机制后面有探讨,就是加锁。
  • 如果不加 atomic 会怎么样呢?当内存长度大于地址总线的时候,例如在 64 位零碎下内存中读取无奈像 bool 等纯量类型原子性实现,可能会在读取的时候产生写入,从造成异常情况。atomic 还会应用 memory barrier 可能保障内存操作的程序,依照咱们代码的书写程序来。

材料举荐

如果你正在跳槽或者正筹备跳槽无妨动动小手,增加一下咱们的交换群 1012951431 来获取一份具体的大厂面试材料为你的跳槽多添一份保障。

正文完
 0