设计模式是什么? 你知道哪些设计模式,并简要叙述?
设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型的事情。
- MVC模式:Model View Control,把模型 视图 控制器 层进行解耦合编写。
- MVVM模式:Model View ViewModel 把模型 视图 业务逻辑 层进行解耦和编写。
- 单例模式:通过static关键词,声明全局变量。在整个进程运行期间只会被赋值一次。
- 观察者模式:KVO是典型的通知模式,观察某个属性的状态,状态发生变化时通知观察者。
- 委托模式:代理+协议的组合。实现1对1的反向传值操作。
- 工厂模式:通过一个类方法,批量的根据已有模板生产对象。
MVC 和 MVVM 的区别
- MVVM是对胖模型进行的拆分,其本质是给控制器减负,将一些弱业务逻辑放到VM中去处理。
- MVC是一切设计的基础,所有新的设计模式都是基于MVC进行的改进。
frame 和 bounds 有什么不同?
frame指的是:该view在父view坐标系统中的位置和大小。(参照点是父view的坐标系统)
bounds指的是:该view在本身坐标系统中的位置和大小。(参照点是本身坐标系统)
Objective-C 中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么?
线程创建有三种方法:
- 使用NSThread创建
- 使用GCD的dispatch
- 使用子类化的NSOperation,然后将其加入NSOperationQueue;在主线程执行代码,方法是performSelectorOnMainThread,如果想延时执行代码可以performSelector:onThread:withObject:waitUntilDone:
Category(类别)、 Extension(扩展)和继承的区别
区别:
- 分类有名字,类扩展没有分类名字,是一种特殊的分类。
- 分类只能扩展方法(属性仅仅是声明,并没真正实现),类扩展可以扩展属性、成员变量和方法。
- 继承可以增加,修改或者删除方法,并且可以增加属性。
如何对iOS设备进行性能测试?
- Profile-> Instruments ->Time Profiler
开发项目时你是怎么检查内存泄露?
- 静态分析 analyze。
- instruments工具里面有个leak可以动态分析。
什么是block?
闭包(block):闭包就是获取其它函数局部变量的匿名函数。
- block反向传值
- 在控制器间传值可以使用代理或者block,使用block相对来说简洁。
block的注意点
- 在block内部使用外部指针且会造成循环引用情况下,需要用__week修饰外部指针: __weak typeof(self) weakSelf = self;
- 在block内部如果调用了延时函数还使用弱指针会取不到该指针,因为已经被销毁了,需要在block内部再将弱指针重新强引用一下。 __strong typeof(self) strongSelf = weakSelf;
- 如果需要在block内部改变外部栈区变量的话,需要在用__block修饰外部变量。
你一般是怎么用Instruments的?
- Time Profiler: 性能分析
- Zombies:检查是否访问了僵尸对象,但是这个工具只能从上往下检查,不智能。
- Allocations:用来检查内存,写算法的那批人也用这个来检查。
- Leaks:检查内存,看是否有内存泄露。
iOS中常用的数据存储方式有哪些?
数据存储有四种方案:NSUserDefault、KeyChain、file、DB。
其中File有三种方式:plist、Archive(归档)
DB包括:SQLite、FMDB、CoreData
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(), ^{ // 合并图片 });
什么是 RunLoop
从字面上讲就是运行循环,它内部就是do-while循环,在这个循环内部不断地处理各种任务。
一个线程对应一个RunLoop,基本作用就是保持程序的持续运行,处理app中的各种事件。通过runloop,有事运行,没事就休息,可以节省cpu资源,提高程序性能。
什么是 Runtime
Runtime又叫运行时,是一套底层的C语言API,其为iOS内部的核心之一,我们平时编写的OC代码,底层都是基于它来实现的。
Runtime实现的机制是什么,怎么用,一般用于干嘛?
- 使用时需要导入的头文件
- Runtime 运行时机制,它是一套C语言库。
- 实际上我们编写的所有OC代码,最终都是转成了runtime库的东西。
比如:
类转成了 Runtime 库里面的结构体等数据类型
方法转成了 Runtime 库里面的C语言函数
平时调方法都是转成了 objc_msgSend 函数(所以说OC有个消息发送机制)
OC是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector)。
[stu show]; 在objc动态编译时,会被转意为:objc_msgSend(stu, @selector(show));
- 因此,可以说 Runtime 是OC的底层实现,是OC的幕后执行者。
有了Runtime库,能做什么事情呢?
Runtime库里面包含了跟类、成员变量、方法相关的API。
比如:
- 获取类里面的所有成员变量。
- 为类动态添加成员变量。
- 动态改变类的方法实现。
- 为类动态添加新的方法等。
因此,有了Runtime,想怎么改就怎么改。
什么是 TCP / UDP ?
- TCP:传输控制协议。
TCP 是面向连接的,建立连接需要经历三次握手,是可靠的传输层协议。 - UDP:用户数据协议。
UDP 是面向无连接的,数据传输是不可靠的,它只管发,不管收不收得到。 简单的说,TCP注重数据安全,而UDP数据传输快点,但安全性一般。
tableView的重用机制?
UITableView 通过重用单元格来达到节省内存的目的: 通过为每个单元格指定一个重用标识符,即指定了单元格的种类,当屏幕上的单元格滑出屏幕时,系统会把这个单元格添加到重用队列中,等待被重用,当有新单元格从屏幕外滑入屏幕内时,从重用队列中找看有没有可以重用的单元格,如果有,就拿过来用,如果没有就创建一个来使用。
你是怎么封装一个view的
- 可以通过纯代码或者xib的方式来封装子控件
- 建立一个跟view相关的模型,然后将模型数据传给view,通过模型上的数据给view的子控件赋值
/** * 纯代码初始化控件时一定会走这个方法 */
- (instancetype)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame])
{
[self setupUI];
}
return self;
}
/** * 通过xib初始化控件时一定会走这个方法 */
- (id)initWithCoder:(NSCoder *)aDecoder {
if(self = [super initWithCoder:aDecoder]) {
[self setupUI];
}
return self;
}
- (void)setupUI {
// 初始化代码
}
第三方框架
AFNetworking 底层原理分析
AFNetworking主要是对NSURLSession和NSURLConnection(iOS9.0废弃)的封装,其中主要有以下类:
- AFHTTPRequestOperationManager:内部封装的是 NSURLConnection, 负责发送网络请求, 使用最多的一个类。(3.0废弃)
- AFHTTPSessionManager:内部封装是 NSURLSession, 负责发送网络请求,使用最多的一个类。
- AFNetworkReachabilityManager:实时监测网络状态的工具类。当前的网络环境发生改变之后,这个工具类就可以检测到
- AFSecurityPolicy:网络安全的工具类, 主要是针对 HTTPS 服务。
- AFURLRequestSerialization:序列化工具类,基类。上传的数据转换成JSON格式 (AFJSONRequestSerializer).使用不多。
- AFURLResponseSerialization:反序列化工具类;基类.使用比较多
- AFJSONResponseSerializer; JSON解析器,默认的解析器.
- AFHTTPResponseSerializer; 万能解析器; JSON和XML之外的数据类型,直接返回二进 制数据.对服务器返回的数据不做任何处理.
- AFXMLParserResponseSerializer; XML解析器;
描述下SDWebImage里面给UIImageView加载图片的逻辑
SDWebImage 中为 UIImageView 提供了UIImageView+WebCache.h,
这个分类中有一个最常用的接口
sd_setImageWithURL:placeholderImage:会在真实图片出现前会先显示占位图片,当真实图片被加载出来后再替换占位图片。
加载图片的过程大致如下:
- 首先会在 SDWebImageCache 中寻找图片是否有对应的缓存, 它会以url 作为数据的索引先在内存中寻找是否有对应的缓存
- 如果缓存未找到就会利用通过MD5处理过的key来继续在磁盘中查询对应的数据, 如果找到了, 就会把磁盘中的数据加载到内存中,并将图片显示出来
- 如果在内存和磁盘缓存中都没有找到,就会向远程服务器发送请求,开始下载图片
- 下载后的图片会加入缓存中,并写入磁盘中
- 整个获取图片的过程都是在子线程中执行,获取到图片后回到主线程将图片显示出来
SDWebImage原理: 调用类别的方法: 1. 从内存(字典)中找图片(当这个图片在本次使用程序的过程中已经被加载过),找到直接使用。 2. 从沙盒中找(当这个图片在之前使用程序的过程中被加载过),找到使用,缓存到内存中。 3. 从网络上获取,使用,缓存到内存,缓存到沙盒。
编码格式(优化细节)
在 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
避免使用C语言中的基本数据类型,建议使用 Foundation 数据类型
对应关系如下:
int -> NSInteger unsigned -> NSUInteger float -> CGFloat 动画时间 -> NSTimeInterval
如何实行cell的动态的行高
如果希望每条数据显示自身的行高,必须设置两个属性,
- 预估行高
设置预估行高 tableView.estimatedRowHeight = 200。 - 自定义行高
设置定义行高 tableView.estimatedRowHeight = UITableViewAutomaticDimension。
如果要让自定义行高有效,必须让容器视图有一个自下而上的约束。
用StoryBoard开发界面有什么弊端?如何避免?
使用简单逻辑页面的跳转是可以使用sb的,开发比较块。但是SB对于逻辑项目比较复杂的时候,开发起来比较慢。
不适合多人合作开发;也不利于版本的梗系和后期的维护。
使用sb在项目变异编译的时候,也都会直接加载到内存中,造成内存的浪费。 可以使用xib来代替,编辑复杂逻辑界面时候可以使用纯码编写。
Swift
类(class)和结构体(struct)有什么区别?
Swift 中,类是引用类型,结构体是值类型。值类型在传递和赋值时将进行复制,而引用类型则只会使用引用对象的一个"指向"。所以他们两者之间的区别就是两个类型的区别。
请说明并比较以下关键词:Open, Public, Internal, File-private, Private
Swift 有五个级别的访问控制权限,从高到底依次为比如 Open, Public, Internal, File-private, Private。
基本原则:高级别的变量不允许被定义为低级别变量的成员变量。
比如一个 private 的 class 中不能含有 public 的 String。反之,低级别的变量却可以定义在高级别的变量中。比如 public 的 class 中可以含有 private 的 Int。
- Open 具备最高的访问权限。其修饰的类和方法可以在任意 Module 中被访问和重写;它是 Swift 3 中新添加的访问权限。
- Public 的权限仅次于 Open。与 Open 唯一的区别在于它修饰的对象可以在任意 Module 中被访问,但不能重写。
- Internal 是默认的权限。它表示只能在当前定义的 Module 中访问和重写,它可以被一个 Module 中的多个文件访问,但不可以被其他的 Module 中被访问。
- File-private 也是 Swift 3 新添加的权限。其被修饰的对象只能在当前文件中被使用。例如它可以被一个文件中的 class,extension,struct 共同使用。
- Private 是最低的访问权限。它的对象只能在定义的作用域内使用。离开了这个作用域,即使是同一个文件中的其他作用域,也无法访问。
请说明并比较以下关键词:strong, weak, unowned
Swift 的内存管理机制与 Objective-C一样为 ARC(Automatic Reference Counting)。
它的基本原理是,一个对象在没有任何强引用指向它时,其占用的内存会被回收。反之,只要有任何一个强引用指向该对象,它就会一直存在于内存中。
- strong 代表着强引用,是默认属性。
当一个对象被声明为 strong 时,就表示父层级对该对象有一个强引用的指向。此时该对象的引用计数会增加1。 - weak 代表着弱引用。
当对象被声明为 weak 时,父层级对此对象没有指向,该对象的引用计数不会增加1。它在对象释放后弱引用也随即消失。继续访问该对象,程序会得到 nil,不亏崩溃 - unowned 与弱引用本质上一样。
唯一不同的是,对象在释放后,依然有一个无效的引用指向对象,它不是 Optional 也不指向 nil。如果继续访问该对象,程序就会崩溃。
加分回答:
weak 和 unowned 的引入是为了解决由 strong 带来的循环引用问题。
简单来说,就是当两个对象互相有一个强指向去指向对方,这样导致两个对象在内存中无法释放。
weak 和 unowned 的使用场景有如下差别:
- 当访问对象时该对象可能已经被释放了,则用 weak。比如 delegate 的修饰。
- 当访问对象确定不可能被释放,则用 unowned。比如 self 的引用。
- 实际上为了安全起见,很多公司规定任何时候都使用 weak 去修饰。
在Swift和Objective-C的混编项目中,如何在Swift文件中调用Objective-C文件中已经定义的方法?如何在Objective-C文件中调用Swift文件中定义的方法?
- Swift中若要使用Objective-C代码,可以在ProjectName-Bridging-Header.h里添加Objective-C的头文件名称,Swift文件中即可调用相应的Objective-C代码。
一般情况Xcode会在Swift项目中第一次创建Objective-C文件时自动创建ProjectName-Bridging-Header.h文件。 - Objective-C中若要调用Swift代码,可以导入Swift生成的头函数ProjectName-Swift.h来实现。
- Swift文件中若要规定固定的方法或属性暴露给Objective-C使用,可以在方法或属性前加上@objc来声明。如果该类是NSObject子类,那么Swift会在非private的方法或属性前自动加上@objc。
实现一个函数。求一个整型二维数组中所有元素之和
func sumPairs(_ nums: [[Int]]) -> Int {
return nums.flatMap { $0 }.reduce(0) { $0 + $1 }
}
Swift 有函数式编程的思想。其中 flatMap, map, reduce, filter 是其代表的方法。
本题中考察了 flatMap 的降维思路,以及 reduce 的基本使用。相比于一般的 for 循环,这样的写法要更加得简洁漂亮。
项目
- 有已经上线的项目么?
- 项目里哪个部分是你完成的?(找一个亮点问一下如何实现的)
- 开发过程中遇到过什么困难,是如何解决的?
学习
- 遇到一个问题完全不能理解的时候,是如何帮助自己理解的?举个例子?
- 有看书的习惯么?最近看的一本是什么书?有什么心得?
- 有没有使用一些笔记软件?会在多平台同步以及多渠道采集么?(如果没有,问一下是如何复习知识的)
- 有没有使用清单类,日历类的软件?(如果没有,问一下是如何安排,计划任务的)
- 平常看博客么?有没有自己写过?(如果写,有哪些收获?如果没有写,问一下不写的原因)