基础:
1、如何令自己所写的对象具有拷贝功能?
- 实现NSCoping协议。如果自定义的对象分为可变版本与不可变版本,那么就要同时实现 NSCopying与NSMutableCopying协议。
@protocol NSCopying
- (id)copyWithZone:(nullable NSZone *)zone;
@end
@protocol NSMutableCopying
- (id)mutableCopyWithZone:(nullable NSZone *)zone;
@end
2、说说你理解weak属性?
- 当ARC发现count大于0,就不会释放资源,而weak不会使count增加,weak只是简单的把源对象的指针拿过来用,不影响ARC的release动作,在使用weak的时候要确保使用期间源对象是有效的,也就是说必须在源对象的生命周期内使用weak引用,否则的话会导致程序异常。
- 初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
- 添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数,objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
- 释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
3、UIView和CALayer是什么关系?
- UIView继承自UIResponder,能接收并响应事件,负责显示内容的管理
- CALayer继承自NSObject,不能响应事件,负责显示内容的绘制
- UIView本身更像是一个CALayer的管理器,访问它的跟绘图和跟坐标有关的属性,例如frame,bounds等,实际上内部都是在访问它所包含的CALayer的相关属性
4、@synthesize 和 @dynamic 分别有什么作用?
-
synthesize
,编译器自动生成setter和getter的方法,在你没有手动去实现这两个方法时。 -
dynamic
,告诉编译器你会动态生成setter和getter方法,不会要编译器帮你生成。
5、动态绑定
- 运行时确定要调用的方法
- 动态绑定将调用方法的确定也推迟到运行时。在编译时,方法的调用并不和代码绑定在一起,只有在消实发送出来之后,才确定被调用的代码。通过动态类型和动态绑定技术,您的代码每次执行都可以得到不同的结果。运行时因子负责确定消息的接收者和被调用的方法。运行时的消息分发机制为动态绑定提供支持。当您向一个动态类型确定了的对象发送消息时,运行环境系统会通过接收者的isa指针定位对象的类,并以此为起点确定被调用的方法,方法和消息是动态绑定的。而且,您不必在Objective-C 代码中做任何工作,就可以自动获取动态绑定的好处。您在每次发送消息时,特别是当消息的接收者是动态类型已经确定的对象时,动态绑定就会例行而透明地发生。
6、Category(类别)、 Extension(扩展)和继承的区别
-
类别(Category)
- 为已有的类添加新方法(不能添加实例变量,可以封装属性)
- 语法: @interface 已有类 (类别名)
- 习惯性将类别的文件名命名为"类名+类别名.h"
- 能将一个类的代码分散到多个文件对中
- 对类进行模块化设计
- 调用私有方法
-
扩展Extension
- 可以添加实例变量
- 语法: @interface 已有类 ()
- 相当于匿名的类别
- 将类的声明部分分散到多个文件,但实现文件只有一个
- 扩展的文件名命名为"类名_扩展名.h"
- 类别不能申明新的实例变量,扩展可以
- 类别可以将类的声明和实现部分分散到不同的文件中
- 扩展只能将类的声明部分分散到不同的文件中
- 继承必须子类化,可以增加/重写方法,可以申明新的实例变量。
7、为什么代理要用weak?代理的delegate和dataSource有什么区别?block和代理的区别?
- 防止循环引用。例如View有一个协议,需要一个代理实现回调。一个Controller添加这个View,并且遵守协议,成为View的代理。如果不用week,用strong,Controller ->View -> delegate -> Controller,就循环引用了。
- delegate偏重于与用户交互的回调,有那些方法可以供我使用,例如UITableviewDelegate;
- dataSource偏重于数据的回调,view里面有什么东西,属性都是什么,例如UITableviewDatasource;
- Block: Objective-C/Swift中对闭包(closure)的实现,广泛使用在回调上。
- Delegate: Cocoa的基本设计模式之一,面向协议(protocol)的编程,广泛使用在回调和对象间传值。
8、id和NSObject*的区别
- NSObject包含了一些其他的方法,需要实现NSObject协议,可以用NSObject来表示id,但是不能用id来表示NSObject
- id关键字在编译的时候不会被检查,而NSObject在编译的时候被被检查是否含有一些错误的方法
- id可以是任何对象,包括不是NSObject的对象
- 定义id的时候不需要*,而定义NSOject的时候需要。
9、使用系统的某些block api(如UIView的block版本写动画时),是否也考虑引用循环问题?
- 所谓“引用循环”是指双向的强引用,所以那些“单向的强引用”(block 强引用 self )没有问题。
- 但如果你使用一些参数中可能含有 ivar 的系统 api ,如 GCD 、NSNotificationCenter就要小心一点:比如GCD 内部如果引用了 self,而且 GCD 的其他参数是 ivar,则要考虑到循环引用。
10、用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
- copy 深复制,新建一个对象,并将源对象的内容复制给新对象,复制后有两个对象。
- strong 浅复制,复制对象的引用,复制后仍只有一个对象,但是有两个指针指这个对象。
- NSString,NSArray这些类拥有Mutable的子类。当你给属性赋值的时候,用的是不可变类型,其实strong和copy没有什么差别,危险的地方在于,如果用于暴露的接口,可能会被外部的变量改变。
当赋值Mutable对象时,copy会进行一次深copy,重新分配一块内存,strong是直接引用源内存。
11、static有什么作用?
- 用static声明局部变量,使其变为静态存储方式(静态数据区),作用域不变;用static声明外部变量,其本身就是静态变量,这只会改变其连接方式,使其只在本文件内部有效,而其他文件不可连接或引用该变量。
- 用static用于函数定义时,对函数的连接方式产生影响,使得函数只在本文件内部有效,对其他文件是不可见的。这样的函数又叫作静态函数。使用静态函数的好处是,不用担心与其他文件的同名函数产生干扰,另外也是对函数本身的一种保护机制。
- 函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
- 在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
- 在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
- 在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
- 在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。
12、预处理指令#include中的<>和””的区别是什么?
- 尖括号指示预处理器从系统路径开始搜索要包含的文件
- 双引号指示预处理器先从用户路径开始搜索要包含的文件,如果搜索不到再搜索系统路径
13、预处理指令#include和#import的区别是什么?
- import是Objective-C包含头文件的关键字,
- include是C/C++包含头文件的关键字;
- 使用#import,头文件会自动只包含一次,不会重复包含,可以解决头文件相互包含的问题。
14、变量有哪些种类? (5种存储类)
15、请描述静态区、栈、堆中定义的变量的区别
- 静态区中的变量在编译期就已经分配好了内存,其生命周期与应用程序生命周期相同;
- 栈中的变量都是自动存储类的变量,在程序运行的过程中动态的申请内存,其生命周期与函数栈帧的生命周期相同,其内存的申请与释放由编译器负责;
- 在堆中申请和释放内存由用户决定。没有声明在堆中的变量,往往通过声明一个指针变量来访问堆中的内存。
16、堆和栈的区别是什么?
- 所处位置:栈位于应用程序内存空间最高位处,堆与静态区高位处相邻
- 扩展方向:栈向低地址扩展,堆向高地址扩展
- 管理方式:栈由编译器自动管理,堆由程序员自行控制
- 内存分布:栈是一块连续的内存空间,堆是不连续的内存空间(链式结构)
- 内存大小:Mac OS X 64bit中,栈空间默认为8M,堆受限于虚拟内存与位数
- 碎片问题:栈不会产生碎片,堆会造成碎片
- 执行效率:栈效率高(有专门的指令和寄存器),堆效率低
17、NSInteger和CGFloat是什么数据类型?
- NSInteger在32位系统中为int,在64位系统中为long
- CGFloat在32位系统中为float,在64位系统中为double
18、Objective-C支持多继承吗?如果没有用什么代替?
- 多继承是指一个类从多于一个直接基类派生类的能力,Objective-C不支持多继承。
- 可以用下面三种方法代替:
- 消息转发
- 协议/委托
- 类别
19、为什么说Objective-C是动态语言?
- 多态,即数据类型在编译期和运行时可能会不一致,是在运行时才最终决定。
20、简述Objective-C中对象的内存管理
- Objective-C中对象的内存管理采用了引用计数。当一个对象被他人引用时,引用计数会加一,alloc、retain、copy都会造成引用计数加一;当一个对象被release时,引用计数会减一,当引用计数减到零时,才会调用dealloc真正的释放掉对象。
21、为什么很多类的delegate属性用的是assign而不是retain/strong?
- 循环引用就是多个对象之间形成了环装引用。
- 要打破循环引用,所以需要声明成assign
- 例如:
- UITableViewController管理了一个UITableView,并且UITableView的delegate和datasource为UITableViewController。
- 如果delegate和datasource使用了retain/strong内存管理方式,则:
- 当创建UITableViewController实例时,retainCount=1
- 当创建所管理的UITableView实例时会设置delegate和datasource,这时retainCount=3
- 当释放UITableViewController实例时,retainCount减1,这时retainCount=2
- 这就造成了UITableViewController无法被释放掉
22、frame和bounds有什么不同?
- frame:本视图在父视图坐标系中的位置和大小
- bounds:本视图在自身坐标系中的位置和大小
23、简述单例模式
- 单例模式是一种常见的软件设计模式,它能保证在整个应用程序生命周期内,单例类的实例至多只有一个,并能通过一个全局入口对这个实例进行访问。
- 拓展:单例的写法
h文件中声明
+ (instancetype)shareObject;
m文件中实现
// 单例(在m文件中实现)
+ (instancetype)shareObject{
static Model *model = nil;
// 在整个程序的生命期只会被执行一次
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
model = [[super allocWithZone:NULL] init];
});
return model;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
//使用加锁的方式,保证只分配一次存储空间
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
24、简述tableView中cell的重用机制
- 在UITableView的头文件中,有两个属性,分别为visiableCells和reusableTableCells。visiableCells属性保存当前显示的cells,reusableTableCells则作为cells的可重用池。
- 当一个cell不被显示时,会被放入可重用池中。当UITableView通过委托方法tableView: cellForRowAtIndexPath:来获取新的cell时,应该先通过dequeueReusableCellWithIdentifier:方法从可重用池中获取cell,如果获取不到再进行创建。
- 这样无论UITableView中多少行的数据,最多也只需创建可显示数量的cell,避免过多的创建cell对象,极大的节省了内存和提高了运行的速度。
25、响应者链是如何传递的?
- 一次完整的触摸事件的传递响应过程如下图,其中从第一响应者开始,任何环节只要实现了触摸处理,则会中断传递;如果传递到了AppDelegate还没有任何响应者处理触摸事件,则会丢弃事件。
26、__block和__weak修饰符的区别?
- __block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。
- __weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。
- __block对象可以在block中被重新赋值,__weak不可以。
__weakViewController* weakSelf =self;
GCD里面用 __weak 防止内存释放不了,循环引用。
底层:
27、main()之前的过程有哪些?
- dyld 开始将程序二进制文件初始化
- 交由ImageLoader 读取 image,其中包含了我们的类,方法等各种符号(Class、Protocol 、Selector、 IMP)
- 由于runtime 向dyld 绑定了回调,当image加载到内存后,dyld会通知runtime进行处理
- runtime 接手后调用map_images做解析和处理
- 接下来load_images 中调用call_load_methods方法,遍历所有加载进来的Class,按继承层次依次调用Class的+load和其他Category的+load方法
- 至此 所有的信息都被加载到内存中
- 最后dyld调用真正的main函数
注意:dyld会缓存上一次把信息加载内存的缓存,所以第二次比第一次启动快一点
28、objc在向一个对象发送消息时,发生了什么?
- objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,然后在发送消息的时候,objc_msgSend方法不会返回值,所谓的返回内容都是具体调用时执行的
29、静态库的原理是什么?你有没有自己写过静态编译库,遇到了哪些问题?
- 静态库 一般都是以 .a 或者 .framework 形式存在。
- 使用静态库的好处:模块化分工合作、可重用、避免少量改动导致大量的重复编译链接。
30、Runloop是什么?
- RunLoop和线程的关系:
- RunLoop是用来管理线程的,每个线程对应一个RunLoop对象。我们不可以去创建当前线程的RunLoop对象,但是我们可以去获取当前线程的RunLoop。RunLoop就是来监听该线程有无事件发生,如果有就工作,如果没有就休眠。
- 主线程的RunLoop对象默认开启,其他线程默认不开启。
- Runloop与线程:
- Runloop和线程的关系:一个Runloop对应着一条唯一的线程。
- Runloop的创建:主线程Runloop已经创建好了,子线程的runloop需要手动创建。
- Runloop的生命周期:在第一次获取时创建,在线程结束时销毁。
UITrackingRunLoopMode
: (优先切换!!)这个模式就是当UI事件交互的时候Runloop切换到的模式!!!
场景:这一模式优先级最高,当UI事件交互的时候,都会优先切换到这一模式。
NSDefaultRunLoopMode
:Runloop的默认模式!只要有事件就处理!
场景:默认模式,只要有事件就会自动切换到此模式。
NSRunLoopCommonModes
:占位符!!(在默认下和UITrackingRunLoopMode下!)
场景:这个主要用在添加一个NSTimer到RunLoop中。是一个tag,本质上不是一个Mode,默认NSDefaultRunLoopMode和 NSTrackingRunLoopMode都绑定这个tag。
31、OC完整的消息转发机制
- 首先根据receiver对象的isa指针获取它对应的class
- 优先在class的cache查找message方法,如果找不到,再到
methodLists查找 - 如果没有在class找到,再到super_class查找
- 一旦找到message这个方法,再依据receiver 中的self 指针找到当前的对象,调用当前对象的具体实现的方法(IMP),然后传递参数,调用实现方法。
32、以+ scheduledTimerWithTimeInterval...的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?
- 在主线程中以+scheduledTimerWithTimeInterval...的方式触发的timer 默认是运行在 NSDefaultRunLoopMode 模式下的,当滑动页面上的列表时,进入了 UITrackingRunLoopMode 模式,这时候 timer 就会停止可以修改 timer 的运行模式为 NSRunLoopCommonModes,这样定时器就可以一直运行了
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerTick:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
33、如何手动触发一个value的KVO
键值观察通知依赖于NSObject的两个方法:willChangeValueForKey:和didChangeValueForKey:。
- 在一个被观察属性发生改变之前,
willChangeValueForKey:
一定会被调用,这就会记录旧的值。 - 而当改变发生后,
didChangeValueForKey:
会被调用,继而objectServeValueForKey:ofObject:change:content:也会被调用。
如果可以手动实现这些调用,就可以实现“手动触发”了。
34、假设有一个字符串aabcad,请写一段程序,去掉字符串中不相邻的重复字符串,即上述字符串处理之后的输出结果为:aabcd
NSMutableString * str1 = [[NSMutableString alloc] initWithFormat:@"aabcad"];
for (int i = 0; i < str1.length - 1; i++) {
for (int j = i + 1; j < str1.length ; j++) {
// 由于字符的特殊性 无法使用 字符串 isEqualToString 进行比较 只能转化为ASCII 值进行比较 所以 需要加 unsigined 修饰
unsigned char a = [str1 characterAtIndex:i];
unsigned char b = [str1 characterAtIndex:j];
if (a == b) {
if (j - i > 1) {
// NSRange: 截取字符串 {j, 1} j: 第一个字符开始 1: 截取几个字符
NSRange range = {j, 1};
[str1 deleteCharactersInRange:range];
j = i--;
}
}
}
}
NSLog(@"------ %@-------", str1);
线程:
35、ios中有几种实现多线程的方法?
- NSThread、NSOperation和GCD
- 线程安全(加锁、可修改类型是非线程安全的)、主线程更新界面
36、ViewController生命周期
- viewDidLoad在view 从nib文件初始化时调用,
- loadView在controller的view为nil时调用。此方法在编程实现view时调用,view 控制器默认会注册memory warning notification,当view controller的任何view 没有用的时候,
- viewDidUnload会被调用,在这里实现将retain 的view release,如果是retain的IBOutlet view 属性则不要在这里release,IBOutlet会负责release 。
37、dispatch_barrier_async的作用是什么?
- dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
38、如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)
/**
* 队列组 dispatch_group_notify
*/
- (void)groupNotify{
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 加载图片
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 加载图片
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步任务1、任务2都执行完毕后,回到主线程执行下边任务
// 合成图片
});
NSLog(@"group---end");
}
http:
39、http与https的区别?
- HTTPS和HTTP的区别主要如下:
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
40、服务器能否知道APNS推送后有没有到达客户端的方法?
- APNS是苹果提供的远程推送的服务,APP开发此功能之后,用户允许推送之后,服务端可以向安装了此app的用户推送信息。但是APNS推送无法保证100%到达。
目前关于APNS苹果更新了新的策略,即 APNS/HTTP2.
如果服务器像APNS服务器推送信息之后,服务器能够接收到APNS是否真的成功像客户端推送成功了某个信息。
网友评论