iOS有用的面试题大集合

面试题从何处得来

阅读面试题之前

在正式开始之前,我期望你能对iOS/Mac OS X平台开发有所了解,在iOS开发中已经很少需要自己写复杂的算法了,一般情况下很少会在面试中出现算法的考核,如果你了解一些基础的算法,还是有帮助的。

Now!!请使用ARC

什么是iOS开发

iOS是iPhone iPad等手持设备的操作系统,所谓的iOS开发就是开发运行在iOS系统上的应用或者游戏,比如支付宝,微信,微博等,当然这也包括了iPad版的应用,iOS开发可以归纳到移动开发领域。

有时候面试官是那种’脑残粉’,了解一下Apple的发展历史,可能比较聊的开。

苹果Mac计算机31年发展历程回顾

苹果公司

苹果公司在知乎上的话题

乔布斯个人传记

拼写正确的重要性

有些面试官可能更注重细节,所以,拼写的单词一定要对,比如iOS,Xcode,iPhone,Objective-C,JSON等,良好的拼写习惯,会让面试官觉得你细心靠谱。

Swift和Objective-C的比较

仁者见仁智者见智,从个人的使用角度上来看,Swift在某些情况上比Objective-C更加的严谨了,入门非常简单,但是想开发应用,还是需要学习cocoa框架,这玩意路子还是Objective-C的,所以有基础可能更好的理解Swift在iOS/Mac OS X 中的开发和应用。

知乎原文

了解Watch OS

Watch OS是苹果公司推出的应用在手表上的一个操作系统,Watch OS 1.0需要跟iPhone相结合才能工作。

Apple Watch

Watch OS 2.0 开发概述


iOS面试

property 后面可以有哪些修饰符
  1. 读写修饰符 readwrite | readonly

    readwrite Xcode会帮助我们创建settergetter方法,readonly Xcode只会帮助我们创建getter方法,不会创建setter方法。

  2. setter相关的修饰符 assign | retain | copy

    2.1 setter相关的修饰符表明了setter方法该如何实现,assign用于基本数据类型NSIntegerCGFloat,C数据类型intfloatid类型等,这个符号不会涉及内存管理,但是如果是对象类使用了它,可能会导致内存泄漏或者EXC_BAD_ACCESS错误。

    2.2 retain用于对象类的内存管理,如果基本数据类型使用它,Xcode会直接报错。当对象类使用此修饰符时,setter方法的实现是先release一次,然后再对新的对象做一次retain操作。

    2.3 copy主要用于NSString,用于内容复制。

  3. 原子性修饰符 atomic | nonatomic

    atomic 表示线程安全

    nonatomic 表示非线程安全,使用此修饰符会提高性能

  4. gettersetter修饰符

    这两个修饰符用于设置生成的getter,setter的方法名

  5. strongweak修饰符(ARC) 在ARC中内存管理都只需要使用这两个修饰符,而且strong是默认全局的,只要你写了Objective-C的对象,不自己添加weak的话,默认就是strong。 5.1 strong表示这个对象的拥有者 一个对象可以有多个拥有者,strong就是用来表示对这个对象的拥有。比如在往NSMutableArray中添加Objective-C对象,当你从数组中删除时,这个对象并不会释放。需要你手动设置为nil,或者在控制器的生命周期内,由系统来释放。 5.2 weak指针变量仍然可以指向一个对象,但不是这个对象的拥有者 weak修饰的指针变量也可以指向对象,但不是这个对象的实际拥有者,也就是说weak修饰的指针变量如果想要释放,需要strong修饰的指针变量设置为nilweak修饰的指针变量也会是一个nil,它指向的对象已经没有了,还需要设置weak修饰的指针变量为nil

  6. nonnull nullable null_resettable

Xcode 6.3推出的nullability annotations,主要是为了更好的Swift与Objective-C混编,在Swift中有可选型的概念!,?,但是Objective-C中木有这玩意,于是Xcode 6.3中才有了这个, 从字面可以看出: nonnull 表示对象不应该为空,如果是这个修饰符对应的就是Swift中已经解包的对象或者! nullable表示可以为nil或者NULL,对应是Swift中的可选? null_resettable则是表达属性的空属性,该属性setter访问器允许将其设置为nil(设置该属性为默认值),但是它的getter访问器不会提供一个nil值(因为它提供了默认值),有一个这样的属性如UIView’s tintColor,如果没有tint颜色指定时它会提供一个默认的tint颜色值,对应的Swift使用是var tintColor:UIColor!

实战

  1. 使用 weak 关键字,相比 assign 有什么不同 一般情况下使用weak是避免循环引用,因为它不是对象的拥有者。而assign则是用于基本数据类型,或者C类型,而且assign是直接赋值,可能会导致一个问题。比如我想a和b共用一块内存,a是用assign修饰的,a = b,现在a使用的目的已经完成,我想释放这个内存,但是a并不知道b到底用没用完,如果此时a释放内存,而b还在使用,那么会导致应用程序crash,使用weak就能避免这样的问题。
  2. 怎么用 copy 关键字 copy拷贝的是内容,retain是拷贝的指针 * 以string为例,如果string的属性为copy的话,那么传入参数为NSString的话,即为不可变string,retain,copy效果一样. * 如果传入参数是mutable的话,那么copy拷贝内容,源随意变化不影响该属性的值.retain拷贝指针,源变化则属性值着变化,因为属性和源指向如何使用呢,通常在需要拷贝内容,但是副本和源不要互相影响的情况下使用.* 同一内存地址. * 例如array/dictionary中,可能会需要一个副本来做一些操作(筛选,排序等),但是并不希望影响原始值,则可以使用copy
  3. @property (copy) NSMutableArray *array; 这样写有什么问题吗 因为用了copy, 内部会深拷贝一次, 指针实际指向的是NSArray, 所以如果调用removeObjectaddObject方法的话, 会unRecognized selector
  4. 如何让自己的类用 copy 修饰符?如何重写带 copy 关键字的 setter? 当一个对象发生改变时不影响另外一个对象,这里就需要使用copy关键字了,实现NSCopying协议,重写- (id)copyWithZone:(NSZone *)zone方法。 {% codeblock lang:objc %}
  • (void)setName:(NSString *)name { if(_name != name) { _name = [name copy]; } } {% endcodeblock %}
  1. @protocol 和 category 中如何使用 @property @protocol可以通过关键字:@synthesize或者在继承的类里面重新定义一次该属性(extension里面定义是不行的) category通过关联:objc_setAssociatedObject/objc_getAssociatedObject
  2. @property 的本质是什么?ivargettersetter 是如何生成并添加到这个类中的 @property本质是定义一个objc_property结构体 如何生成目前不清楚
  3. weak属性需要在dealloc中置nil么 不需要,因为weak会自动设置nil
  4. @synthesize和@dynamic分别有什么作用 关于@synthesize(现在已经不需要在写这个属性了,它是用来生成getter和setter方法) @dynamic 就是要告诉编译器gettersetter方法会在程序运行或者用到动态绑定的方式,以便让编译器通过编译,这个主要要在NSManagerObject上。
  5. ARC下,不显式指定任何属性关键字时,默认的关键字都有哪些 在默认情况下,所有的实例变量和局部变量都是strong类型的。
  6. @property声明的NSString(或NSArrayNSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题 因为不想改变了其中的值后把原来的值也跟着改变了,用了strong后会出现这样的状况。
  7. 什么是ARC 请阅读,然后随便谈谈你的理解即可。 ARC是为了解决下面几个问题
    • 当我们要释放一个堆内存时,首先要确定指向这个堆空间的指针都被release了。(避免提前释放)
    • 释放指针指向的堆空间,首先要确定哪些指针指向同一个堆,这些指针只能释放一次。(MRC下即谁创建,谁释放,避免重复释放)
    • 模块化操作时,对象可能被多个模块创建和使用,不能确定最后由谁去释放。
    • 多线程操作时,不确定哪个线程最后使用完毕 手把手教你ARC——iOS/Mac开发ARC入门和使用 理解 Objective-C 的 ARC
  8. 请解释以下keywords的区别: assign vs weak, block vs weak assign适用于基本数据类型,weak是适用于NSObject对象,并且是一个弱引用。
    • assign其实也可以用来修饰对象,那么我们为什么不用它呢? 因为被assign修饰的对象在释放之后,指针的地址还是存在的,也就是说指针并没有被置为nil。如果在后续的内存分配中,刚好分到了这块地址,程序就会崩溃掉。
    • weak修饰的对象在释放之后,指针地址会被置为nil。所以现在一般弱引用就是用weak
    • block是用来修饰一个变量,这个变量就可以在block中被修改,使用block修饰的变量在block代码快中会被retainARC下,MRC下不会retain
    • weak:使用weak修饰的变量不会在block代码块中被retain同时,在ARC下,要避免block出现循环引用 weak typedof(self)weakSelf = self
  9. __blockarc非arc下含义一样吗 是不一样的,ARC会retain,非ARC不会。
  10. 描述一个你遇到过的retain cycle例子 在viewController中避免循环引用 {% codeblock lang:objc %} [ downloadData:^(id responseData){ _data = responseData; }]; {% endcodeblock %}
    解决办法 {% codeblock lang:objc %} __weak ViewController *weakSelf = self; [ downloadData:^(id responseData){ weakSelf.data = responseData; }]; {% endcodeblock %}
  11. +(void)load; +(void)initialize;有什么用处 在Objective-C中,runtime会自动调用每个类的两个方法。+load会在类初始加载时调用,+initialize会在第一次调用类的类方法或实例方法之前被调用。这两个方法是可选的,且只有在实现了它们时才会被调用。 共同点:两个方法都只会被调用一次。
  12. UIViewCALayer有什么关系
    • UIView是iOS界面元素的基础,所有的界面元素都继承于它。它本身是由CoreAnimation来实现的,它真正绘图的部分是由一个CALayer的类来管理的,UIView本身更像是一个CALayer的管理器。
    • UIView都存在一个layer属性,可以访问到CALayer的实例。
    • UIViewCALayer类也存在一个view树结构,可以像UIView一样进行添加
    • UIViewlayer树在系统内部,由系统来维护,它存在着三棵树,分别是逻辑树,动画树,显示树
  13. 如何高性能的给UIImageView加个圆角
    • 使用贝塞尔曲线来切割图片
    • 使用Quartz2D直接绘制图片
  14. 使用drawRect有什么影响 drawRect方法依赖Core Graphics框架来进行自定义的绘制,但这种方法主要的缺点就是它处理touch事件的方式:每次按钮被点击后,都会用setNeddsDisplay进行强制重绘;而且不止一次,每次单点事件触发两次执行。这样的话从性能的角度来说,对CPU和内存来说都是欠佳的。
  15. SDWebImage里面给UIImageView加载图片的逻辑是什么样的 详情看最新版SDWebImage的使用
  16. 麻烦你设计个简单的图片内存缓存器 图片的内存缓存,可以考虑将图片数据保存到一个数据模型中,所以在程序运行时这个模型都存在内存中,一定要具备移除策略,即释放数据模型。
  17. 讲讲你用Instrument优化动画性能的经历 怎么使用instrument
  18. loadView是干嘛用的 当你访问一个ViewControllerview属性时,如果此时view的值是nil,那么,ViewController就会自动调用loadView这个方法。这个方法就会加载或者创建一个view对象,赋值给view属性。 loadView默认做的事情是:如果此ViewController存在一个对应的nib文件,那么就加载这个nib。否则,就创建一个UIView对象。 如果你用Interface Builder来创建界面,那么不应该重载这个方法。 如果你想自己创建view对象,那么可以重载这个方法。此时你需要自己给view属性赋值。你自定义的方法不应该调用super。如果你需要对view做一些其他的定制操作,在viewDidLoad里面去做。 iOS 的loadView 及使用loadView中初始化View注意的问题
  19. 用过CoreData或者SQLite吗?读写是分线程的吗?遇到过死锁没?咋解决的 参考CoreData与SQLite的线程安全
  20. GCD里面有哪几种Queue?你自己建立过串行queue吗?背后的线程模型是什么样的
    • 主队列 dispatch_main_queue(); 串行 ,更新UI
    • 全局队列 dispatch_global_queue(); 并行,四个优先级:backgroundlowdefaulthigh
    • 自定义队列 dispatch_queue_t queue; 可以自定义是并行:DISPATCH_QUEUE_CONCURRENT或者串行DISPATCH_QUEUE_SERIAL
  21. 为什么其他语言里叫函数调用, Objective-C里则是给对象发消息(或者谈下对runtime的理解) 网上关于runtime的资料非常多,其实这方面在平时的开发中使用非常非常之少,底层的黑魔法。 Objective-C特性:Runtime Objective-C Runtime
  22. 什么是method swizzling 在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的。 详细的案例
  23. runtime 如何实现 weak 属性 {% codeblock lang:objc %} OBJC_ASSOCIATION_ASSIGN OBJC_ASSOCIATION_RETAIN_NONATOMIC OBJC_ASSOCIATION_COPY_NONATOMIC OBJC_ASSOCIATION_RETAIN OBJC_ASSOCIATION_COPY objc_setAssociatedObject(self, &myKey, anObject, OBJC_ASSOCIATION_RETAIN); {% endcodeblock %} 可以自定义weak来实现内存管理,Apple已经为我们准备了常量。 参考 Associated Objects Objective-C Runtime 运行时之二:成员变量与属性
  24. objc中向一个nil对象发送消息将会发生什么 objc的特性是允许对一个 nil 对象发送消息不会 Crash,因为会被忽略掉。
  25. 什么时候会报unrecognized selector的异常 调用一个不存在的方法
  26. objc中向一个对象发送消息[obj foo]objc_msgSend()函数之间有什么关系 {% codeblock lang:objc %} [obj foo]; //编译时会变成 objc_msgSend(obj,@selector(foo));

[obj foo:parameter]; //编译时会变成 objc_msgSend(obj,@selector(foo:),parameter); {% endcodeblock %} 31. 一个objc对象如何进行内存布局 可参考Objective-C内存布局 32. 一个objc对象的isa的指针指向什么?有什么作用? isa是一个Class 类型的指针. 每个实例对象有个isa的指针,他指向对象的类,而Class里也有个isa的指针, 指向meteClass(元类)。元类保存了类方法的列表。当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass).根元类的isa指针指向本身,这样形成了一个封闭的内循环。 33. 下面的代码输出什么 {% codeblock lang:objc %} @implementation Son : Father

  • (id)init { self = [super init]; if (self) { NSLog(@"%@", NSStringFromClass([self class])); NSLog(@"%@", NSStringFromClass([super class])); } return self; } @end {% endcodeblock %} 输出Son
  1. runtime如何通过selector找到对应的IMP地址 id (*IMP)(id, SEL, …) 这个函数使用当前CPU架构实现的标准的C调用约定。第一个参数是指向self的指针(如果是实例方法,则是类实例的内存地址;如果是类方法,则是指向元类的指针),第二个参数是方法选择器(selector),接下来是方法的实际参数列表。 前面介绍过的SEL就是为了查找方法的最终实现IMP的。由于每个方法对应唯一的SEL,因此我们可以通过SEL方便快速准确地获得它所对应的IMP,查找过程将在下面讨论。取得IMP后,我们就获得了执行这个方法代码的入口点,此时,我们就可以像调用普通的C语言函数一样来使用这个函数指针了。 通过取得IMP,我们可以跳过Runtime的消息传递机制,直接执行IMP指向的函数实现,这样省去了Runtime消息传递过程中所做的一系列查找操作,会比直接向对象发送消息高效一些。

Hybrid 混合开发