1.关于方法的执行顺序问题,代码示例如下:
- (void)GCDTest {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"4");
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"5");
});
[self performSelector:@selector(test2)];
[self performSelector:@selector(test3) withObject:nil afterDelay:0];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"6"); // 开启了一个异步线程,因此这里执行的时机不确定
});
[self test1];
}
- (void)test1 {
NSLog(@"1");
}
- (void)test2 {
NSLog(@"2");
}
- (void)test3 {
NSLog(@"3");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"test insert dispatch_async in test3");
});
}
// 打印结果
2019-05-16 16:24:22.657190+0800 test[26135:323906] 2
2019-05-16 16:24:22.657362+0800 test[26135:323906] 1
2019-05-16 16:24:22.657373+0800 test[26135:323974] 6 //位置随机不确定
2019-05-16 16:24:22.667737+0800 test[26135:323906] 4
2019-05-16 16:24:22.667832+0800 test[26135:323906] 5
2019-05-16 16:24:22.668530+0800 test[26135:323906] 3
2019-05-16 16:24:22.668625+0800 test[26135:323976] test insert dispatch_async in test3
- dispatch_after 第二个参数为0,可以理解为在当前时刻向主队列添加一个block,文档中 asynchronously 可以看出是在当前时刻添加到主队列的尾部;
- 这里主要考的是 dispatch_async 这个函数的特点,往指定的队列添加一个block立马返回;
- [self performSelector:@selector(test2)];相当于[self test2];
-
[self performSelector:@selector(test3) withObject:nil afterDelay:0];通过Timer添加到runloop中,其Mode值为
NSDefaultRunLoopMode
;NSDefaultRunLoopMode是当前线程上正在执行的任务优先,所以如果当前线程上有其他任务要执行,那么test3让步,其他任务结束后再轮到test3执行;如果往当前队列(test3)中添加dispatch_async任务,通过测试,其优先级跟performSelector: withObject: afterDelay:是等同的;
2.UIButton与UITableView的层级结构?
UIButton-->UIControl-->UIView-->UIResponder-->NSObject
UITableView-->UIScrollView-->UIView-->UIResponder-->NSObject
3.项目开始没做容错处理,如何拦截潜在的崩溃?
解决方案:拦截存在潜在崩溃危险的方法,在拦截的方法里进行相应的处理,就可以防止方法的崩溃了。
步骤:1.
通过category给类添加方法用来替换掉原来存在潜在崩溃可能的方法。2.
利用runtime的方法交换技术,将系统方法替换成我们给类添加的新方法。3.
利用异常的捕获NSException
来防止程序的崩溃,并进行相应的处理。
总结:category + runtime + NSException
。
4.Object-C有多继承吗?有的话是怎么实现的,没有的话要怎么才能实现类似的功能?cocoa中所有的类都是NSObject的子类。
oc中没有多继承。要达到类似多继承一样的效果,可以通过接口(protocol委托代理)来实现。
5.OC有私有方法吗?私有变量呢?
私有的定义:私有是指只能够在本类内部使用或访问,但是不能在类的外部被访问。
在OC中,所有实例变量(成员变量)默认都是私有的,所有实例方法默认都是公有的。
但是可以通过runtime机制来打破这一规则,所以oc没有绝对的私有方法和私有变量
。
6.简单介绍一下你对iOS内存管理的理解?
其实就是引用计数(reference count)
的管理。内存管理就是在程序需要时,程序员分配一段内存,而当使用完之后将它释放。创建一个对象并获取它的所有权,rc = 1(alloc/new/copy/mutableCopy)。
Autorelease Pool:在开发中,我们常常都会用到局部变量
,局部变量一个特点就是当它超过作用域
时,就会自动释放
。而autorelease pool
跟局部变量
类似,当执行代码超过autorelease pool
块时,所有放在autorelease pool的对象都会自动调用release
。工作原理如下:创建一个NSAutoreleasePool对象,在autorelease pool
块的对象调用autorelease
方法,释放NSAutoreleasePool
对象。
iOS/OS X内存管理方法有两种:手动引用计数(MRC)和自动引用计数(ARC)。
自动引用计数
简单来说,它让编译器
来代替程序员来自动加入retain
和release
方法来持有
和释放对象
的所有权
。
在ARC内存管理机制中,id
和其他对象类型变量
必须是以下四个ownership qualifiers
(所有权修饰符/修饰词)其中的一个来修饰:所以在管理Objective-C对象内存
的时候,你必须选择其中一个,如下:
__strong
:被它修饰的变量持有对象的所有权
(默认,如果不指定其他,编译器就默认加入
)
__weak
:被它修饰的变量都不持有对象的所有权
,而且当变量指向的对象的RC为0时,变量设置为nil
。
__unsafe_unretained
:被它修饰的变量都不持有对象的所有权
,但当变量指向的对象的RC为0时,变量并不设置为nil
,而是继续保持对象的地址
;这样的话,对象有可能已经释放,但继续访问,就会造成非法访问(Invalid Access)。
__autoreleasing
:相比之前的创建、使用和释放NSAutoreleasePool
对象,现在你只需要将代码放在@autoreleasepool块即可。你也不需要调用autorelease方法了,只需要用__autoreleasing修饰变量即可。
Property属性修饰语:
Property modifier | Ownership qualifier |
---|---|
strong | __strong |
retain | __strong |
copy | __strong |
weak | __weak |
assign | __unsafe_unretained |
unsafe_unretained | __unsafe_unretained |
7.如何获取对象的引用计数(reference count)?
MRC
环境下,获取对象的reference count方法:retainCount
;
ARC
环境下,获取对象的reference count方法:CFGetRetainCount((__bridge CFTypeRef)objectA)
;
8.KVO的实现原理?
KVO基本原理:
1.
KVO是基于runtime机制实现的。
2.
当某个类的属性对象第一次被观察时,系统就会在运行时动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter方法
。派生类在被重写的setter方法内实现真正的通知机制。
3.
如果原类为Person
,那么生成的派生类名为NSKVONotifying_Person
。
4.
每个类对象
中都有一个isa指针
指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类
,从而在给被监控属性
赋值时执行的是派生类的setter方法
。
5.
键值观察通知依赖于NSObject
的两个方法:willChangeValueForKey:
和didChangeValueForKey:
。在一个被观察属性
发生改变之前,willChangeValueForKey:
一定会被调用,这就会记录旧的值。而当改变发生后,didChangeValueForKey:
会被调用,继而observeValueForKeyPath:ofObject:change:context:
也会被调用。
9.观察者模式?
定义对象间的一种一对多
依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。典型例子:通知
和KVO
。
10.如果让你实现NSNotificationCenter,讲一下思路?
1.NSNotificationCenter
是一个单例;
2.NSNotificationCenter
内部使用可变字典NSMutableDictionary
来存储,以通知名称postName
作为key
,以数组NSArray作为值,该数组存储着每个观察者的信息:观察者对象
、观察者处理方法
、通知名称
等。
3.
当发送通知时,以通知名称为key
去获取相应的观察者信息数组,然后遍历这个数组,取出观察者对象和相对应处理方法,进行实例方法调用。
11.Category的实现原理,以及category为什么只能加方法不能加实例变量?
category是可以添加属性
(需要用runtime动态添加setter/getter方法),不能添加实例变量
;不能添加实例变量的原因:一个类的实例变量在编译阶段,就会在objc_class
的class_ro_t
这里进行存储和布局,而category是在运行时才进行加载的,然后在加载ObjC运行时的过程中在realizeClass
方法中:
// 从 `class_data_bits_t `调用 `data` 方法,将结果从 `class_rw_t `强制转换为 `class_ro_t `指针
const class_ro_t *ro = (const class_ro_t *)cls->data();
// 初始化一个 `class_rw_t` 结构体
class_rw_t *rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
// 设置`结构体 ro` 的值以及 `flag`
rw->ro = ro;
// 最后设置正确的` data`。
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
运行时加载的时候把class_ro_t
里面的方法、协议、属性等内容赋值给 class_rw_t
,而class_rw_t
里面没有用来存储相关变量的数组,这样的结构也就注定实例变量是无法在运行期进行填充。
12.swift中struct和class的区别?
swift中,class
是引用类型,struct
是值类型。值类型在传递
和赋值
时将进行复制,而引用类型则只会使用引用对象
的一个“指向”。所以他们两者之间的区别就是两个类型的区别。
class
有这几个功能struct
却没有:
1.class
可以继承,这样子类可以使用父类的特性和方法。
2.
类型转换可以在runtime
的时候检查和解释一个实例的类型。
3.
可以用deinit
来释放资源。
4.
一个类可以被多次引用。
struct
也有这样几个优势:
1.
结构较小,适用于复制操作,相对于一个class
的实例被多次引用更加安全。
2.
无需担心内存memory leak
或者多线程冲突问题。
13.在一个HTTPS连接的网站里,输入账号密码点击登录后,到服务器返回这个请求前,中间经历了什么?
1.
客户端打包请求。包括url
,端口,你的账号密码等等。账号密码登录应该用的是Post
方式,所以相关的用户信息会被加载到body
里面。这个请求应该包含三个方面:网络地址
、协议
、资源路径
。注意:这里是HTTPS
,就是HTTP + SSL / TLS
,在HTTP
上又加了一层处理加密信息的模块(相当于是个锁)。这个过程相当于是客户端请求钥匙。
2.
服务器接受请求。一般客户端的请求会先发送到DNS服务器
。DNS服务器负责将你的网络地址解析成IP地址,这个IP地址对应网上一台机器。这其中可能发生Hosts Hijack
和ISP failure
的问题。过了DNS
这一关,信息就到了服务器端,此时客户端会和服务器的端口之间建立一个socket连接
,socket一般都是以file descriptor
的方式解析请求。这个过程相当于是服务器端分析是否要向客户端发送钥匙模板
。
3.
服务器端返回数字证书。服务器端会有一套数字证书(相当于是个钥匙模板
),这个证书会先发送给客户端。这个过程相当于是服务器端向客户端发送钥匙模板。
4.
客户端生成加密信息。根据收到的数字证书(钥匙模板),客户端会生成钥匙
,并把内容锁上,此时信息已经加密。这个过程相当于客户端生成钥匙并锁上请求。
5.
客户端发送加密信息。服务器端会收到由自己发送出去的数字证书加锁的信息
。这个时候生成的钥匙
也一并被发送到服务器端。这个过程是相当于客户端发送请求。
6.
服务器端解锁加密信息。服务器端收到加密信息后
,会根据得到的钥匙进行解密
,并把要返回的数据进行对称加密(密钥加密)
。这个过程相当于服务器端解锁请求、生成(并加锁)回应信息
。
对称加密(密钥加密):
密钥加密是发送和接收数据的双方,使用相同的或对称的密钥对明文进行加密解密运算的加密方法。由于其速度快
,对称性加密通常在消息发送方需要加密大量数据时使用。数字证书是经过权威机构(CA)认证的公钥。
对称加密
采用了对称密码编码技术,它的特点是文件加密和解密使用相同的密钥加密,也就是密钥也可以用作解密密钥,这种方法在密码学中叫做对称加密算法,对称加密算法使用起来简单快捷,密钥较短,且破译困难,除了数据加密标准(DES),另一个对称密钥加密系统是国际数据加密算法(IDEA),它比DES的加密性好,而且对计算机功能要求也没有那么高
与对称加密算法不同,非对称加密算法
需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。
公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
非对称加密算法实现机密信息交换的基本过程是:甲方生成一对密钥并将其中的一把作为公用密钥向其它方公开;得到该公用密钥的乙方使用该密钥对机密信息进行加密后再发送给甲方;甲方再用自己保存的另一把专用密钥对加密后的信息进行解密。甲方只能用其专用密钥解密由其公用密钥加密后的任何信息。
总结:对称与非对称取决于加密时采用的加密算法。
7.
服务器端向客户端返回信息。客户端会收到相应的加密信息
。这个过程相当于服务器端向客户端发送回应
。
8.
客户端解锁返回信息。客户端会用刚刚生成的钥匙进行解密,将内容显示在浏览器上。
14.关键字const的含义?
const意味着“只读”。关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉别人这个参数的应用目的。通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。
(1)合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会去改变它了;
(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。
const int a;
a是一个常整数。
int const a;
a是一个常整数。
const int * a;
a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。
int * const a;
a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。
int const * a const;
a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。
15.#import和#include的区别,@class代表什么?
@class一般用于头文件中,需要声明该类的某个实例变量的时候用到,在m文件中还是需要使用#import,#include是c语言中引入头文件的方法,而#import比起#include的好处就是不会引起重复包含。
16.堆和栈的区别?
管理方式:对于栈来讲,是由编译器自动管理,无需我们手动控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
申请大小:
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存区域(栈顶的地址和栈的最大容量是系统预先规定好的,在Windows下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小
)。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
碎片问题:对于堆
来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈
来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出。
分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloc函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。
堆则是C/C++函数库提供的,它的机制是很复杂的。
17.为什么很多内置的类,如TableViewController的delegate的属性是weak不是retain/assign?
为了避免循环强引用;assign会造成野指针;
18.定义属性时,什么情况使用copy、assign、retain?
assign用于简单数据类型,如NSInteger、基本数据类型。
retain和copy用于对象,copy用于当a指向一个对象,b也想指向同样的对象的时候,如果用assign,a如果释放,再调用b会crash,如果用copy的方式,a和b各自有自己的内存,就可以解决这个问题。
19.对象是什么时候被release的,讲述具体过程?
引用计数为0时,对象被释放;具体过程:autorelease实际上只是把对release
的调用延迟了,对于每一个Autorelease
,系统只是把该Object放入了当前的Autorelease pool
中,当该pool被释放时,该pool中的所有Object会被调用Release。对于每一个Runloop,系统会隐式
创建一个Autorelease pool,这样所有的release pool会构成一个像CallStack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的Autorelease pool会被销毁,这样这个pool里的每个Object(就是autorelease的对象
)会被release。那什么是一个Runloop呢?一个UI事件,Timer call,delegate call,都会是一个新的Runloop。
20.iOS有垃圾回收吗?
Objective-C 2.0有垃圾回收机制,但是只能在Mac OS X Leopard 10.5以上的版本使用。其他没有。
21.tableView的重用机制?
查看UITableView头文件,会找到NSMutableArray *visibleCells,和NSMutableDictionary *reusableTableCells两个结构。visibleCells内保存当前显示的cells,reusableTableCells保存可重用的cells。
TableView显示之初,reusableTableCells为空,那么[tableView dequeueReusableCellWithIdentifier:CellIdentifier]返回nil
。开始的cell都是通过[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]来创建,而且cellForRowAtIndexPath只是调用最大显示cell数的次数。
22.ViewController的loadView、viewDidLoad、viewDidUnload分别是什么时候调用的,在自定义ViewController时在这几个函数中应该做什么工作?
由init、loadView、viewDidLoad、viewDidUnload、dealloc的关系说起。init方法:在init方法中实例化必要的对象(遵从LazyLoad思想)。init方法中初始化ViewController本身。
loadView方法:当view需要被展示而它却是nil时,viewController会调用该方法,不要直接调用该方法
。如果手工维护views,必须重载重写该方法。如果使用IB维护views,必须不能重载重写该方法。
loadView和IB构建view:你在控制器中实现了loadView方法,那么你可能会在应用运行的某一个时候被内存管理控制调用。如果设备内存不足的时候,view控制器会收到didReceiveMemoryWarning的消息。默认的实现是检查当前控制器的view是否在使用,如果它的view不在当前正在使用的view hierarchy(视图层次结构
)里面,且你的控制器实现了loadView方法,那么这个view将被release,loadView方法将被再次调用来创建一个新的view。
viewDidLoad方法:在view创建完毕后,最终都会调用viewDidLoad方法。
viewDidUnload方法:当系统内存吃紧
的时候会调用该方法(注:ViewController没有被dealloc
)。didReceiveMemoryWarning
方法的默认实现是:如果当前UIViewController的view不在应用程序的视图层次结构(View Hierarchy)中,即view的superview为nil的时候,就会将view释放,并且调用viewDidUnload方法。作用:
上面说到,发出内存警告且view被释放的时候就会调用viewDidUnload方法,所以一般在释放资源,主要是释放界面元素相关的资源,将相关的实例都赋值为nil。
23.ViewController的didReceiveMemoryWarning是在什么时候调用的?默认的操作是什么?
当程序接到内存警告时View Controller将会收到这个消息。这个函数的默认实现是:检查controller是否可以安全地释放它的view
(这里的view
指的是controller的view属性)。
如果view可以被释放,那么这个函数释放view并调用viewDidUnload。你可以重载这个函数来释放controller中使用的其他内存。但要记得调用这个函数的super实现来允许父类(一般是UIViewController)释放view。如果你的ViewController保存着view的子view的引用,那么你应该在这个函数中来释放这些引用。
24.怎么理解MVC,在Cocoa中MVC是怎么实现的?
MVC分为Model层、View层、Controller层
;1.
Model和View不能互相通信,只能通过controller传递;2.
Controller可读写调用Model,Model通过通知
和KVO
机制与controller间接通信;3.
Controller可直接与View对话,通过outlet
,直接操作View,outlet直接对应到View中的控件,View通过action
向Controller报告事件的发生;Controller是View的直接数据源;Controller是View的代理,以同步View与Controller;
25.MVVM模式的理解?
由Model、View、ViewModel
三部分构成,在MVVM架构下,View和Model之间并没有直接的联系,而是通过ViewModel进行交互,Model和ViewModel之间的交互是双向的,因此View数据的变化会同步到Model中,而Model数据的变化也会立即反应到View上。(ViewModel 通过双向数据绑定把 View层 和 Model层 连接了起来)
26.delegate和notification的区别,分别在什么情况下使用?
delegate
一般是一对一通信,notification
一般是一对多通信;delegate要定义协议方法,代理对象实现协议方法,并且需要建立代理关系才可以实现通信;notification需要注册通知中心,然后再需要调用postNotificationName传递消息给通知中心,通知中心监听到消息,并调用回调消息;delegate更注重过程传输,当对象间关系比较复杂时用delegate更清晰;
27.self.xxx与_xxx有什么区别?
通过self.xxx
是对属性进行访问,本质是调用属性的setter方法,引用计数器会+1;_xxx
是直接对成员变量(实例变量)进行访问,是对指针的赋值,引用计数器没发生变化。
28.id、nil代表什么?
id是指向struct objc_object的一个指针,这个意思基本上是说,id是一个指向任何一个继承了Object(或者NSObject)类的对象。需要注意的是id是一个万能指针。 nil和C语言的NULL相同,在objc/objc.h中定义。nil表示一个Object-C对象,这个对象的指针指向空(没有东西就是空)。
29.内存管理Autorelease、retain、copy、assign的set方法和含义?
1.你初始化(alloc/init)的对象,你需要释放(release)它。例如:NSMutableArray *aArray = [[NSArray alloc] init];后,需要[aArray release];
2.你retain或copy的,你需要释放它。例如:[aArray retain]后,需要[aArray release];
3.被传递(assign)的对象,你需要斟酌的retain和release。例如:obj2 = [[obj1 someMethod] autorelease];
对象2接收对象1的一个自动释放的值,或传递一个基本数据类型(NSInteger,NSString)时:你或希望将对象2进行retain,以防止它在被使用之前就被自动释放掉。但是在retain后,一定要在适当的时候进行释放。
30.属性readwrite、readonly、assign、retain、copy、nonatomic各是什么作用,在哪种情况下用?
readwrite:默认属性,将生成不带额外参数的getter和setter方法(setter方法只有一个参数)。
readonly:将只生成getter方法而不生成setter方法(getter方法没有get前缀)。
assign:指定setter方法用简单的赋值,这是默认操作。你可以对标量类型(如int)使用这个属性。你可以想象一个float,它不是一个对象,所以它不能retain、copy。
retain:指定retain应该在后面的对象上调用,前一个值发送一条release消息。你可以想象一个NSString实例,它是一个对象,而且你可能想要retain它。
copy:指定应该使用对象的副本(深度拷贝),前一个值发送一条release消息。基本上像retain,但是没有增加引用计数,是分配一块新的内存来放置它。
nonatomic:不保证setter/getter的原子性,多线程情况下数据可能会有问题。
atomic:对于对象的默认属性,就是setter/getter生成的方法是一个院子操作。如果有多个线程同时调用setter的话,不会出现某一个线程执行setter全部语句之前,另一个线程开始执行setter的情况,相关于方法头尾加了锁一样。
31.类变量的@protected、@private、@public、@package声明各有什么含义?
OC对存取权限的设定,也是变量的作用域。protected该类和所有的子类中的方法可以直接访问这样的变量,这是默认的。private该类中的方法可以访问这样的变量,子类不可以。public除了自己和子类中的方法外,也可以被其他类或者其他模块中的方法所访问,开放性最大。package对于64位图像,这样的成员变量可以在实现这个类的图像中随意访问。
32.浅拷贝和深拷贝区别是什么?
浅拷贝引用计数+1,不生成全新副本,深拷贝引用计数不变,生成全新副本,并引用计数为1;
33.Cocoa中有虚基类的概念么?怎么简洁的实现?
没有,虚基类是解决c++中多重继承问题而产生的;可通过接口实现多继承类似的效果。
34.NSString和NSMutableString有什么区别?
NSString不可变,NSMutableString可变;
35.自动释放池跟GC(垃圾回收)有什么区别?iPhone上有GC么?[pool release] 和 [pool drain]有什么区别?
Autorelease Pools概要
一个“Autorelease Pool”实例中“包含”其他各种调用了“autorelease”方法的对象。当它释放时,其中所有被管理对象都会收到“Autorelease”的消息。 注意,同一个对象可以被多次调用“autorelease”方法,并可以放到同一个“Autorelease Pool”中。引入这个自动释放池机制,对象的“autorelease”方法代替“release”方法可以延长它的生命周期,直接到当前“Autorelease Pool”释放。如果想让此对象的生命周期超过“Autorelease Pool”,还可以再次“retain”,如下方法:
Cocoa总是认为当前至少有一个“Autorelease Pool”对象是可用的。若此对象并不存在,你调用的“autorelease”的所有对象都不会被自动释放掉,从而造成内存泄漏。
没有。在引用计数方式下,二者没有什么不同,你通常要使用drain。在GC(garbage-collected)环境下,drain会在必要时引起GC作用,然而release是个空操作。
36.C和obj-c如何混用?
1.
obj-c的编译器处理后缀为m的文件时,可以识别obj-c和c的代码,处理mm文件可以识别obj-c、c、c++代码,但cpp文件必须只能用c/c++代码,而且cpp文件include的头文件中,也不能出现obj-c的代码,因为cpp只是cpp。
2.
在mm文件中混用cpp直接使用即可,所以obj-c混用cpp可以。
3.
在cpp中混用obj-c,其实就是使用obj-c编写的模块是我们想要的。
如果模块以类实现,那么要按照cpp class的标准写类的定义,头文件中不能出现obj-c的东西,包括#import cocoa的。实现文件中,即类的实现代码中可以使用obj-c的东西,可以import,只是后缀是mm。
如果模块以函数实现,那么头文件要按c的格式声明函数,实现文件中,c++函数内部可以用obj-c,但后缀还是mm或m。
总结:只要cpp文件和cpp include的文件中不包含obj-c的东西就可以用了,cpp混用obj-c的关键是使用接口,而不能直接使用实现代码,实际上cpp混用的是obj-c编译后的o文件,这个东西其实是无差别的,所以可以用。obj-c的编译器支持cpp。
37.响应者链是什么?
事件的传递从上往下,事件的响应从下往上。
38.UIScrollView用到了什么设计模式?还能在Foundation库中找到类似的吗?
组合模式,所有的container view都用了这个模式。
观察者模式,所有的UIResponder都用了这个模式。
模板模式,所有datasource和delegate接口都是模板模式的典型应用。
39.timer的间隔周期准吗?为什么?怎样实现一个准确的timer?
NSTimer不是绝对准确的,可以精确到50-100毫秒,而且中间耗时或阻塞错过下一个点,那么下一个点就pass过去了;
40.类别的作用?继承和类别在实现中有何区别?
category可以在不熟悉,不改变原来代码的情况下往里面添加新的方法,只能添加,不能删除修改。并且如果类别和原来类中的方法产生名称冲突,则类别将覆盖原来的方法,因为类别具有更高的优先级。
类别主要有3个作用:
1.
将类的实现分散到多个不同文件或多个不同框架中。
2.
创建对私有方法的前向引用。
3.
向对象添加非正式协议。
继承可以增加,修改或者删除方法,并且可以增加属性。
41.类别和类扩展的区别?
category和extension的不同在于后者可以添加属性。另外后者添加的方法是必须要实现的。extension可以认为是一个私有的category。
42.oc中的协议和java中的接口概念有何不同?
OC中的代理有2层含义,官方定义为formal(正式协议
)和informal protocol(非正式协议
)。前者和Java接口一样。
informal protocol中的方法属于设计模式考虑范畴,不是必须实现的,但是如果有实现,就会改变类的属性。非正式协议使用interface修饰。
43.KVC和KVO的区别?
KVC:键值编码,是一种间接访问实例变量的方法。通过key获取value,基于NSKeyValueCoding协议,通过setValue: forKey:
实现值的传递;
KVO:键值观察者,是观察者设计模式的一种具体实现。通过注册的观察者,进行消息传递,通过addObserver: forKeyPath: options: context:
注册观察者,key对应的value发生变化就会被观察者监听到;
44.代理的作用?
从代理的原理方向回答,代理注重过程传输,在解耦方面有巨大作用。
45.oc中可修改和不可修改类型?
比如NSArray和NSMutableArray,NSDictionary和NSMutableDictionary能否动态添加和修改内容。前者在初始化后的内存空间就是固定不可变的,后者可以添加修改等,可以动态申请新的内存空间。
46.我们说的oc是动态运行时语言是什么意思?
runtime,在程序运行时动态的插入代码改变源码的结构:新函数方法的添加,替换原有函数方法的功能。
运行时多态:
1.动态类型。 如id类型。实际上静态类型因为其固定性和可预知性而使用得更加广泛。静态类型是强类型,而动态类型属于弱类型。运行时决定接收者。
这里补充讲一下强、弱类型:语言有无类型、强类型和弱类型三种。无类型的不做任何检查,甚至不区分指令和数据;弱类型的检查很弱,仅能区分指令和数据;强类型的严格在编译期进行检查。强类型语言在没有强制类型转化前,不允许两种不同类型的变量相互操作
2.动态绑定。让代码在运行时判断需要调用什么方法,而不是在编译时。与其他面向对象语言一样,方法调用和代码并没有在编译时连接在一起,而是在消息发送时才进行连接。运行时决定调用哪个方法。
3.动态载入。让程序在运行时添加代码模块以及其他资源。用户可以根据需要加载一些可执行代码和资源,而不是在启动时就加载所有组件。可执行代码中可以含有和程序运行时整合的新类。
对象是运行时类的一个实例。在类里声明了的实例变量和方法,它的每个实例都在内存中拥有同样的实例变量,以及指向那些方法的指针。在oc中对象永远是通过指针来引用的。
47.通知和协议的区别/不同之处?
从对应关系来说,通知是一对多,代理协议是一对一。从使用方式来说,通知需要注册通知中心,通知的接收者和发送者不需要知道对方,可以指定接收通知的具体方法,通知名可以是任何字符串,传递的类似于信号,用完需要注销监听者;代理协议需要订立协议,设置代理并遵循协议,注重过程传输;
48.什么是推送消息?
就是推送,设备通过iOS系统向APNS服务器获取设备UUID,然后传给后台,后台通过获取到的UUID跟APNS交互,然后通过APNS给设备推送消息;
49.关于多态性?
多态一般都要跟继承结合起来说,其本质是子类通过覆盖或重载父类的方法,来使得对同一类对象同一方法的调用产生不同的结果。这里需要辨析的地方在:同一类对象指的是继承层级再上一层的对象,更加泛化。
程序中的多态:父类指针指向子类对象。
原文:https://www.jianshu.com/p/83f174b493d6
50.对于单例的理解?
一个单例类,在整个程序生命周期中只有一个实例,并且提供一个类方法供全局调用,在编译时初始化这个类,然后一直保存在内存中,到程序(APP)退出时由系统自动释放这部分内存。
51.frame和bounds的区别?
frame:该view在父view坐标系统
中的位置和大小。(参照点是:父亲的坐标系统)
bounds:该view在本地坐标系统
中的位置和大小。(参照点是:本地坐标系统,就相当于ViewB自己的坐标系统,以0,0点为起点)
52.方法和选择器有何不同?
selector是一个方法的名字,method是一个组合体,包含了名字和实现。
53.懒加载?
延时加载,避免内存过高,异步加载,避免线程堵塞;
54.是否在一个视图控制器中嵌入两个tableView控制器?
可以在一个ViewController中添加两个tableView视图控制器;
55.一个tableView是否可以关联两个不同的数据源?你会怎么处理?
按理是可以的,但要在设置代理回调用到数据前合并数据源。
56. 你使用过Objective-C的运行时编程(Runtime Progr amming)么?如果使用过,你用它做了什么?你还能记得你所使用的相关的头文件或者某些方法的名称吗?
用过;用到的场景:归档、动态添加属性和方法、进行方法映射的交换、动态插入方法;
引用头文件:#import <objc/runtime.h>
。
方法举例:方法交换class_getInstanceMethod、method_exchangeImplementations
,获得类中的所有成员变量class_copyIvarList
,发送消息函数objc_msgSend()
等。
57. NSUserDefaults使用时应注意的事项?
NSUserDefaults可以存储常见数据类型,但是存储的对象全是不可变的
,若要存储可变类型
需转换成不可变;如果想要存储自定义类型
,那么将自定义类型
转换为NSData类型
。归档可以实现把自定义的对象存放在文件中。