【2019年最新大厂面试题】 iOS面试题附答案

推荐文章

200道iOS面试题整理,底层、技术亮点公司须要的这里都有

若是你依然在编程的世界里迷茫,不知道本身的将来规划,小编给你们推荐一个iOS高级交流群:458839238 里面能够与大神一块儿交流并走出迷茫。小白可进群免费领取学习资料,看看前辈们是如何在编程的世界里傲然前行! 群内提供数据结构与算法、底层进阶、swift、逆向、整合面试题等免费资料 附上一份收集的各大厂面试题(附答案) ! 群文件直接获取 各大厂面试题ios

一.设计模式是什么? 你知道哪些设计模式,并简要叙述?面试

设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型的事情。 1). MVC模式:Model View Control,把模型 视图 控制器 层进行解耦合编写。 2). MVVM模式:Model View ViewModel 把模型 视图 业务逻辑 层进行解耦和编写。 3). 单例模式:经过static关键词,声明全局变量。在整个进程运行期间只会被赋值一次。 4). 观察者模式:KVO是典型的通知模式,观察某个属性的状态,状态发生变化时通知观察者。 5). 委托模式:代理+协议的组合。实现1对1的反向传值操做。 6). 工厂模式:经过一个类方法,批量的根据已有模板生产对象。 MVC 和 MVVM 的区别objective-c

在MVC下,Controller基本是没法测试的,里面混杂了个各类逻辑,并且分散在不一样的地方。有了MVVM咱们就能够测试里面的viewModel,来验证咱们的处理结果对不对(Xcode7的测试已经愈来愈完善了)。算法

好比iOS里面有iPhone版本和iPad版本,除了交互展现不同外,业务逻辑的model是一致的。这样,咱们就能够以很小的代价去开发另外一个app。数据库

MVVM是MVC的一个升级版,目前的MVC也能够很快的转换到MVVM这个模式。VC能够省去一大部分展现逻辑。编程

缺点:swift

每一个VC都附带一个viewModel,类的数量*2设计模式

咱们把逻辑给了viewModel,那势必Model也会变得很复杂,里面的属性和方法愈来愈多。可能重写的方法比较多,由于涉及到一些数据的转换以及和controller之间的通讯。数组

因为数据都是从viewModel来,想一想忽然来了一个新人,一看代码,不知道真实的模型是谁。好比经常使用tableview的数据源,通常都是一个数组,若是不断的经过viewModel去取,沟通上没有那么直接。何况每封一层,意味着要写不少代码去融合他们的转换。浏览器

Model负责存储、定义、操做数据;

View用来展现给用户,而且和用户进行交互;

Controller是Model和View的协调者,Controller把Model中的数据拿过来给View使用。Controller能够直接与Model和View进行通讯,而View不能与Controller直接通讯。,当有数据更新时,Model也要与Controller进行通讯,这个时候就要用Notification和KVO,这个方式就像发广播同样,Model发信号,Controller设置接收监听信号,当有数据更新是就发信号给Controller,Model和View不能直接通讯,这样违背MVC设计原则。View与Controller通讯须要利用代理协议的方式,Controller能够直接根据Model决定View的展现。View若是接受响应事件则经过delegate,target-action,block等方式告诉Controller的状态变化。Controller进行业务的处理,而后再控制View的展现。

关于MVVM的优势:

方便测试

便于代码的移植

兼容MVC

类会增多

viewModel会愈来愈庞大

调用复杂度增长

二.#import跟 #include 有什么区别,@class呢,#import<> 跟 #import””有什么区别?

(1)#import指令是Object-C针对@include的改进版本,能确保引用的文件只会被引用一次,不会陷入递归包含的问题中;

(2)@import与@class的区别:

#import会链入该头文件的所有信息,包括实体变量和方法等;二@class只是告诉编译器,其后面声明的名称是类的名称,至于这些类如何定义的,暂时不用考虑。在头文件中,通常只须要知道被引用的类的名称就能够了,不须要知道其内部的实体变量和方法,因此在头文件中通常使用@class来声明这个名称是类的名称;而在实现类里面,由于会用到这个引用类的内部的实体变量和方法,因此须要使用#import类包含这个被引用类的头文件。

@class还能够解决循环包含的问题

(3)#import<>跟#import""的区别:

#import<>用来包含系统自带的文件,#import""用来包含自定义的文件

(4)属性readwrite,readonly,assign,retain,copy,nonatomic 各是什么做用,在那种状况下用?

• readwrite:是可读可写特性,同时生成get方法和set方法的声明和实现(补充:默认属性,将生成不带额外参数的getter和setter方法(setterff只有一个参数))

• readonly:只读特性,只会生成get方法的声明和实现;不但愿属性在类外改变

• assign:是赋值特性,set方法的实现是直接赋值,用于基本数据类型;仅设置变量时

• retain:表示持有特性,set方法将传入参数先保留,再赋值,传入参数的retaincount会+1;

• copy:表示拷贝特性,set方法的实现是release旧值,copy新值,用于NSString、block等类型(set方法将传入的对象复制一份;须要彻底一份新的变量时使用);

• nonatomic:非原子操做,决定编译器生成的setter getter是不是原子操做,atomic表示多线程安全,通常使用nonatomic

三.frame 和 bounds 有什么不一样?

frame指的是:该view在父view坐标系统中的位置和大小。(参照点是父view的坐标系统) bounds指的是:该view在自己坐标系统中的位置和大小。(参照点是自己坐标系统)

四.Objective-C的类能够多重继承么?能够实现多个接口么?Category是什么?重写一个类的方式用继承好仍是分类好?为何?

答:Objective-C的类不能够多重继承;能够实现多个接口(协议);Category是类别;通常状况用分类好,用Category去重写类的方法,仅对本Category有效,不会影响到其余类与原有类的关系。

五.@property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的

@property 的本质是什么? @property = ivar + getter + setter; “属性” (property)有两大概念:ivar(实例变量)、getter+setter(存取方法)

“属性” (property)做为 Objective-C 的一项特性,主要的做用就在于封装对象中的数据。 Objective-C 对象一般会把其所须要的数据保存为各类实例变量。实例变量通常经过“存取方法”(access method)来访问。其中,“获取方法” (getter)用于读取变量值,而“设置方法” (setter)用于写入变量值。

六.@property中有哪些属性关键字?/ @property 后面能够有哪些修饰符?

属性能够拥有的特质分为四类: 1.原子性--- nonatomic 特质 2.读/写权限---readwrite(读写)、readonly (只读) 3.内存管理语义---assign、strong、 weak、unsafe_unretained、copy 4.方法名---getter= 、setter= 5.不经常使用的:nonnull,null_resettable,nullable

七.属性关键字 readwrite,readonly,assign,retain,copy,nonatomic 各是什么做用,在那种状况下用?

答: 1). readwrite 是可读可写特性。须要生成getter方法和setter方法。 2). readonly 是只读特性。只会生成getter方法,不会生成setter方法,不但愿属性在类外改变。 3). assign 是赋值特性。setter方法将传入参数赋值给实例变量;仅设置变量时,assign用于基本数据类型。 4). retain(MRC)/strong(ARC) 表示持有特性。setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1。 5). copy 表示拷贝特性。setter方法将传入对象复制一份,须要彻底一份新的变量时。 6). nonatomic 非原子操做。决定编译器生成的setter和getter方法是不是原子操做,atomic表示多线程安全,通常使用nonatomic,效率高。

八.什么状况使用 weak 关键字,相比 assign 有什么不一样?

1.在 ARC 中,在有可能出现循环引用的时候,每每要经过让其中一端使用 weak 来解决,好比: delegate 代理属性。 2.自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,自定义 IBOutlet 控件属性通常也使用 weak;固然,也能够使用strong。

IBOutlet连出来的视图属性为何能够被设置成weak? 由于父控件的subViews数组已经对它有一个强引用。

不一样点: assign 能够用非 OC 对象,而 weak 必须用于 OC 对象。 weak 代表该属性定义了一种“非拥有关系”。在属性所指的对象销毁时,属性值会自动清空(nil)。

九.怎么用 copy 关键字?

用途:

  1. NSString、NSArray、NSDictionary 等等常用copy关键字,是由于他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary;
  2. block 也常用 copy 关键字。

说明: block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 能够把它放到堆区.在 ARC 中写不写都行:对于 block 使用 copy 仍是 strong 效果是同样的,但写上 copy 也无伤大雅,还能时刻提醒咱们:编译器自动对 block 进行了 copy 操做。若是不写 copy ,该类的调用者有可能会忘记或者根本不知道“编译器会自动对 block 进行了 copy 操做”,他们有可能会在调用以前自行拷贝属性值。这种操做多余而低效。

十.用@property声明的 NSString / NSArray / NSDictionary 常用 copy 关键字,为何?若是改用strong关键字,可能形成什么问题?

答:用 @property 声明 NSString、NSArray、NSDictionary 常用 copy 关键字,是由于他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操做(就是把可变的赋值给不可变的),为确保对象中的字符串值不会无心间变更,应该在设置新属性值时拷贝一份。

  1. 由于父类指针能够指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 不管给我传入是一个可变对象仍是不可对象,我自己持有的就是一个不可变的副本。
  2. 若是咱们使用是 strong ,那么这个属性就有可能指向一个可变对象,若是这个可变对象在外部被修改了,那么会影响该属性。

//总结:使用copy的目的是,防止把可变类型的对象赋值给不可变类型的对象时,可变类型对象的值发送变化会无心间篡改不可变类型对象原来的值。

十一.浅拷贝和深拷贝的区别?

答: 浅拷贝:只复制指向对象的指针,而不复制引用对象自己。 深拷贝:复制引用对象自己。内存中存在了两份独立对象自己,当修改A时,A_copy不变。

十二.这个写法会出什么问题:@property (nonatomic, copy) NSMutableArray *arr;

问题:添加,删除,修改数组内的元素的时候,程序会由于找不到对应的方法而崩溃。

缘由:是由于 copy 就是复制一个不可变 NSArray 的对象,不能对 NSArray 对象进行添加/修改。

十三.如何让本身的类用 copy 修饰符?如何重写带 copy 关键字的 setter?

若想令本身所写的对象具备拷贝功能,则需实现 NSCopying 协议。若是自定义的对象分为可变版本与不可变版本,那么就要同时实现 NSCopying 与 NSMutableCopying 协议。 具体步骤: 1. 需声明该类听从 NSCopying 协议 2. 实现 NSCopying 协议的方法。

十四.@synthesize 和 @dynamic 分别有什么做用?

@property有两个对应的词,一个是@synthesize(合成实例变量),一个是@dynamic。 若是@synthesize和@dynamic都没有写,那么默认的就是 @synthesize var = _var; // 在类的实现代码里经过 @synthesize 语法能够来指定实例变量的名字。(@synthesize var = _newVar;)

  1. @synthesize 的语义是若是你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
  2. @dynamic 告诉编译器,属性的setter与getter方法由用户本身实现,不自动生成(如,@dynamic var)。

十五.常见的 Objective-C 的数据类型有那些,和C的基本数据类型有什么区别?如:NSInteger和int

答: Objective-C的数据类型有NSString,NSNumber,NSArray,NSMutableArray,NSData等等,这些都是class,建立后即是对象,而C语言的基本数据类型int,只是必定字节的内存空间,用于存放数值;NSInteger是基本数据类型,并非NSNumber的子类,固然也不是NSObject的子类。NSInteger是基本数据类型Int或者Long的别名(NSInteger的定义typedef long NSInteger),它的区别在于,NSInteger会根据系统是32位仍是64位来决定是自己是int仍是long。

十六.id 声明的对象有什么特性?

答:id 声明的对象具备运行时的特性,便可以指向任意类型的Objcetive-C的对象。

十七.Objective-C 如何对内存管理的,说说你的见解和解决方法?

答:Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。 1). 自动内存计数ARC:由Xcode自动在App编译阶段,在代码中添加内存管理代码。 2). 手动内存计数MRC:遵循内存谁申请、谁释放;谁添加,谁释放的原则。 3). 内存释放池Release Pool:把须要释放的内存统一放在一个池子中,当池子被抽干后(drain),池子中全部的内存空间也被自动释放掉。内存池的释放操做分为自动和手动。自动释放受runloop机制影响。

十八.Objective-C 中建立线程的方法是什么?若是在主线程中执行代码,方法是什么?若是想延时执行代码、方法又是什么?

答:线程建立有三种方法:使用NSThread建立、使用GCD的dispatch、使用子类化的NSOperation,而后将其加入NSOperationQueue;在主线程执行代码,方法是performSelectorOnMainThread,若是想延时执行代码能够用performSelector:onThread:withObject:waitUntilDone:

十九.Category(类别)、 Extension(扩展)和继承的区别

区别:

  1. 分类有名字,类扩展没有分类名字,是一种特殊的分类。
  2. 分类只能扩展方法(属性仅仅是声明,并没真正实现),类扩展能够扩展属性、成员变量和方法。
  3. 继承能够增长,修改或者删除方法,而且能够增长属性。

二十.咱们说的OC是动态运行时语言是什么意思?

答:主要是将数据类型的肯定由编译时,推迟到了运行时。简单来讲, 运行时机制使咱们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。

二十一.为何咱们常见的delegate属性都用是week而不是retain/strong?

答:是为了防止delegate两端产生没必要要的循环引用。 @property (nonatomic, weak) id delegate;

二十二。何时用delete,何时用Notification?

Delegate(委托模式):1对1的反向消息通知功能。 Notification(通知模式):只想要把消息发送出去,告知某些状态的变化。可是并不关心谁想要知道这个。

二十三.什么是 KVO 和 KVC?

1). KVC(Key-Value-Coding):键值编码 是一种经过字符串间接访问对象的方式(即给属性赋值) 举例说明: stu.name = @"张三" // 点语法给属性赋值 [stu setValue:@"张三" forKey:@"name"]; // 经过字符串使用KVC方式给属性赋值 stu1.nameLabel.text = @"张三"; [stu1 setValue:@"张三" forKey:@"nameLabel.text"]; // 跨层赋值 2). KVO(key-Value-Observing):键值观察机制 他提供了观察某一属性变化的方法,极大的简化了代码。 KVO只能被KVC触发,包括使用setValue:forKey:方法和点语法。 // 经过下方方法为属性添加KVO观察

  • (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context; // 当被观察的属性发送变化时,会自动触发下方方法
  • (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{}

KVC 和 KVO 的 keyPath 能够是属性、实例变量、成员变量。

二十四.KVC的底层实现?

当一个对象调用setValue方法时,方法内部会作如下操做: 1). 检查是否存在相应的key的set方法,若是存在,就调用set方法。 2). 若是set方法不存在,就会查找与key相同名称而且带下划线的成员变量,若是有,则直接给成员变量属性赋值。 3). 若是没有找到_key,就会查找相同名称的属性key,若是有就直接赋值。 4). 若是尚未找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。 这些方法的默认实现都是抛出异常,咱们能够根据须要重写它们。

二十五.ViewController生命周期

按照执行顺序排列:

  1. initWithCoder:经过nib文件初始化时触发。
  2. awakeFromNib:nib文件被加载的时候,会发生一个awakeFromNib的消息到nib文件中的每一个对象。
  3. loadView:开始加载视图控制器自带的view。
  4. viewDidLoad:视图控制器的view被加载完成。
  5. viewWillAppear:视图控制器的view将要显示在window上。
  6. updateViewConstraints:视图控制器的view开始更新AutoLayout约束。
  7. viewWillLayoutSubviews:视图控制器的view将要更新内容视图的位置。
  8. viewDidLayoutSubviews:视图控制器的view已经更新视图的位置。
  9. viewDidAppear:视图控制器的view已经展现到window上。
  10. viewWillDisappear:视图控制器的view将要从window上消失。
  11. viewDidDisappear:视图控制器的view已经从window上消失。

二十六.你是否接触过OC中的反射机制?简单聊一下概念和使用

1). class反射 经过类名的字符串形式实例化对象。 Class class = NSClassFromString(@"student"); Student *stu = [[class alloc] init]; 将类名变为字符串。 Class class =[Student class]; NSString className = NSStringFromClass(class); 2). SEL的反射 经过方法的字符串形式实例化方法。 SEL selector = NSSelectorFromString(@"setName");
[stu performSelector:selector withObject:@"Mike"]; 将方法变成字符串。 NSStringFromSelector(@selector
(setName:));

二十七.如何对iOS设备进行性能测试?

1.app使用过程当中,接听电话。能够测试不一样的通话时间的长短,对于通话结束后,原先打开的app的响应,好比是否停留在原先界面,继续操做时的相应速度等。

2.app使用过程当中,有推送消息时,对app的使用影响

3.设备在充电时,app的响应以及操做流畅度

4.设备在不一样电量时(低于10%,50%,95%),app的响应以及操做流畅度

5.意外断电时,app数据丢失状况

6.网络环境变化时,app的应对状况如何:是否有适当提示?从有网络环境到无网络环境时,app的反馈如何?从无网络环境回到有网络环境时,是否能自动加载数据,多久才能开始加载数据

7.多点触摸的状况

8.跟其余app之间互相切换时的响应

9.进程关闭再从新打开的反馈

10.IOS系统语言环境变化时

二十八.开发项目时你是怎么检查内存泄露?

1 内存泄漏

内存泄漏是编程中经常见到的一个问题,内存泄漏每每会一种奇怪的方式来表现出来,基本上每一个程序都表现出不一样的方式。 可是通常最后的结果只有两个,一个是程序当掉,一个是系统内存不足。 还有一种就是比较介于中间的结果程序不会当,可是系统的反映时间明显下降,须要定时的Reboot才会正常。

有一个很简单的办法来检查一个程序是否有内存泄漏。就是是用Windows的任务管理器(Task Manager)。运行程序,而后在任务管理器里面查看 “内存使用”和”虚拟内存大小”两项,当程序请求了它所须要的内存以后,若是虚拟内存仍是持续的增加的话,就说明了这个程序有内存泄漏问题。 固然若是内存泄漏的数目很是的小,用这种方法可能要过很长时间才能看的出来。

2 缘由

内存泄漏产生的缘由通常是三种状况:

1. 内存忘记回收,这个是不该该的事情。可是也是在代码种很常见的问题。分配内存以后,用完以后,就必定要回收。若是不回收,那就形成了内存的泄漏,形成内存泄漏的Code若是被常常调用的话,那内存泄漏的数目就会愈来愈多的。从而影响整个系统的运行。

3 检查方法

通常的内存泄漏检查的确是很困难,可是也不是彻底没有办法。若是你用VC的库来写东西的话,那么很幸运的是,你已经有了不少检查内存泄漏的工具,只是你想不想用的问题了。Visual C++的Debug版本的C运行库(C Runtime Library)。它已经提供好些函数来帮助你诊断你的代码和跟踪内存泄漏。 并且最方便的地方是这些函数在Release版本中彻底不起任何做用,这样就不会影响你的Release版本程序的运行效率。

好比下面的例子里面,有一个明细的内存泄漏。固然若是只有这么几行代码的话,是很容易看出有内存泄漏的。可是想在成千上万行代码里面检查内存泄漏问题就不是那么容易了。

若是你双击包含行文件名的输出行,指针将会跳到源文件中内存被分配地方的行。当没法肯定那些代码产生了内存泄漏的时候,咱们就须要进行内存状态比较。在可疑的代码段的先后设置内存检查点,比较内存使用是否有可疑的变化。以肯定内存是否有泄漏。为此要先定义三个_CrtMemState 对象来保存要比较的内存状态。两个是用来比较,一个用了保存前面两个之间的区别

把光标移到DEBUG_NEW上面 按F12,就能够进入Afx.h中定义这些Class和函数的代码部分。 VC中内存泄漏的常规检查办法主要是上面的两种。固然这两种方法只是针对于Debug版本的Heap的检查。若是Release版本中还有内存泄漏,那么检查起来就麻烦不少了。

分配完内存以后忘了回收;

程序Code有问题,形成没有办法回收;

某些API函数操做不正确,形成内存泄漏。

二十九.什么是懒加载?

答:懒加载就是只在用到的时候才去初始化。也能够理解成延时加载。 我以为最好也最简单的一个例子就是tableView中图片的加载显示了, 一个延时加载, 避免内存太高,一个异步加载,避免线程堵塞提升用户体验。

三十.类变量的 @public,@protected,@private,@package 声明各有什么含义?

@public 任何地方都能访问; @protected 该类和子类中访问,是默认的; @private 只能在本类中访问; @package 本包内使用,跨包不能够。

三十一.什么是谓词?

谓词就是经过NSPredicate给定的逻辑条件做为约束条件,完成对数据的筛选。 //定义谓词对象,谓词对象中包含了过滤条件(过滤条件比较多) NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age<%d",30]; //使用谓词条件过滤数组中的元素,过滤以后返回查询的结果 NSArray *array = [persons filteredArrayUsingPredicate:predicate];

三十二.isa指针问题

isa:是一个Class 类型的指针. 每一个实例对象有个isa的指针,他指向对象的类,而Class里也有个isa的指针, 指向meteClass(元类)。元类保存了类方法的列表。当类方法被调 用时,先会从自己查找类方法的实现,若是没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass)。根元类的isa指针指向自己,这样造成了一个封闭的内循环。

三十三.如何访问并修改一个类的私有属性?

1.KVC

咱们能够用setValue:的方法设置私有属性,并利用valueForKey:的方法访问私有属性。假设咱们有一个类Person,而且这个类有一个私有属性name。看代码:

Person * ls = [[Person alloc] init];

[ls setValue:@"wo" forKey:@"name"];

2.runtime

咱们能够利用runtime获取某个类的全部属性(私有属性、非私有属性),在获取到某个类的属性后就能够对该属性进行访问以及修改了。

三十四.一个objc对象的isa的指针指向什么?有什么做用?

isa 指的就是 是个什么,对象的isa指向类,类的isa指向元类(meta class),元类isa指向元类的根类。isa帮助一个对象找到它的方法。isa:是一个Class 类型的指针. 每一个实例对象有个isa的指针,他指向对象的类,而Class里也有个isa的指针, 指向meteClass(元类)。元类保存了类方法的列表。当类方法被调用时,先会从自己查找类方法的实现,若是没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass).根元类的isa指针指向自己,这样造成了一个封闭的内循环。

三十五.isKindOfClass、isMemberOfClass、selector做用分别是什么

isKindOfClass:做用是某个对象属于某个类型或者继承自某类型。 isMemberOfClass:某个对象确切属于某个类型。 selector:经过方法名,获取在内存中的函数的入口地址。

三十六.delegate 和 notification 的区别

1). 两者都用于传递消息,不一样之处主要在于一个是一对一的,另外一个是一对多的。 2). notification经过维护一个array,实现一对多消息的转发。 3). delegate须要二者之间必须创建联系,否则无法调用代理的方法;notification不须要二者之间有联系。

三十七.什么是block?

闭包(block):闭包就是获取其它函数局部变量的匿名函数。

三十八.block的注意点

1). 在block内部使用外部指针且会形成循环引用状况下,须要用__week修饰外部指针: __weak typeof(self) weakSelf = self; 2). 在block内部若是调用了延时函数还使用弱指针会取不到该指针,由于已经被销毁了,须要在block内部再将弱指针从新强引用一下。 __strong typeof(self) strongSelf = weakSelf; 3). 若是须要在block内部改变外部栈区变量的话,须要在用__block修饰外部变量。

三十九.BAD_ACCESS在什么状况下出现?

答:这种问题在开发时常常遇到。缘由是访问了野指针,好比访问已经释放对象的成员变量或者发消息、死循环等。

四十.lldb(gdb)经常使用的控制台调试命令?

1). p 输出基本类型。是打印命令,须要指定类型。是print的简写 p (int)[[[self view] subviews] count] 2). po 打印对象,会调用对象description方法。是print-object的简写 po [self view] 3). expr 能够在调试时动态执行指定表达式,并将结果打印出来。经常使用于在调试过程当中修改变量的值。 4). bt:打印调用堆栈,是thread backtrace的简写,加all可打印全部thread的堆栈 5). br l:是breakpoint list的简写

四十一.你通常是怎么用Instruments的?

Instruments里面工具不少,经常使用: 1). Time Profiler: 性能分析 2). Zombies:检查是否访问了僵尸对象,可是这个工具只能从上往下检查,不智能。 3). Allocations:用来检查内存,写算法的那批人也用这个来检查。 4). Leaks:检查内存,看是否有内存泄露。

四十二.iOS中经常使用的数据存储方式有哪些?

数据存储有四种方案:NSUserDefault、KeyChain、file、DB。 其中File有三种方式:plist、Archive(归档) DB包括:SQLite、FMDB、CoreData

四十三.iOS的沙盒目录结构是怎样的?

沙盒结构: 1). Application:存放程序源文件,上架前通过数字签名,上架后不可修改。 2). Documents:经常使用目录,iCloud备份目录,存放数据。(这里不能存缓存文件,不然上架不被经过) 3). Library: Caches:存放体积大又不须要备份的数据。(经常使用的缓存路径) Preference:设置目录,iCloud会备份设置信息。 4). tmp:存放临时文件,不会被备份,并且这个文件下的数据有可能随时被清除的可能。

四十四.iOS多线程技术有哪几种方式?

答:pthread、NSThread、GCD、NSOperation

四十五.GCD 与 NSOperation 的区别:

GCD 和 NSOperation 都是用于实现多线程: GCD 基于C语言的底层API,GCD主要与block结合使用,代码简洁高效。 NSOperation 属于Objective-C类,是基于GCD更高一层的封装。复杂任务通常用NSOperation实现。

四十六.写出使用GCD方式从子线程回到主线程的方法代码

答:dispatch_sync(dispatch_get_main_queue(), ^{ });

四十七.如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,而后在都下载完成后合成一张整图)

// 使用Dispatch Group追加block到Global Group Queue,这些block若是所有执行完毕,就会执行Main Dispatch Queue中的结束处理的block。 // 建立队列组 dispatch_group_t group = dispatch_group_create(); // 获取全局并发队列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_async(group, queue, ^{ /*加载图片1 */ }); dispatch_group_async(group, queue, ^{ /*加载图片2 */ }); dispatch_group_async(group, queue, ^{ /*加载图片3 */ }); // 当并发队列组中的任务执行完毕后才会执行这里的代码 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 合并图片 });

四十八.dispatch_barrier_async(栅栏函数)的做用是什么?

函数定义:dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block); 做用: 1.在它前面的任务执行结束后它才执行,它后面的任务要等它执行完成后才会开始执行。 2.避免数据竞争

// 1.建立并发队列 dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT); // 2.向队列中添加任务 dispatch_async(queue, ^{ // 1.2是并行的 NSLog(@"任务1, %@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"任务2, %@",[NSThread currentThread]); });

dispatch_barrier_async(queue, ^{ NSLog(@"任务 barrier, %@", [NSThread currentThread]); });

dispatch_async(queue, ^{ // 这两个是同时执行的 NSLog(@"任务3, %@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"任务4, %@",[NSThread currentThread]); });

// 输出结果: 任务1 任务2 ——》 任务 barrier ——》任务3 任务4 // 其中的任务1与任务2,任务3与任务4 因为是并行处理前后顺序不定。

四十九.什么是 RunLoop

从字面上讲就是运行循环,它内部就是do-while循环,在这个循环内部不断地处理各类任务。 一个线程对应一个RunLoop,基本做用就是保持程序的持续运行,处理app中的各类事件。经过runloop,有事运行,没事就休息,能够节省cpu资源,提升程序性能。

主线程的run loop默认是启动的。iOS的应用程序里面,程序启动后会有一个以下的main()函数 int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }

五十.什么是 Runtime

Runtime又叫运行时,是一套底层的C语言API,其为iOS内部的核心之一,咱们平时编写的OC代码,底层都是基于它来实现的。

五十一.Runtime实现的机制是什么,怎么用,通常用于干吗?

1). 使用时须要导入的头文件 <objc/message.h> <objc/runtime.h> 2). Runtime 运行时机制,它是一套C语言库。 3). 实际上咱们编写的全部OC代码,最终都是转成了runtime库的东西。 好比: 类转成了 Runtime 库里面的结构体等数据类型, 方法转成了 Runtime 库里面的C语言函数, 平时调方法都是转成了 objc_msgSend 函数(因此说OC有个消息发送机制) // OC是动态语言,每一个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector)。 // [stu show]; 在objc动态编译时,会被转意为:objc_msgSend(stu, @selector(show)); 4). 所以,能够说 Runtime 是OC的底层实现,是OC的幕后执行者。

有了Runtime库,能作什么事情呢? Runtime库里面包含了跟类、成员变量、方法相关的API。 好比: (1)获取类里面的全部成员变量。 (2)为类动态添加成员变量。 (3)动态改变类的方法实现。 (4)为类动态添加新的方法等。 所以,有了Runtime,想怎么改就怎么改。

五十二.什么是 Method Swizzle(黑魔法),什么状况下会使用?

1). 在没有一个类的实现源码的状况下,想改变其中一个方法的实现,除了继承它重写、和借助类别重名方法暴力抢先以外,还有更加灵活的方法 Method Swizzle。 2). Method Swizzle 指的是改变一个已存在的选择器对应的实现的过程。OC中方法的调用可以在运行时经过改变,经过改变类的调度表中选择器到最终函数间的映射关系。 3). 在OC中调用一个方法,实际上是向一个对象发送消息,查找消息的惟一依据是selector的名字。利用OC的动态特性,能够实如今运行时偷换selector对应的方法实现。 4). 每一个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点相似函数指针,指向具体的方法实现。 5). 咱们能够利用 method_exchangeImplementations 来交换2个方法中的IMP。 6). 咱们能够利用 class_replaceMethod 来修改类。 7). 咱们能够利用 method_setImplementation 来直接设置某个方法的IMP。 8). 归根结底,都是偷换了selector的IMP。

五十三._objc_msgForward 函数是作什么的,直接调用它将会发生什么?

答:_objc_msgForward是 IMP 类型,用于消息转发的:当向一个对象发送一条消息,但它并无实现的时候,_objc_msgForward会尝试作消息转发。

五十四.什么是 TCP / UDP ?

TCP:传输控制协议。 UDP:用户数据协议。

TCP 是面向链接的,创建链接须要经历三次握手,是可靠的传输层协议。 UDP 是面向无链接的,数据传输是不可靠的,它只管发,无论收不收获得。 简单的说,TCP注重数据安全,而UDP数据传输快点,但安全性通常。

五十五.通讯底层原理(OSI七层模型)

OSI采用了分层的结构化技术,共分七层: 物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。

五十六.OC中建立线程的方法是什么?若是在主线程中执行代码,方法是什么?

// 建立线程的方法

  • [NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil]
  • [self performSelectorInBackground:nil withObject:nil];
  • [[NSThread alloc] initWithTarget:nil selector:nil object:nil];
  • dispatch_async(dispatch_get_global_queue(0, 0), ^{});
  • [[NSOperationQueue new] addOperation:nil];

// 主线程中执行代码的方法

  • [self performSelectorOnMainThread:nil withObject:nil waitUntilDone:YES];
  • dispatch_async(dispatch_get_main_queue(), ^{});
  • [[NSOperationQueue mainQueue] addOperation:nil];

五十七.用伪代码写一个线程安全的单例模式

static id _instance;

  • (id)allocWithZone:(struct _NSZone *)zone { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instance = [super allocWithZone:zone]; }); return _instance; }

  • (instancetype)sharedData { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instance = [[self alloc] init]; }); return _instance; }

  • (id)copyWithZone:(NSZone *)zone { return _instance; }

五十八.在手势对象基础类UIGestureRecognizer的经常使用子类手势类型中哪两个手势发生后,响应只会执行一次?

答:UITapGestureRecognizer,UISwipeGestureRecognizer是一次性手势,手势发生后,响应只会执行一次。

五十九.HTTP协议中 POST 方法和 GET 方法有那些区别?

  1. GET用于向服务器请求数据,POST用于提交数据
  2. GET请求,请求参数拼接形式暴露在地址栏,而POST请求参数则放在请求体里面,所以GET请求不适合用于验证密码等操做
  3. GET请求的URL有长度限制,POST请求不会有长度限制

六十.请简单的介绍下APNS发送系统消息的机制

APNS优点:杜绝了相似安卓那种为了接受通知不停在后台唤醒程序保持长链接的行为,由iOS系统和APNS进行长链接替代。 APNS的原理: 1). 应用在通知中心注册,由iOS系统向APNS请求返回设备令牌(device Token) 2). 应用程序接收到设备令牌并发送给本身的后台服务器 3). 服务器把要推送的内容和设备发送给APNS 4). APNS根据设备令牌找到设备,再由iOS根据APPID把推送内容展现

第三方框架

AFNetworking 底层原理分析

AFNetworking主要是对NSURLSession和NSURLConnection(iOS9.0废弃)的封装,其中主要有如下类: 1). AFHTTPRequestOperationManager:内部封装的是 NSURLConnection, 负责发送网络请求, 使用最多的一个类。(3.0废弃) 2). AFHTTPSessionManager:内部封装是 NSURLSession, 负责发送网络请求,使用最多的一个类。 3). AFNetworkReachabilityManager:实时监测网络状态的工具类。当前的网络环境发生改变以后,这个工具类就能够检测到。 4). AFSecurityPolicy:网络安全的工具类, 主要是针对 HTTPS 服务。

5). AFURLRequestSerialization:序列化工具类,基类。上传的数据转换成JSON格式 (AFJSONRequestSerializer).使用很少。 6). AFURLResponseSerialization:反序列化工具类;基类.使用比较多: 7). AFJSONResponseSerializer; JSON解析器,默认的解析器. 8). AFHTTPResponseSerializer; 万能解析器; JSON和XML以外的数据类型,直接返回二进 制数据.对服务器返回的数据不作任何处理. 9). AFXMLParserResponseSerializer; XML解析器;

描述下SDWebImage里面给UIImageView加载图片的逻辑

SDWebImage 中为 UIImageView 提供了一个分类UIImageView+WebCache.h, 这个分类中有一个最经常使用的接口sd_setImageWithURL:placeholderImage:,会在真实图片出现前会先显示占位图片,当真实图片被加载出来后再替换占位图片。

加载图片的过程大体以下: 1.首先会在 SDWebImageCache 中寻找图片是否有对应的缓存, 它会以url 做为数据的索引先在内存中寻找是否有对应的缓存 2.若是缓存未找到就会利用经过MD5处理过的key来继续在磁盘中查询对应的数据, 若是找到了, 就会把磁盘中的数据加载到内存中,并将图片显示出来 3.若是在内存和磁盘缓存中都没有找到,就会向远程服务器发送请求,开始下载图片 4.下载后的图片会加入缓存中,并写入磁盘中 5.整个获取图片的过程都是在子线程中执行,获取到图片后回到主线程将图片显示出来

SDWebImage原理: 调用类别的方法: 1. 从内存(字典)中找图片(当这个图片在本次使用程序的过程当中已经被加载过),找到直接使用。 2. 从沙盒中找(当这个图片在以前使用程序的过程当中被加载过),找到使用,缓存到内存中。 3. 从网络上获取,使用,缓存到内存,缓存到沙盒。

友盟统计接口统计的全部功能

APP启动速度,APP停留页面时间等

算法

求最大公约数

/** 1.直接遍历法 / int maxCommonDivisor(int a, int b) {     int max = 0;     for (int i = 1; i <=b; i++) {         if (a % i == 0 && b % i == 0) {             max = i;         }     }     return max; } /* 2.展转相除法 */ int maxCommonDivisor(int a, int b) {     int r;     while(a % b > 0) {         r = a % b;         a = b;         b = r;     }     return b; }

// 扩展:最小公倍数 = (a * b)/最大公约数

模拟栈操做

/**  *  栈是一种数据结构,特色:先进后出  *  练习:使用全局变量模拟栈的操做  */ #include <stdio.h> #include <stdbool.h> #include <assert.h> //保护全局变量:在全局变量前加static后,这个全局变量就只能在本文件中使用 static int data[1024];//栈最多能保存1024个数据 static int count = 0;//目前已经放了多少个数(至关于栈顶位置)

//数据入栈 push void push(int x){ assert(!full());//防止数组越界 data[count++] = x; } //数据出栈 pop int pop(){ assert(!empty()); return data[--count]; } //查看栈顶元素 top int top(){ assert(!empty()); return data[count-1]; }

//查询栈满 full bool full() { if(count >= 1024) { return 1; } return 0; }

//查询栈空 empty bool empty() { if(count <= 0) { return 1; }     return 0; }

int main(){     //入栈     for (int i = 1; i <= 10; i++) {         push(i);     }

//出栈     while(!empty()){         printf("%d ", top()); //栈顶元素         pop(); //出栈     }     printf("\n");          return 0; }

排序算法

选择排序、冒泡排序、插入排序三种排序算法能够总结为以下:

/**

  • 【选择排序】:最值出如今起始端
  • 第1趟:在n个数中找到最小(大)数与第一个数交换位置
  • 第2趟:在剩下n-1个数中找到最小(大)数与第二个数交换位置
  • 重复这样的操做...依次与第三个、第四个...数交换位置
  • 第n-1趟,最终可实现数据的升序(降序)排列。

*/ void selectSort(int *arr, int length) {     for (int i = 0; i < length - 1; i++) { //趟数         for (int j = i + 1; j < length; j++) { //比较次数             if (arr[i] > arr[j]) {                 int temp = arr[i];                 arr[i] = arr[j];                 arr[j] = temp;             }         }     } }

/**

  • 【冒泡排序】:相邻元素两两比较,比较完一趟,最值出如今末尾
  • 第1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推动,最值最后出如今第n个元素位置
  • 第2趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推动,最值最后出如今第n-1个元素位置
  • …… ……
  • 第n-1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推动,最值最后出如今第2个元素位置 */ void bublleSort(int *arr, int length) {     for(int i = 0; i < length - 1; i++) { //趟数         for(int j = 0; j < length - i - 1; j++) { //比较次数             if(arr[j] > arr[j+1]) {                 int temp = arr[j];                 arr[j] = arr[j+1];                 arr[j+1] = temp;             }         }     } }

都将数组分为已排序部分和未排序部分。

  1. 选择排序将已排序部分定义在左端,而后选择未排序部分的最小元素和未排序部分的第一个元素交换。
  2. 冒泡排序将已排序部分定义在右端,在遍历未排序部分的过程执行交换,将最大元素交换到最右端。
  3. 插入排序将已排序部分定义在左端,将未排序部分元的第一个元素插入到已排序部分合适的位置。

选择排序

冒泡排序

折半查找(二分查找)

/**

  • 折半查找:优化查找时间(不用遍历所有数据)
  • 折半查找的原理:
  • 1> 数组必须是有序的
  • 2> 必须已知min和max(知道范围)
  • 3> 动态计算mid的值,取出mid对应的值进行比较
  • 4> 若是mid对应的值大于要查找的值,那么max要变小为mid-1
  • 5> 若是mid对应的值小于要查找的值,那么min要变大为mid+1

*/

// 已知一个有序数组, 和一个key, 要求从数组中找到key对应的索引位置 int findKey(int *arr, int length, int key) {     int min = 0, max = length - 1, mid;     while (min <= max) {         mid = (min + max) / 2; //计算中间值         if (key > arr[mid]) {             min = mid + 1;         } else if (key < arr[mid]) {             max = mid - 1;         } else {             return mid;         }     }     return -1; }

编码格式(优化细节)

在 Objective-C 中,enum 建议使用 NS_ENUM 和 NS_OPTIONS 宏来定义枚举类型。

//定义一个枚举(比较严密) typedef NS_ENUM(NSInteger, BRUserGender) { BRUserGenderUnknown, // 未知 BRUserGenderMale, // 男性 BRUserGenderFemale, // 女性 BRUserGenderNeuter // 无性 };

@interface BRUser : NSObject

@property (nonatomic, readonly, copy) NSString *name; @property (nonatomic, readonly, assign) NSUInteger age; @property (nonatomic, readonly, assign) BRUserGender gender;

  • (instancetype)initWithName:(NSString *)name age:(NSUInteger)age gender:(BRUserGender)gender;

@end

//说明: //既然该类中已经有一个“初始化方法” ,用于设置 name、age 和 gender 的初始值: 那么在设计对应 @property 时就应该尽可能使用不可变的对象:其三个属性都应该设为“只读”。用初始化方法设置好属性值以后,就不能再改变了。 //属性的参数应该按照下面的顺序排列: (原子性,读写,内存管理)

避免使用C语言中的基本数据类型,建议使用 Foundation 数据类型,对应关系以下:

int -> NSInteger unsigned -> NSUInteger float -> CGFloat 动画时间 -> NSTimeInterval

其它知识点

一.什么是 OpenGL、Quartz 2D?

Quatarz 2d 是Apple提供的基本图形工具库。只是适用于2D图形的绘制。 OpenGL,是一个跨平台的图形开发库。适用于2D和3D图形的绘制。

ffmpeg框架:​ffmpeg 是音视频处理工具,既有音视频编码解码功能,又能够做为播放器使用。

谈谈 UITableView 的优化

1). 正确的复用cell。 2). 设计统一规格的Cell 3). 提早计算并缓存好高度(布局),由于heightForRowAtIndexPath:是调用最频繁的方法; 4). 异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口; 4). 滑动时按需加载,这个在大量图片展现,网络加载的时候很管用! 5). 减小子视图的层级关系 6). 尽可能使全部的视图不透明化以及作切圆操做。 7). 不要动态的add 或者 remove 子控件。最好在初始化时就添加完,而后经过hidden来控制是否显示。 8). 使用调试工具分析问题。

二如何实行cell的动态的行高

若是但愿每条数据显示自身的行高,必须设置两个属性,1.预估行高,2.自定义行高。 设置预估行高 tableView.estimatedRowHeight = 200。 设置定义行高 tableView.estimatedRowHeight = UITableViewAutomaticDimension。 若是要让自定义行高有效,必须让容器视图有一个自下而上的约束。

三说说你对 block 的理解

栈上的自动复制到堆上,block 的属性修饰符是 copy,循环引用的原理和解决方案。

四说说你对 runtime 的理解

主要是方法调用时如何查找缓存,如何找到方法,找不到方法时怎么转发,对象的内存布局。

五什么是野指针、空指针?

野指针:不知道指向了哪里的指针叫野指针。即指针指向不肯定,指针存的地址是一个垃圾值,未初始化。 空指针:不指向任何位置的指针叫空指针。即指针没有指向,指针存的地址是一个空地址,NULL。

六什么是 OOA / OOD / OOP ?

OOA(Object Oriented Analysis)   --面向对象分析 OOD(Object Oriented Design)     --面向对象设计 OOP(Object Oriented Programming)--面向对象编程

七 多线程是什么

多线程是个复杂的概念,按字面意思是同步完成多项任务,提升了资源的使用效率,从硬件、操做系统、应用软件不一样的角度去看,多线程被赋予不一样的内涵,对于硬件,如今市面上多数的CPU都是多核的,多核的CPU运算多线程更为出色;从操做系统角度,是多任务,如今用的主流操做系统都是多任务的,能够一边听歌、一边写博客;对于应用来讲,多线程可让应用有更快的回应,能够在网络下载时,同时响应用户的触摸操做。在iOS应用中,对多线程最初的理解,就是并发,它的含义是原来先作烧水,再摘菜,再炒菜的工做,会变成烧水的同时去摘菜,最后去炒菜。

八. iOS 中的多线程

iOS中的多线程,是Cocoa框架下的多线程,经过Cocoa的封装,可让咱们更为方便的使用线程,作过C++的同窗可能会对线程有更多的理解,好比线程的创立,信号量、共享变量有认识,Cocoa框架下会方便不少,它对线程作了封装,有些封装,可让咱们建立的对象,自己便拥有线程,也就是线程的对象化抽象,从而减小咱们的工程,提供程序的健壮性。

GCD是(Grand Central Dispatch)的缩写 ,从系统级别提供的一个易用地多线程类库,具备运行时的特色,能充分利用多核心硬件。GCD的API接口为C语言的函数,函数参数中多数有Block,关于Block的使用参看这里,为咱们提供强大的“接口”,对于GCD的使用参见本文

NSOperation与Queue

NSOperation是一个抽象类,它封装了线程的细节实现,咱们能够经过子类化该对象,加上NSQueue来同面向对象的思惟,管理多线程程序。具体可参看这里:一个基于NSOperation的多线程网络访问的项目。

NSThread

NSThread是一个控制线程执行的对象,它不如NSOperation抽象,经过它咱们能够方便的获得一个线程,并控制它。但NSThread的线程之间的并发控制,是须要咱们本身来控制的,能够经过NSCondition实现。

参看 iOS多线程编程之NSThread的使用

其余多线程

在Cocoa的框架下,通知、Timer和异步函数等都有使用多线程,(待补充).

九. 在项目何时选择使用GCD,何时选择NSOperation?

项目中使用NSOperation的优势是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具备面向对象的优势(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。

项目中使用GCD的优势是GCD自己很是简单、易用,对于不复杂的多线程操做,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用。

十 KVO,NSNotification,delegate及block区别

十一 将一个函数在主线程执行的4种方法

dispatch_async(dispatch_get_main_queue(), ^{

//须要执行的方法

});

NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];  //主队列

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{

//须要执行的方法

}];

[mainQueue addOperation:operation];

[self performSelector:@selector(method) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES modes:nil];

[self performSelectorOnMainThread:@selector(method) withObject:nil waitUntilDone:YES];

[[NSThread mainThread] performSelector:@selector(method) withObject:nil]; [[NSRunLoop mainRunLoop] performSelector:@selector(method) withObject:nil];

十二 如何让计时器调用一个类方法

十三 如何重写类方法

十四 NSTimer建立后,会在哪一个线程运行。

十五 id和NSObject*的区别

typedef struct objc_object *id

十六.ios开发逆向传值的几种方法整理

第一种:代理传值

第二个控制器:

@protocol WJSecondViewControllerDelegate

  • (void)changeText:(NSString*)text; @end @property(nonatomic,assign)iddelegate;

  • (IBAction)buttonClick:(UIButton*)sender { _str = sender.titleLabel.text; [self.delegate changeText:sender.titleLabel.text]; [self.navigationController popViewControllerAnimated:YES]; } 第一个控制器:

  • (IBAction)pushToSecond:(id)sender { WJSecondViewController *svc = [[WJSecondViewController alloc]initWithNibName:@"WJSecondViewController" bundle:nil]; svc.delegate = self; svc.str = self.navigationItem.title; [self.navigationController pushViewController:svc animated:YES]; [svc release]; }

  • (void)changeText:(NSString *)text{ self.navigationItem.title = text; } 第二种:通知传值

第一个控制器:

//注册监听通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(limitDataForModel:) name:@"NOV" object:nil];

  • (void)limitDataForModel:(NSNotification *)noti{ self.gamesInfoArray = noti.object; } 第二个控制器:

//发送通知 [[NSNotificationCenter defaultCenter] postNotificationName:@"NOV" object:gameArray]; 第三种:单例传值

Single是一个单例类,而且有一个字符串类型的属性titleName

在第二个控制器:

  • (IBAction)buttonClick:(UIButton*)sender { Single *single = [Single sharedSingle]; single.titleName = sender.titleLabel.text; [self.navigationController popViewControllerAnimated:YES]; } 第一个控制器:

  • (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; Single *single = [Single sharedSingle]; self.navigationItem.title = single.titleName; } 第四种:block传值

第二个控制器:

@property (nonatomic,copy) void (^changeText_block)(NSString*);

  • (IBAction)buttonClick:(UIButton*)sender { _str = sender.titleLabel.text; self.changeText_block(sender.titleLabel.text); [self.navigationController popViewControllerAnimated:YES]; } 第一个控制器:

  • (IBAction)pushToSecond:(id)sender { WJSecondViewController *svc = [[WJSecondViewController alloc]initWithNibName:@"WJSecondViewController" bundle:nil]; svc.str = self.navigationItem.title; [svc setChangeText_block:^(NSString *str) {

    self.navigationItem.title = str; }]; [self.navigationController pushViewController:svc animated:YES]; } 第五种:extern传值

第二个控制器:

extern NSString *btn;

  • (IBAction)buttonClick:(UIButton*)sender { btn = sender.titleLabel.text; [self.navigationController popViewControllerAnimated:YES]; } 第一个控制器:

NSString *btn = nil;

  • (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; self.navigationItem.title = btn; } 第六种:KVO传值

第一个控制器:

  • (void)viewDidLoad { [super viewDidLoad]; _vc =[[SecondViewController alloc]init]; //self监听vc里的textValue属性 [_vc addObserver:self forKeyPath:@"textValue" options:0 context:nil];
    } 第二个控制器:

  • (IBAction)buttonClicked:(id)sender { self.textValue = self.textField.text; [self.navigationController popViewControllerAnimated:YES]; }

Method1:performSelector

[self performSelector:@selector(delayMethod) withObject:nil/可传任意类型参数/ afterDelay:2.0];

注:此方法是一种非阻塞的执行方式,未找到取消执行的方法。

Method2:NSTimer定时器

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(delayMethod) userInfo:nil repeats:NO];

注:此方法是一种非阻塞的执行方式,

Method3:NSThread线程的sleep

[NSThread sleepForTimeInterval:2.0];

注:此方法是一种阻塞执行方式,建议放在子线程中执行,不然会卡住界面。但有时仍是须要阻塞执行,如进入欢迎界面须要沉睡3秒才进入主界面时。

没有找到取消执行方式。

Method4:GCD

__block ViewController/主控制器/ *weakSelf = self;

dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0/延迟执行时间/ * NSEC_PER_SEC));

dispatch_after(delayTime, dispatch_get_main_queue(), ^{ [weakSelf delayMethod]; });`

十七.NSPersistentStoreCoordinator  ,   NSManaged0bjectContext 和NSManaged0bject中的那些须要在线程中建立或者传递

答:NSPersistentStoreCoordinator是持久化存储协调者,主要用于协调托管对象上下文和持久化存储区之间的关系。NSManagedObjectContext使用协调者的托管对象模型将数据保存到数据库,或查询数据。

十八.您是否作过一部的网络处理和通信方面的工做?若是有,能具体介绍一下实现策略么

答:使用NSOperation发送异步网络请求,使用NSOperationQueue管理线程数目及优先级,底层是用NSURLConnetion,

81.你使用过Objective-C的运行时编程(Runtime Programming)么?若是使用过,你用它作了什么?你还能记得你所使用的相关的头文件或者某些方法的名称吗?

答:Objecitve-C的重要特性是Runtime(运行时),在#import <objc/runtime.h> 下能看到相关的方法,用过objc_getClass()和class_copyMethodList()获取过私有API;使用

Method method1 = class_getInstanceMethod(cls, sel1);

Method method2 = class_getInstanceMethod(cls, sel2);

method_exchangeImplementations(method1, method2);  

```   

代码交换两个方法,在写unit test时使用到。

 

82.Core开头的系列的内容。是否使用过CoreAnimation和CoreGraphics。UI框架和CA,CG框架的联系是什么?分别用CA和CG作过些什么动画或者图像上的内容。(有须要的话还能够涉及Quartz的一些内容)

 

答:UI框架的底层有CoreAnimation,CoreAnimation的底层有CoreGraphics。    

UIKit | 

------------ | 

Core Animation | 

Core Graphics |

Graphics Hardware|  

使用CA作过menu菜单的展开收起(太逊了)  

 

十九.是否使用过CoreText或者CoreImage等?若是使用过,请谈谈你使用CoreText或者CoreImage的体验。

 

答:CoreText能够解决复杂文字内容排版问题。CoreImage能够处理图片,为其添加各类效果。体验是很强大,挺复杂的。

 

二十.NSNotification和KVO的区别和用法是什么?何时应该使用通知,何时应该使用KVO,它们的实现上有什么区别吗?若是用protocol和delegate(或者delegate的Array)来实现相似的功能可能吗?若是可能,会有什么潜在的问题?若是不能,为何?(虽然protocol和delegate这种东西面试已经面烂了…)

 

答:NSNotification是通知模式在iOS的实现,KVO的全称是键值观察(Key-value observing),其是基于KVC(key-value coding)的,KVC是一个经过属性名访问属性变量的机制。例如将Module层的变化,通知到多个Controller对象时,能够使用NSNotification;若是是只须要观察某个对象的某个属性,能够使用KVO。

对于委托模式,在设计模式中是对象适配器模式,其是delegate是指向某个对象的,这是一对一的关系,而在通知模式中,每每是一对多的关系。委托模式,从技术上能够如今改变delegate指向的对象,但不建议这样作,会让人迷惑,若是一个delegate对象不断改变,指向不一样的对象。



二十一.你用过NSOperationQueue么?若是用过或者了解的话,你为何要使用NSOperationQueue,实现了什么?请描述它和G.C.D的区别和相似的地方(提示:能够从二者的实现机制和适用范围来描述)。

 

答:使用NSOperationQueue用来管理子类化的NSOperation对象,控制其线程并发数目。GCD和NSOperation均可以实现对线程的管理,区别是 NSOperation和NSOperationQueue是多线程的面向对象抽象。项目中使用NSOperation的优势是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具备面向对象的优势(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。

项目中使用GCD的优势是GCD自己很是简单、易用,对于不复杂的多线程操做,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用。

 

二十二.既然提到G.C.D,那么问一下在使用G.C.D以及block时要注意些什么?它们两是一回事儿么?block在ARC中和传统的MRC中的行为和用法有没有什么区别,须要注意些什么?

 

答:使用block是要注意,若将block作函数参数时,须要把它放到最后,GCD是Grand Central Dispatch,是一个对线程开源类库,而Block是闭包,是可以读取其余函数内部变量的函数。

 

 

二十三. 对于Objective-C,你认为它最大的优势和最大的不足是什么?对于不足之处,如今有没有可用的方法绕过这些不足来实现需求。若是能够的话,你有没有考虑或者实践太重新实现OC的一些功能,若是有,具体会如何作?

 

答:最大的优势是它的运行时特性,不足是没有命名空间,对于命名冲突,能够使用长命名法或特殊前缀解决,若是是引入的第三方库之间的命名冲突,能够使用link命令及flag解决冲突。

  
 

二十四. 你实现过一个框架或者库以供别人使用么?若是有,请谈一谈构建框架或者库时候的经验;若是没有,请设想和设计框架的public的API,并指出大概须要如何作、须要注意一些什么方面,来使别人容易地使用你的框架。

答:抽象和封装,方便使用。首先是对问题有充分的了解,好比构建一个文件解压压缩框架,从使用者的角度出发,只需关注发送给框架一个解压请求,框架完成复杂文件的解压操做,而且在适当的时候通知给是哦难过者,如解压完成、解压出错等。在框架内部去构建对象的关系,经过抽象让其更为健壮、便于更改。其次是API的说明文档。

KVO就是cocoa框架实现的观察者模式,通常同KVC搭配使用,经过KVO能够监测一个值的变化,好比View的高度变化。是一对多的关系,一个值的变化会通知全部的观察者。

NSNotification是通知,也是一对多的使用场景。在某些状况下,KVO和NSNotification是同样的,都是状态变化以后告知对方。NSNotification的特色,就是须要被观察者先主动发出通知,而后观察者注册监听后再来进行响应,比KVO多了发送通知的一步,可是其优势是监听不局限于属性的变化,还能够对多种多样的状态变化进行监听,监听范围广,使用也更灵活。

delegate 是代理,就是我不想作的事情交给别人作。好比狗须要吃饭,就经过delegate通知主人,主人就会给他作饭、盛饭、倒水,这些操做,这些狗都不须要关心,只须要调用delegate(代理人)就能够了,由其余类完成所须要的操做。因此delegate是一对一关系。

block是delegate的另外一种形式,是函数式编程的一种形式。使用场景跟delegate同样,相比delegate更灵活,并且代理的实现更直观。

KVO通常的使用场景是数据,需求是数据变化,好比股票价格变化,咱们通常使用KVO(观察者模式)。delegate通常的使用场景是行为,需求是须要别人帮我作一件事情,好比买卖股票,咱们通常使用delegate。

Notification通常是进行全局通知,好比利好消息一出,通知你们去买入。delegate是强关联,就是委托和代理双方互相知道,你委托别人买股票你就须要知道经纪人,经纪人也不要知道本身的顾客。Notification是弱关联,利好消息发出,你不须要知道是谁发的也能够作出相应的反应,同理发消息的人也不须要知道接收的人也能够正常发出消息。

GCD方法,经过向主线程队列发送一个block块,使block里的方法能够在主线程中执行。

NSOperation 方法

NSThread 方法

RunLoop方法

计时器只能调用实例方法,可是能够在这个实例方法里面调用静态方法。

使用计时器须要注意,计时器必定要加入RunLoop中,而且选好model才能运行。scheduledTimerWithTimeInterval方法建立一个计时器并加入到RunLoop中因此能够直接使用。

若是计时器的repeats选择YES说明这个计时器会重复执行,必定要在合适的时机调用计时器的invalid。不能在dealloc中调用,由于一旦设置为repeats 为yes,计时器会强持有self,致使dealloc永远不会被调用,这个类就永远没法被释放。好比能够在viewDidDisappear中调用,这样当类须要被回收的时候就能够正常进入dealloc中了。

在子类中实现一个同基类名字同样的静态方法

在调用的时候不要使用类名调用,而是使用[self class]的方式调用。原理,用类名调用是早绑定,在编译期绑定,用[self class]是晚绑定,在运行时决定调用哪一个方法。

用scheduledTimerWithTimeInterval建立的,在哪一个线程建立就会被加入哪一个线程的RunLoop中就运行在哪一个线程

本身建立的Timer,加入到哪一个线程的RunLoop中就运行在哪一个线程。

id是一个 objc_object 结构体指针,定义是

id能够理解为指向对象的指针。全部oc的对象 id均可以指向,编译器不会作类型检查,id调用任何存在的方法都不会在编译阶段报错,固然若是这个id指向的对象没有这个方法,该崩溃仍是会崩溃的。

NSObject *指向的必须是NSObject的子类,调用的也只能是NSObjec里面的方法不然就要作强制类型转换。

不是全部的OC对象都是NSObject的子类,还有一些继承自NSProxy。NSObject *可指向的类型是id的子集。

 

 

 

二十五.说说你理解weak属性?

weak实现原理:

Runtime维护了一个weak表,用于存储指向某个对象的全部weak指针。weak表实际上是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象的地址)数组。

一、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。

二、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的做用是更新指针指向,建立对应的弱引用表。

三、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取全部weak指针地址的数组,而后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

 

 

 

追问的问题一:

1.实现weak后,为何对象释放后会自动为nil?

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

 

追问的问题二:

2.当weak引用指向的对象被释放时,又是如何去处理weak指针的呢?

一、调用objc_release

二、由于对象的引用计数为0,因此执行dealloc

三、在dealloc中,调用了_objc_rootDealloc函数

四、在_objc_rootDealloc中,调用了object_dispose函数

五、调用objc_destructInstance

六、最后调用objc_clear_deallocating,详细过程以下:

a. 从weak表中获取废弃对象的地址为键值的记录

b. 将包含在记录中的全部附有 weak修饰符变量的地址,赋值为 nil

c. 将weak表中该记录删除

d. 从引用计数表中删除废弃对象的地址为键值的记录

 

二十六.假如Controller太臃肿,如何优化?

1.将网络请求抽象到单独的类中

方便在基类中处理公共逻辑;

方便在基类中处理缓存逻辑,以及其它一些公共逻辑;

方便作对象的持久化。

2.将界面的封装抽象到专门的类中

构造专门的 UIView 的子类,来负责这些控件的拼装。这是最完全和优雅的方式,不过稍微麻烦一些的是,你须要把这些控件的事件回调先接管,再都一一暴露回 Controller。

3.构造 ViewModel

借鉴MVVM。具体作法就是将 ViewController 给 View 传递数据这个过程,抽象成构造 ViewModel 的过程。

4.专门构造存储类

专门来处理本地数据的存取。

5.整合常量

二十七.项目中网络层如何作安全处理?

一、尽可能使用https

https能够过滤掉大部分的安全问题。https在证书申请,服务器配置,性能优化,客户端配置上都须要投入精力,因此缺少安全意识的开发人员容易跳过https,或者拖到之后遇到问题再优化。https除了性能优化麻烦一些之外其余都比想象中的简单,若是没精力优化性能,至少在注册登陆模块须要启用https,这部分业务对性能要求比较低。

二、不要传输明文密码

不知道如今还有多少app后台是明文存储密码的。不管客户端,server仍是网络传输都要避免明文密码,要使用hash值。客户端不要作任何密码相关的存储,hash值也不行。存储token进行下一次的认证,并且token须要设置有效期,使用refresh

token去申请新的token。

三、Post并不比Get安全

事实上,Post和Get同样不安全,都是明文。参数放在QueryString或者Body没任何安全上的差异。在Http的环境下,使用Post或者Get都须要作加密和签名处理。

四、不要使用301跳转

301跳转很容易被Http劫持攻击。移动端http使用301比桌面端更危险,用户看不到浏览器地址,没法察觉到被重定向到了其余地址。若是必定要使用,确保跳转发生在https的环境下,并且https作了证书绑定校验。

五、http请求都带上MAC

全部客户端发出的请求,不管是查询仍是写操做,都带上MAC(Message Authentication

Code)。MAC不但能保证请求没有被篡改(Integrity),还能保证请求确实来自你的合法客户端(Signing)。固然前提是你客户端的key没有被泄漏,如何保证客户端key的安全是另外一个话题。MAC值的计算能够简单的处理为hash(request

params+key)。带上MAC以后,服务器就能够过滤掉绝大部分的非法请求。MAC虽然带有签名的功能,和RSA证书的电子签名方式却不同,缘由是MAC签名和签名验证使用的是同一个key,而RSA是使用私钥签名,公钥验证,MAC的签名并不具有法律效应。

六、http请求使用临时密钥

高延迟的网络环境下,不经优化https的体验确实会明显不如http。在不具有https条件或对网络性能要求较高且缺少https优化经验的场景下,http的流量也应该使用AES进行加密。AES的密钥能够由客户端来临时生成,不过这个临时的AES

key须要使用服务器的公钥进行加密,确保只有本身的服务器才能解开这个请求的信息,固然服务器的response也须要使用一样的AES

key进行加密。因为http的应用场景都是由客户端发起,服务器响应,因此这种由客户端单方生成密钥的方式能够必定程度上便捷的保证通讯安全。

七、AES使用CBC模式

不要使用ECB模式,记得设置初始化向量,每一个block加密以前要和上个block的秘文进行运算。

二十八.main()以前的过程有哪些?

一、main以前的加载过程

1)dyld 开始将程序二进制文件初始化

2)交由ImageLoader 读取 image,其中包含了咱们的类,方法等各类符号(Class、Protocol 、Selector、 IMP)

3)因为runtime 向dyld 绑定了回调,当image加载到内存后,dyld会通知runtime进行处理

4)runtime 接手后调用map_images作解析和处理

5)接下来load_images 中调用call_load_methods方法,遍历全部加载进来的Class,按继承层次依次调用Class的+load和其余Category的+load方法

6)至此 全部的信息都被加载到内存中

7)最后dyld调用真正的main函数



一、Runtime相关面试问题

Runtime是什么?见名知意,其概念无非就是“由于 Objective-C 是一门动态语言,因此它须要一个运行时系统……这就是 Runtime 系统”云云。对博主这种菜鸟而言,Runtime 在实际开发中,其实就是一组C语言的函数。胡适说:“多研究些问题,少谈些主义”,云山雾罩的概念听多了老是容易头晕,接下来咱们直接上runtime思惟导图帮助你们理清思路:

runtime思惟导图

![iOS开发之家](https://upload-images.jianshu.io/upload_images/16555213-4213ced28da70859.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


二、多线程相关面试问题

多线程是一个比较轻量级的方法来实现单个应用程序内多个代码执行路径, 从技术角度来看,一个线程就是一个须要管理执行代码的内核级和应用级数据结 构组合。

多线程思惟导图

![iOS开发之家](https://upload-images.jianshu.io/upload_images/16555213-7ceba091aa775dd8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


三、RunLoop相关面试问题

我相信大多数开发者同样,迷惑于runloop,最初只了解能够经过runloop一些监听事件的通知来作一些事情,优化性能。关于runloop源码的基础知识,能够参考下面的思惟导图:

runloop思惟导图

![iOS开发之家](https://upload-images.jianshu.io/upload_images/16555213-262968ddfe8be488.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


四、设计模式相关面试问题

设计模式(Design pattern)是一套被反复使用、多数人知晓的、通过分类编目的、代码设计经验的总结。
使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的;模式使代码编制真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构同样。

设计模式思惟导图

![iOS开发之家](https://upload-images.jianshu.io/upload_images/16555213-08e9b4d446eeff37.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


五、架构/框架相关面试问题

“100个读者就有100个哈姆雷特”同样,对于架构的理解不一样的软件工程师有不一样的见解。架构设计每每是一个权衡的过程,每个架构设计者都要考虑到各个因素,好比团队成员的技术水平、具体的业务场景、项目的成长阶段和开发周期。下图是小编的一些架构理念,仅供参考:

架构/框架思惟导图

![iOS开发之家](https://upload-images.jianshu.io/upload_images/16555213-aba4b745be5433bb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


六、算法相关面试问题

算法思惟导图

![iOS开发之家](https://upload-images.jianshu.io/upload_images/16555213-ab6cc63bd9c5d5b5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


七、第三方库相关面试问题

![iOS开发之家](https://upload-images.jianshu.io/upload_images/16555213-34a780c1046ac641.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)





复制代码
相关文章
相关标签/搜索