最近在找工作,整理了几个比较基础的面试题,回答仅供回顾,不是太深入,也不一定正确,平时还是应该有目的性的深入理解。
1.深拷贝与浅拷贝
回答:
深拷贝是新拷贝一块内存交给对象使用,copy声明的就是深拷贝。
浅拷贝是创建一个新对象,两个对象指向同一个内存地址,ratain声明的就是浅拷贝。
在oc中只有一种情况下是浅拷贝,就是不可变对象的copy,其它的都是深拷贝(包括不可变对象的mutableCopy、可变对象的copy和mutableCopy)。
2.retain、strong、assign、weak、copy、nonatomic和atomic分别是做什么的
回答:
atomic是声明属性为原子属性,线程安全的,为防止属性在写的过程中被另一个线程读取,造成数据错误,一般用于读取文件,在手机端很少使用(桌面端会使用),应避免使用,因为它底层的实现是自旋锁(对应的为同步锁,也叫互斥锁,这是线程同步的知识)性能比较低,最好找其他的方式代替,比如通过改变代码逻辑的方式,属性默认是atomic。
nonatomic是声明属性为非原子属性,是线程不安全的。
assign是默认的值,用以声明基础数据类型(NSInteger、CGFloat)和C数据类型(int、float)。
retain用于声明对象。
copy只对实行了NSCopying协议的对象类型有效,也是用于声明对象。
strong用来声明一个强引用的属性,对应retain和copy。
weak声明一个弱引用的属性,可以自动nil化。
unsafe_unretained也是声明一个弱引用属性,但不会自动nil化,也就是说,如果内存被释放掉,这个指针就是一个野指针。
3.objective-c的内存管理原理
oc为每块内存设定一个引用计数,当内存被赋值给a对象时,引用计数加1,当内存又被赋值给b对象时,引用计数再加1,这时候引用计数是2,当a不再使用这块内存时,引用计数减1,当b不再使用这块内存时,本来引用计数应该再减1,变成0,但是这时候系统为了减少系统开支,不会进行减1操作,而会直接将内存释放掉。
4.UIView、UIWindow和CALayer的联系和区别
回答:
UIView负责渲染矩形区域的内容,为矩形区域增加动画和相应的触摸事件,布局和管理多个子视图。
UIWindow是一个特殊的UIView,而一个程序中通常只有一个UIWindow,但可以手动创建多个。
UIWindow主要起到三个作用:
1.作为容器,包含程序所有要显示的视图;
2.传递触摸消息到其它的UIView或者其它对象;
3.与UIViewController协同工作,完成设备方向旋转的支持。
CALayer是绘制内容的,不处理事件响应,与UIView是相互依赖的,依赖于UIView来显示绘制内容,UIView依赖于CALayer来提供内容。
- @implementation Son : Father
- (id)init {
if (self = [super init]) {
NSLog(@"%@“, NSStringFromClass([self class]));
NSLog(@"%@“, NSStringFromClass([super class]));
NSLog(@"%@“, NSStringFromClass(self.superclass));
}
return self;
}
@end
//打印结果就是
Son
Son
Father
为什么呢?
回答:
因为self和super的消息主体依然是self,也就是说self和super指向的是同一个对象,只是查找方法的位置有区别,一个从本类,一个从本类的超类。
一般情况下class方法只在根类NSObject中定义,极少情况有子类重写class方法,所以[self calss]和[super class]都是在根类中找方法实现,如果重写可能会不太一样。
- (id)init {
6.#import、#include和@class的联系和区别
回答:
通常引用一个类有两种方法,一种是#import,另一种是@class,两者的区别在于#import会包含引用类的所有信息,包括被引用类的属性和方法,而@class只会告诉编译器声明了一个类,而类中有什么信息不需要知道。
import被确定每个文件只会被引用一次,而#include会被多次引用,容易引起交叉编译。
7.手写block与反写block
回答:
typedef void(^selectRowAtIndex)(NSInteger index);
@property (nonatomic, copy) selectRowAtIndex handle;
[UIView transitionWithView:self.view
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
[[blueViewController view] removeFromSuperview];
[[self view] insertSubview:yellowViewController.view atIndex:0];
}
completion:nil];
typedef void(^animations)(void);
typedef void(^completion)(BOOL finished);
8.类别(category)和继承的区别
回答:
类别是对一个功能完备类的补充,比如一个Animal类,具有run和eat方法,想给类增加一个bark方法,可以使用类别。
而多个类具有相同的实例变量和方法时,考虑用继承,即子类可以继承父类相同的特性,比如Animal类具有年龄和体重两个属性,Dog也具有年龄和体重两个属性,Dog就可以继承Animal的两个属性,即为继承。
共同点是都给类进行了扩展。
区别是:
1.类别是对方法的扩展,不能添加成员变量,继承可以在父类的成员变量基础上添加新的成员变量。
2.类别只能添加新方法,而不能修改和删除原来的方法,继承可以增加、修改和删除方法。
3.类别不提倡对原有方法进行重载,继承可以使用super对原来的方法进行重载。
4.类别可以被继承,如果一个父类中定义了一个类别,那么子类中也会继承此类别。
9.__block与__weak的区别
回答:
__block对象在block中是可以被修改和重新赋值的。
__block可以修饰对象和基本数据类型,而__weak只能修饰对象。
__weak对象不可以在block中被重新赋值。
10.多线程
回答:
多线程的实现方式包括:
Pthreads 跨平台,使用难度大,很少使用。
NSThread 更加面向对象,可直接操作线程对象。生命周期需要自己手动管理,最常用的可能是暂停功能- (void)sleepForTimeInterval:(NSTimeInterval)time;
GCD 基于C语言,取代NSThread,充分利用设备多核,常用,会自动管理线程的生命周期。
GCD是基于任务和队列来进行的,队列遵循FIFO(先进先出)原则,队列氛围并行队列和串行队列。
NSOperation & NSOperationQueue 对GCD的封装,比GCD更加面向对象,比GCD性能稍微差一点,常用。
NSOperation与NSOperationQueue分别对应GCD中的任务和队列的概念。
11.isKindOfClass和isMemberOfClass的区别
回答:
isKindOfClass可以确定一个对象是否是一个类的实例,或者该类祖先类的实例,而isMemberOfClass只能用来判断前者,不能用来判断后者。
12.UCP和TCP的区别
回答:
TCP(传输控制协议),提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,流量控制等功能,保证能从一端传到另一端。
UDP(用户数据协议),是一个简单的面向数据的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。因为UDP在传输数据使不用在客户和服务器之间建立一个连接,且没有超时重发机制,所以传输速度很快。
13.delegate、block和NSNotification的区别
回答:
NSNotification发出消息后,可以有多个消息接受者,delegate效率比较高,delegate发出消息后只能由某个特定的对象获得消息。
block可以代替delegate的功能,而且实现起来比较简洁,delegate需要对每个事件进行判断识别来源,而block可以在创建事件时区分开来。
14.KVO和KVC的区别
回答:
KVC即是指 NSKeyValueCoding,一个非正式的Protocol,提供一种机制来间接访问对象的属性。而不是通过调用Setter、Getter方法访问。KVO 就是基于 KVC 实现的关键技术之一。
- (id)valueForKey:(NSString *)key;
- (void)setValue:(id)value forKey:(NSString *)key;
KVO的是KeyValue Observe的缩写,中文是键值观察。这是一个典型的观察者模式,观察者在键值改变时会得到通知。
addObserver:forKeyPath:options:context:;
observeValueForKeyPath:ofObject:change:context:
KVO需要注意在对象销毁时,要移除观察者,不然会崩溃。
removeObserver:forKeyPath:context:
KVO是同步执行,不能执行耗时方法,KVO可能发生资源抢夺
15.GCD
回答:
dispatch_get_global_queue 后台执行
dispatch_get_main_queue 主线程执行
dispatch_once_t 一次性执行
dispatch_time_t 延迟执行
dispatch_group_async 并行执行
dispatch_group_notify 并行执行后汇总
还有自定义队列