1九、iOS面试题·自整理·Three

1.请简述你对工厂方法的理解?mysql

工厂Mycontrol,设计控件是用到工厂设计模式。类簇相似于工厂设计模式;工厂模式就是定义建立对象的接口,让子类决定实例化哪个类。这样,类的实例化就推迟到了子类程序员

 

2.UITableView有哪些优化方式?web

    UITableView的优化主要从三个方面入手:面试

提早计算并缓存好高度(布局),由于heightForRowAtIndexPath:是调用最频繁的方法;sql

异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口;数据库

滑动时按需加载,这个在大量图片展现,网络加载的时候很管用!(SDWebImage已经实现异步加载,配合这条性能杠杠的)。设计模式

 

3.你如何理解blockblock有什么用途?数组

  咱们能够把Block当作Objective-C的匿名函数。Block容许开发者在两个对象之间将任意的语句当作数据进行传递,每每这要比引用定义在别处的函数直观。另外,block的实现具备封闭性(closure),而又可以很容易获取上下文的相关状态信息。缓存

block是代码块,其本质和变量相似。不一样的是代码块存储的数据是一个函数体。使用Block,就能够像其余标准函数同样,传入参数,并获得返回值。网络

做为OC对象的属性,实现对象之间的传值    • Block能够看作是一个变量,所以能够做为OC对象的属性

4.请问怎样可以保证定位更省电?

1.开启开始定位以后,不关闭,让其持续定位

  2.设置距离筛选器:坐标移动到指定距离才会调用代理方法

  3.设置精准度:经过下降计算的过程(GPS),来达到省电的目的

5.请简述NSUserDefaults的使用场景和 使用注意事项?

SUserDefaults适合存储轻量级的数据,他不只能够存储基本数据类型,还能够存储NSNumberIntegerFloatDouble),NSStringNSDateNSArrayNSDictionaryBOOL类型。

可是NSUserDefaults不能存储自定义的类对象,若把一个对象存储到NSUserDefaults会报错。聪明的人会把对象放进数组,再把数组存入NSUserDefaults,不过这样作事错误的,由于数组中包含了自定义对象。

若要在NSUserDefaults中存入自定义对象,则对象须要遵循NSCoding协议,并实现encodeWithCoder方法和initWithCoder方法。具体参考http://my.oschina.NET/u/1245365/blog/294449

值得一提的是,我发现类型为NSNull的空数据也是没法存入NSUserDefaults的。若数据中有NSNull类型空数据,把它置nil便可存入NSUserDefaults

总之,NSUserDefaults是一种操做简单的数据库

6.iOS中数据库使用什么技术实现的 ?

使用SqliteCoreData实现的

7.iOS中如何实现数据模型的存储?

 归档也是iOS提供给开发者的一种数据存储的方式,事实上,几乎全部的数据类型均可以经过归档来进行存取。其存储与读取的过程,主要封装在两个类中:NSKeyedArchiverNSKeyedUnarchiver

8.为何说Objective-C是一门动态的语言?

   Objective-C是动态语言,它并不是经过调用类的方法来执行功能,而是给对象发送消息,

  对象在接收到消息以后会去找匹配的方法来运行。

9.讲一下MVC和MVVM,MVP?

1.MVC做为老牌架构, 优势在于将业务场景按展现数据类型划分出多个模块, 每一个模块中的C层负责业务逻辑和业务展现, 而M和V应该是互相隔离的以作重用, 另外每一个模块处理得当也能够做为重用单元. 拆分在于解耦, 顺便作了减负, 隔离在于重用, 提高开发效率. 缺点是没有区分业务逻辑和业务展现, 对单元测试不友好.

2.MVP做为MVC的进阶版, 提出区分业务逻辑和业务展现, 将全部的业务逻辑转移到P层, V层接受P层的数据更新通知进行页面展现. 优势在于良好的分层带来了友好的单元测试, 缺点在于分层会让代码逻辑优势绕, 同时也带来了大量的代码工做, 对程序员不够友好.

3.MVVM做为集大成者, 经过数据绑定作数据更新, 减小了大量的代码工做, 同时优化了代码逻辑, 只是学习成本有点高, 对新手不够友好.

4.MVP和MVVM由于分层因此会创建MVC两倍以上的文件类, 须要良好的代码管理方式.

5.在MVP和MVVM中, V和P或者VM之间理论上是多对多的关系, 不一样的布局在相同的逻辑下只须要替换V层, 而相同的布局不一样的逻辑只须要替换P或者VM层. 但实际开发中P或者VM每每由于耦合了V层的展现逻辑退化成了一对一关系(好比SceneA中须要显示"xxx+Name", VM就将Name格式化为"xxx + Name". 某一天SceneB也用到这个模块, 全部的点击事件和页面展现都同样, 只是Name展现为"yyy + Name", 此时的VM由于耦合SceneA的展现逻辑, 就显得比较尴尬), 针对此类状况, 一般有两种办法, 一种是在VM层加状态进而判断输出状态, 一种是在VM层外再加一层FormatHelper. 前者可能由于状态过多显得代码难看, 后者虽然比较优雅且拓展性高, 可是过多的分层在数据还原时就略显笨拙, 你们应该按需选择.

这里随便瞎扯一句, 有些文章上来就说MVVM是为了解决C层臃肿, MVC难以测试的问题, 其实并非这样的. 按照架构演进顺序来看, C层臃肿大部分是没有拆分好MVC模块, 好好拆分就好了, 用不着MVVM. 而MVC难以测试也能够用MVP来解决, 只是MVP也并不是完美, 在VP之间的数据交互太繁琐, 因此才引出了MVVM. 当MVVM这个彻底体出现之后, 咱们从结果看起源, 发现它作了好多事情, 其实并非, 它的前辈们付出的努力也并很多!

10.为何在默认状况下没法修改被block捕获的变量? __block都作了什么?

  若是是用block(用static也能够)修饰的局部变量,在block内部访问的话,而是把这个局部变量的地址传递过去了,因此会跟踪这个局部变量的变化,而且能够修改,
若是block内部引用的变量是全局变量的话,那么在block内部访问,他也是把这个变量的地址传递过去了.。

11.模拟一下循环引用的一个状况?block实现界面反向传值如何实现?

ClassA和ClassB分属两个不一样的线程,ClassB一般由ClassA发起请求建立,并由ClassA使用,ClassB则会在必要时通知ClassA一些事件。二者中各保留了对方的一个引用计数指针RefPtr。

若是在析构时释放成员变量的话,就会发生循环引用的问题,致使两个对象释放失败。

block的回调的使用步骤

        1.声明    : 在谁那里调用就在谁那里声明

            实现代码

                typedef void(^MyBlock)(NSString *name);//block的重命名

                @property (nonatomic,copy) MyBlock block;//block的声明

 

        2.实现    : 谁要装值就在谁那里实现

            实现代码

                 SecondViewController *secondVC = [[SecondViewController alloc] init];

                 [self presentViewController:secondVC animated:YES completion:nil];//在这里没用导航控制器,用presentViewController来进入下一个视图

                 //block实现

                 secondVC.block = ^void(NSString *name)

                 {

                    _label.text = name;

                 };//block的位置摆放很做用的,由于它是一个函数,不过必定不能放在使用它的对象的外面和前面就行了

 

        3.调用    : 谁要传值就在谁那里调用

                self.block(@"呵呵");//block的调用

 总结一句话:block用在不一样视图控制器之间的值回传,回传还有代理、单例,在回传中最简单的就是用block了

 */

 12.objc在向一个对象发送消息时,发生了什么?

SomeClass * someObject; someObject = nil; [someObject doSomething];

就像这样,向nil发送了doSomething;OC中nil是被当作0定义的。也就是说runtime要去获取这个nil的信息,会去读取内存中0的位置,这确定是不容许的,会返回nil,0,0.0等数据,根据返回值类型。

比较让你混淆的是,僵尸对象。僵尸对象并非nil,僵尸对象是你的object被销毁或者用于其余地方了,可是指向它的指针还在。会发生向一个object发送一个它没有的方法。

13.何时会报unrecognized selector错误?iOS有哪些机制来避免走到这一步?

   对象未实现该方法。

   对象已经被释放。

  

    使用[id respondsToSelector:]进行判断。


forward.jpeg


Method resolution
objc运行时会调用+resolveInstanceMethod:或者 +resolveClassMethod:,让你有机会提供一个函数实现。若是你添加了函数,那运行时系统就会从新启动一次消息发送的过程,不然 ,运行时就会移到下一步,消息转发(Message Forwarding)。
返回Nil和self,去调用第三步methodSignatureForSelector和forwarInvocation;返回receiver,若是receiver有响应就直接处理,若是没有就去对应的对象内去调用第三步;调用子类的函数,子类没有进行这几个方法的重载,在父类处理时返回子类,会死循环。
Fast forwarding
若是目标对象实现了-forwardingTargetForSelector:,Runtime 这时就会调用这个方法,给你把这个消息转发给其余对象的机会。 只要这个方法返回的不是nil和self,整个消息发送的过程就会被重启,固然发送的对象会变成你返回的那个对象。不然,就会继续Normal Fowarding。 这里叫Fast,只是为了区别下一步的转发机制。由于这一步不会建立任何新的对象,但下一步转发会建立一个NSInvocation对象,因此相对更快点。
Normal forwarding
这一步是Runtime最后一次给你挽救的机会。首先它会发送-methodSignatureForSelector:消息得到函数的参数和返回值类型。若是-methodSignatureForSelector:返回nil,Runtime则会发出-doesNotRecognizeSelector:消息,程序这时也就挂掉了。若是返回了一个函数签名,Runtime就会建立一个NSInvocation对象并发送-forwardInvocation:消息给目标对象。

14.可否向编译后获得的类中增长实例变量?可否向运行时建立的类中添加实例变量?为何?

  

  1. 不能向编译后获得的类增长实例变量
  2. 能向运行时建立的类中添加实例变量

解释:

  1. 编译后的类已经注册在runtime中,类结构体中的objc_ivar_list实例变量的链表和instance_size实例变量的内存大小已经肯定,runtime会调用class_setvarlayout或class_setWeaklvarLayout来处理strong weak引用.因此不能向存在的类中添加实例变量

     2.运行时建立的类是能够添加实例变量,调用class_addIvar函数.可是的在调用objc_allocateClassPair以后,objc_registerClassPair以前,缘由同上.

15.runtime如何实现weak变量的自动置nil?

runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址做为 key,当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到全部以a为键的 weak 对象,从而设置为 nil。

16.给类添加一个属性后,在类结构体里哪些元素会发生变化?

定义结构体会

17.runloop是来作什么的?runloop和线程有什么关系?主线程默认开启了runloop么?子线程呢?

总的说来,Run loop,正如其名,loop表示某种循环,和run放在一块儿就表示一直在运行着的循环。
实际上,run loop和线程是紧密相连的,能够这样说run loop是为了线程而生,没有线程,它就没有存在的必要。
Run loops是线程的基础架构部分,Cocoa和CoreFundation都提供了run loop对象方便配置和管理线程的run loop。
每一个线程,包括程序的主线程(main thread)都有与之相应的run loop对象。
18.runloop的mode是用来作什么的?有几种mode?

用来控制一些特殊操做只能在指定模式下运行,通常能够经过指定操做的运行mode 来控制执行时机,以提升用户体验
系统默认注册了 5 个 Mode

kCFRunLoopDefaultMode:App 的默认 Mode,一般主线程是在这个 Mode

下运行,对应 OC 中的:NSDefaultRunLoopMode

UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑

动,保证界面滑动时不受其余 Mode 影响

kCFRunLoopCommonModes:这是一个标记 Mode,不是一种真正的 Mode,事件

能够运行在全部标有 common modes 标记的模式中,对应 OC 中的

NSRunLoopCommonModes , 带 有 common modes 标 记 的 模 式 有 :UITrackingRunLoopMode 和 kCFRunLoopDefaultMode

UIInitializationRunLoopMode:在启动 App 时进入的第一个 Mode,启动完成后

就再也不使用
GSEventReceiveRunLoopMode:接受系统事件的内部 Mode,一般用不到 

runloop和线程的关系:主线程的run loop默认是启动的, 子线程的runloop默认是不开启的,须要咱们本身手动开启循环; 。
19.为何把NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环之后,滑动scrollview的时候NSTimer却不动了?

RunLoop只能运行在一种mode下,若是要换mode,当前的loop也须要停下重启成新的。利用这个机制,ScrollView滚动过程当中NSDefaultRunLoopMode(kCFRunLoopDefaultMode)的mode会切换到UITrackingRunLoopMode来保证ScrollView的流畅滑动:只能在NSDefaultRunLoopMode模式下处理的事件会影响scrllView的滑动。

若是咱们把一个NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环中的时候, ScrollView滚动过程当中会由于mode的切换,而致使NSTimer将再也不被调度。

同时由于mode仍是可定制的,因此:

Timer计时会被scrollView的滑动影响的问题能够经过将timer添加到NSRunLoopCommonModes(kCFRunLoopCommonModes)来解决。

20.苹果是如何实现Autorelease Pool的?

每个线程的 autoreleasepool 其实就是一个指针的堆栈;

每个指针表明一个须要 release 的对象或者 POOL_SENTINEL(哨兵对象,表明一个 autoreleasepool 的边界);

一个 pool token 就是这个 pool 所对应的 POOL_SENTINEL 的内存地址。当这个 pool 被 pop 的时候,全部内存地址在 pool token 以后的对象都会被 release ;

这个堆栈被划分红了一个以 page 为结点的双向链表。pages 会在必要的时候动态地增长或删除;

Thread-local storage(线程局部存储)指向 hot page ,即最新添加的 autoreleased 对象所在的那个 page 。

21.isa指针?(对象的isa,类对象的isa,元类的isa都要说)

1、类的基本概念:

一、类其实也是一个对象, 这个对象会在这个类第一次被使用的时候建立

二、只要有了类对象, 未来就能够经过类对象来建立实例对象

三、实例对象中有一个isa指针, 指向建立本身的类对象

四、类对象中保存了当前对象全部的对象方法

五、当给一个实例对象发送消息的时候, 会根据实例对象中的isa指针去对应的类对象中查找

六、全部类对象的继承关系就是元类对象的继承关系

 

2、isa指针

1.每个对象都包含一个isa指针.这个指针指向当前对象所属的类。

2.[d bark];表示给d所指向的对象发送一条bark消息,调用对象的bark方法,此时对象会顺着内部的isa指针找到存储于类中的方法并执行。

3.isa是对象中的隐藏指针,指向建立这个对象的类。

4.经过isa指针咱们能够在运行的时候知道当前对象是属于那个类。

 

3、元类

一、元类的定义:元类是类对象的类,每一个类都有本身独一无二的元类,即

   (1)当你给对象发送消息时,消息是在寻找这个对象的类的方法列表。

   (2)当你给类发消息时,消息是在寻找这个类的元类的方法列表。

元类是必不可少的,由于它存储了类的类方法。每一个类都必须有独一无二的元类,由于每一个类都有独一无二的类方法。

二、元类的类:

   (1)元类,就像类同样,它也是一个对象,也能够调用它的方法。这就意味着他必须也有一个类。

 (2)全部的元类都使用根元类(继承体系中处于顶端的类的元类)做为他们的类。即全部NSObject的子类的元类都会以NSObject的元类做为他们的类。

   (3)全部的元类使用根元类做为他们的类,根元类的元类则就是它本身。也就是说基类的元类的isa指针指向他本身。

 22.介绍一下分类,能用分类作什么?内部是如何实现的?它为何会覆盖掉原来的方法?

由于分类方法加入类中这一操做是在运行期系统加载分类时完成的,运行期系统会把分类中所实现的每个方法都加入类的方法列表中,具体步骤以下:

  1. category的实例方法、协议以及属性添加到类上

  2. category的类方法和协议添加到类的metaclass上

category的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的后面,因此category的方法会“覆盖”掉原来类的同名方法。

23.运行时能增长成员变量么?能增长属性么?若是能,如何增长?若是不能,为何?

不少人在面试的时候都会被问到Category,既然容许用Category给类增长方法和属性,那为何不容许增长成员变量?
在Objective-C提供的runtime函数中,确实有一个class_addIvar()函数用于给类添加成员变量,可是阅读过苹果的官方文档的人应该会看到:

This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.

大概的意思说,这个函数只能在“构建一个类的过程当中”调用。一旦完成类定义,就不能再添加成员变量了。通过编译的类在程序启动后就被runtime加载,没有机会调用addIvar。程序在运行时动态构建的类须要在调用objc_registerClassPair以后才能够被使用,一样没有机会再添加成员变量。

24.objc中向一个nil对象发送消息将会发生什么?(返回值是对象,是标量,结构体)

在 Objective-C 中向 nil 发送消息是彻底有效的——只是在运行时不会有任何做用:

  1. 若是一个方法返回值是一个对象,那么发送给nil的消息将返回0(nil)。例如:
    Person * motherInlaw = [[aPerson spouse] mother];
    若是 spouse 对象为 nil,那么发送给 nil 的消息 mother 也将返回 nil。
  2. 若是方法返回值为指针类型,其指针大小为小于或者等于sizeof(void*),float,double,long double 或者 long long 的整型标量,发送给 nil 的消息将返回0。
  3. 若是方法返回值为结构体,发送给 nil 的消息将返回0。结构体中各个字段的值将都是0。
  4. 若是方法的返回值不是上述提到的几种状况,那么发送给 nil 的消息的返回值将是未定义的。
相关文章
相关标签/搜索