****************OC语言特性***************
1.属性readwrite,readonly,assign,retain,copy,nonatomic 各是什么作用,在哪种情况下用?
1).readwrite(默认关键字)
是可读可写特性,同时创建set方法和get方法。
2). readonly
是只读特性,只会生成getter方法,不会生成setter方法 ;不希望属性在类外改变;
3). assign
是赋值特性,setter方法将传入参数直接赋值给实例变量;仅设置变量时;
4). retain
一般mrc中使用。表示持有特性,setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1;
5).copy
表示赋值特性,setter方法将传入对象复制一份;需要完全一份新的变量时;
6).nonatomic
非原子操作,决定编译器生成的setter getter是否是原子操作,atomic(默认关键字)
保证赋值和获取是保证线程安全的,一般使用nonatomic。
2.assgin和weak什么区别呢?
assign的特点:
修饰基本数据类型(int BOOL)
修饰对象类型时,不改变其引用计数
会产生悬垂指针(释放之后,继续访问会产生悬垂指针,会造成内存泄漏和程序异常)
weak的特点
不改变被修饰对象的引用计数
所指对象在被释放之后会自动置为nil
区别:
weak 只可以修饰对象。 assign 可修饰对象,和基本数据类型。
assign修饰的对象释放之后,指针仍然指向原来的内存地址。 weak所指对象在被释放之后会自动置为nil
3.类别的作用?继承和类别在实现中有何区别?
答:category 可以在不获悉,不改变原来代码的情况下添加新的方法,只能添加,不能删除修改,并且如果类别和原来类中的方法产生名称冲突,则类别将覆盖原来的方法,因为类别具有更高的优先级。
继承可以增加,修改或者删除方法,并且可以增加属性。
4.分类和拓展的相关问答
- 类别/分类(category)和类扩展(extension)的区别?
分类是运行时决议,拓展是编译时决议
分类是有声明和实现,拓展是只以声明的形式存在。没有具体实现。
不能为系统类添加拓展,可以给系统类添加分类。
- 分类的实现原理?
是由运行时来决议的。然后不同分类当中,含有同名方法。谁最后编译,谁最后生效。分类方法高于宿主类方法级别。
- 你用分类都做了哪些事情?
声明私有方法
分解体积庞大的类文件。
把framework的私有方法公开化
- 分类都可以添加哪些内容?
1.实例方法
2.类方法
3.协议
4.属性(如果非要加属性就要用到runtime ,用它进行关联。)
- 能否给分类添加”成员变量“,成员变量被添加到了哪里?
可以。使用关联对象技术。被添加到了 同一个全局容器中。(其实也是runtime)
- 分类的特点?
运行时决议
可以为系统类添加分类
- 一般用拓展做什么?
声明私有属性
声明私有方法
声明私有成员变量
-
通过runtime给分类(category)添加属性:
#import "Person.h"
@interface Person (Play)
@property (nonatomic,copy) NSString *name;
@end
#import "Person+Play.h"
#import <objc/runtime.h>
static NSString *nameKey = @"nameKey"; //定义一个key值
@implementation Person (Play)
//运行时实现setter方法
- (void)setName:(NSString *)name{
objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
//运行时实现getter方法
- (NSString *)name {
return objc_getAssociatedObject(self, &nameKey);
}
@end
5.什么是代理?代理的作用?
代理是一种软件设计模式。以@protocol形式体现,传递方式是一对一的。
代理的目的是改变或传递控制链。允许一个类在某些特定时刻通知到其他类,而不需要获取到那些类的指针。可以减少框架复杂度。
6.通知和代理的区别?
通知是使用观察者模式
来实现的用于跨层传递消息
的机制。传递方式是一对多的、
代理是代理模式
实现的,只能一对一传递、
7.介绍一下观察者模式?
观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。
在iOS中典型的此模型实现方式为NSNotificationCenter和KVO。
NSNotificationCenter
8.通知的实现机制?/如何实现通知机制?
通知中心会维护一个map表或者字典。key是notificationName。可能会添加多个value。value是Observers_List。
9.KVO的简介?
KVO是oc对观察者设计模式
的实现,苹果使用了isa混写(isa-swizzling)
来实现的KVO. 他提供了观察某一属性变化的方法,极大的简化了代码。
isa混写技术在KVO中是怎样体现的?
当观察某个对象A时,KVO机制利用runtime动态创建一个对象A的子类(NSKVONotifying_A),并对新的子类重写了setter方法,原有类的isa指针指向新的子类。setter方法随后负责通知观察对象属性的改变状况。
10.KVC的简介?
KVC(key-value-coding)苹果提供的键值编码技术
。是一种间接访问对象的属性使用字符串来标识属性,而不是通过调用存取方法,直接或通过实例变量访问的机制。
KVC的实现原理
1.首先搜索 setter 方法,有就直接赋值。
2.如果上面的 setter 方法没有找到,再检查类方法+ (BOOL)accessInstanceVariablesDirectly
i.返回 NO,则执行setValue:forUNdefinedKey:
ii.返回 YES,则按```_<key>```,```_<isKey>```,```<key>```,```<isKey>```的顺序搜索成员名。
3.还没有找到的话,就调用setValue:forUndefinedKey:
11.深拷贝和浅拷贝的问题:
浅拷贝会增加引用计数
浅拷贝指向同一块内存空间
深拷贝不会增加引用计数
深拷贝产生内存分配
NSString * string = @"hello world";
/** 浅拷贝 */
NSString * copyString = [string copy];
/** 深拷贝 */
NSMutableString * mutableCopyString = [string mutableCopy];
NSLog(@"\nstring = %p\ncopyString = %p\nmutableCopyString= %p",string,copyString,mutableCopyString);
打印结果:
string = 0x106e65398
copyString = 0x106e65398
mutableCopyString= 0x6000022436f0
NSMutableString * mString = [NSMutableString stringWithString:@"hello world"];
/** 浅拷贝 */
NSString * copyMString = [mString copy];
/** 深拷贝 */
NSMutableString * mutableCopyMString = [mString mutableCopy]; NSLog(@"\nmString = %p\ncopyMString = %p\nmutableCopyMString= %p",mString,copyMString,mutableCopyMString);
打印结果:
mString = 0x600002243720
copyMString = 0x600002c25c60
mutableCopyMString= 0x6000022438a0
可变对象的copy和mutableCopy都是深拷贝
不可变对象的copy是浅拷贝,mutableCopy是深拷贝
copy方法返回的都是不可变对象
- 使用@property(copy) NSMutableArray *array; 会出现什么问题?
copy方法返回的都是不可变对象,如果操作数据就会程序异常。
- copy,strong和拷贝之间的关系
copy,strong和深浅拷贝没有完全对应关系
简单来说,希望不跟着源头改变,就用copy,跟着改变就用strong
12.MRC下如何重写retain修饰变量的setter方法?
13.#include,#import与@class的区别?
#include是C中用来引用文件的关键字;
#import是OC中用来引用文件关键字;
@class仅仅是告诉编译器有这么一个类, 具体这个类里有什么信息, 完全不知。
引入@class主要是用来解决引用死锁。如果两个类存在循环依赖关系,即A->B,B->A,如果用#import来相互包含,就会出现编译错误:
Expected specifier-qualifier-list before ‘A’或者Expected specifier-qualifier-list before ‘B’。
一般情况下,在 .h文件中,只需要知道类的名字就可以了,所以用@class,而在 .m文件中通常需要知道类的成员变量即方法,所以要用#import来将类文件导进来。
*****************内存管理***********
*****************UI相关问题***********
1.tableView 的重用机制?
tableView的重用机制就是每次只创建屏幕显示区域内的cell,通过重用标识符identifier来标记cell, 当cell要从屏幕外移入屏幕内时, 系统会从重用池内找到相同标识符的cell, 然后拿来显示。
2.如何操作tableView的数据源同步问题??
1.并发访问,数据拷贝
并发及多个线程都可以执行在同一段时间,不需要互相等待,主线程与用户互动,子线程加载cell所需要的网络数据以及预排版
解决方法:如图,主线程首先拷贝一份数据给子线程完成预排版,网络请求与数据解析(json xml转化),这时候如果主线程需要删除某些数据源操作,他就记录这条删除操作,在子线程完成各种加载操作后将这条操作与子线程进行同步一下,然后再回到主线程刷新界面
缺点:可能需要拷贝大量数据,比较消耗内存
2.串行访问
创建一个GCD串行队列,主线程增删改操作需要等待子线程操作完成
解决方法:首先使用GCD创建一个串行队列,子线程先加入队列完成网络加载操作,如果这时候主线程需要修改数据源,这个操作就要等待子线程完成才去进行(串行执行)
缺点:可能子线程的网络请求速度慢,主线程UI操作等待时间长
3.事件传递的机制以及视图响应链?
UIApplication接收到事件,将事件传递给keyWindow。
keyWindow遍历subViews的hitTest:withEvent:方法,找到点击区域内合适的视图来处理事件。
UIView的子视图也会遍历其subViews的hitTest:withEvent:方法,以此类推。
直到找到点击区域内,且处于最上方的视图,将视图逐步返回给UIApplication。
在查找第一响应者的过程中,已经形成了一个响应者链。
应用程序会先调用第一响应者处理事件。
如果第一响应者不能处理事件,则调用其nextResponder方法,一直找响应者链中能处理该事件的对象。
最后到UIApplication后仍然没有能处理该事件的对象,则该事件被废弃。
4.ui的卡顿和掉帧的原因?
在规定的时间内,在下一帧VSync信号到来之前,CPU和GPU没有合成下一帧画面合成。于是就会卡顿和掉帧。
1、UI渲染需要时间较长,无法按时提交结果。
2、一些需要密集计算的处理放在了主线程中执行,导致主线程被阻塞,无法渲染UI界面。
3、网络请求由于网络状态的问题响应较慢,UI层由于没有模型返回无法渲染。
5.什么时候会触发离屏渲染?
当我们设置某一些UI视图的图层属性,如果指定为在离屏显示前不能直接显示的时候。那么就触发了离屏渲染。包括视图的圆角属性 和 蒙层遮罩。 而离屏渲染的概念,起源于GPU层面.指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。
触发的场景:
1.设置圆角(当和maskToBounds一起使用时)
2.设置图层蒙版
3.阴影
4.光栏化
PS:
什么是离屏渲染
指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作
什么是在屏渲染
指在当前屏幕渲染,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行
为何要避免?
触发离屏渲染会增加GPU的工作量。会可能导致CPU+GPU的工作量超出了16.7毫秒。导致UI的卡顿和掉帧。
*****************Block***********
*****************runtime***********
*****************多线程***********
iOS 多线程入门02--NSThread
iOS 多线程入门03--GCD
iOS 多线程入门04--NSOperation
多线程提升
iOS 多线程的安全隐患(线程锁)
*****************RunLoop***********
*****************网络相关***********
*****************其他***********
1.static 关键字的作用?
static静态变量在其他类中是不能通过类名直接访问的,它的作用域只能是在声明的这个.m文件中 。它在程序运行时候只会创建一次,并且在多次调用的时候会保存数值。
2.简述内存分区情况?
1).代码区:存放函数二进制代码
2).数据区:系统运行时申请内存并初始化,系统退出时由系统释放。存放全局变量、静态变量、常量
3).堆区:通过alloc等函数或new等操作符动态申请得到,需程序员手动申请和释放
4).栈区:函数模块内申请,函数结束时由系统自动释放。存放局部变量、函数参数
3.通知的执行方法是在主线程运行还是在分线程运行?
如果将发送通知的方法postNotification写在了主线程,它的执行方法会在主线程运行,如果将postNotification方法写在了子线程内,会在子线程内运行。
4.字典的实现原理?
NSDictionary(字典)是使用hash表来实现key和value之间的映射和存储的。
5.alloc和new的区别?
[className new]基本等同于[[className alloc] init]. 区别只在于alloc分配内存的时候使用了zone,它是给对象分配内存的时候,把关联的对象分配到一个相邻的内存区域内,以便于调用时消耗很少的代价,提升了程序处理速度。
6.空指针与野指针的区别?
1.空指针
1.没有存储任何内存地址的指针就称为空指针(NULL指针)
2.空指针就是被赋值为0的指针,在没有被具体初始化之前,其值为0。
2.野指针
"野指针"不是NULL指针,是指向"垃圾"内存(不可用内存)的指针。野指针是非常危险的。
总结
1.利用野指针发消息是很危险的,会报错。也就是说,如果一个对象已经被回收了,就不要再去操作它,不要再尝试给它发消息。
2.利用空指针发消息是没有任何问题的,也就是说下面的代码是没有错误的。
7.堆和栈的区别?
栈区:由编译器自动分配释放,存放函数的参数值,局部变量值等;
堆区:一般由程序员分配释放(使用new/delete或malloc/free),若程序员不释放,程序结束时可能由OS回收;
栈里面存放的是非对象的基本数据类型(包括指针),堆内存存放着oc对象
8. http和scoket通信的区别?
http是客户端用http协议进行请求,发送请求时候需要封装http请求头,并绑定请求的数据,服务器一般有web服务器配合(当然也非绝对)。 http请求方式为客户端主动发起请求,服务器才能给响应,一次请求完毕后则断开连接,以节省资源。服务器不能主动给客户端响应(除非采取http长连接 技术)。iphone主要使用类是NSUrlConnection。
scoket是客户端跟服务器直接使用socket“套接字”进行连接,并没有规定连接后断开,所以客户端和服务器可以保持连接通道,双方 都可以主动发送数据。一般在游戏开发或股票开发这种要求即时性很强并且保持发送数据量比较大的场合使用。主要使用类是CFSocketRef。
9. 队列和栈有什么区别?
答:队列和栈是两种不同的数据容器。从”数据结构”的角度看,它们都是线性结构,即数据元素之间的关系相同。
队列是一种先进先出的数据结构,它在两端进行操作,一端进行入队列操作,一端进行出列队操作。
栈是一种先进后出的数据结构,它只能在栈顶进行操作,入栈和出栈都在栈顶操作。
10.SDWebImage的实现原理?
11.KVO和KVC的区别?
KVO:键值观察机制,他提供了观察某一属性变化的方法,极大的简化了代码。
KVC:键 – 值编码,是一种间接访问对象的属性使用字符串来标识属性,而不是通过调用存取方法,直接或通过实例变量访问的机制。
12.视图的生命周期?
iOS程序执行顺序和UIViewController 的生命周期(整理)
13. 请简要说明viewDidLoad和viewDidUnload何时调用?
答: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 。
14.描述一下面向过程编程和面向对象编程的区别以及优缺点?
面向过程编程:以事件为中心的编程思想。就是分析出解决问题所需的步骤,然后用函数把这些步骤实现,并按顺序调用,实现整个程序的功能。
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展 。
面向对象编程:面向对象是一种以“对象”为中心的编程思想,把具体的数据和操作封装起来,来构造和实现整个软件的功能。面向对象包含三个基本特征:继承,多态,封装。
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护 。
缺点:性能比面向过程低 。
备注:
1.多态就是父类指针指向子类指针。一个对象有着多重特征,可以在特定的情况下,表现不同的状态,从而对应着不同的属性和方法。
2.多态在继承中的使用:
1):父类指针可以接收子类对象(赋值兼容规则)
2):父类的指针指向不同的子类对象,调用相同的方法,效果不同
15. imageNamed和imageWithContentsOfFile/+imageWithData:(scale:)/-initWithContentsOfFile:/-initWithData:的区别?
1. +imageNamed:该方法使用系统缓存,适合表视图重复加载图像的情形。同时该API根据UIScreen的scale,自动查找包含对应高倍图后缀名(@2x)的文件,如果找到二倍图,则image.scale=2.0,对应逻辑size大小以point度量(pixel度量的一半);如果没找到设置默认image.scale=1.0,对应逻辑size大小同像素尺寸。因此,使用该方法,无需特意指定高倍图后缀。在实际运行时,系统如果发现当前设备是Retina屏(scale=2),会自动寻找"*@2x.png"命名格式的图片,加载针对Retina屏的图片素材,否则会失真。
2. +imageWithContentsOfFile/+imageWithData:(scale:)/-initWithContentsOfFile:/-initWithData:(scale:)
16.懒加载的时候,使用self调用属性造成崩溃。
错误写法
-(UIView *)bgView {
if (!self.bgView) {
self.bgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
self.bgView.backgroundColor = [UIColor redColor];
}
return self.bgView;
}
正确写法
-(UIView *)bgView {
if (!_bgView) {
self.bgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
self.bgView.backgroundColor = [UIColor redColor];
}
return _bgView;
}
为什么会崩溃?
因为:我们在调用if语句做判断时,如果用self去访问的话,此时会调用他的setter跟getter方法,本身自己还没初始化,是nil,但是getter返回的也是nil,那在判断时就会进入死循环,所以就会报这个错.
17.成员变量,实例变量和属性的区别?
@interface ViewController : UIViewController
{
int count; //成员变量
id data; //实例变量
NSString *name; //实例变量
}
@property (nonatomic, copy) NSString *address; //属性
@end
实例是针对 类而言的。 实例是指类的声明; 由此推理, 实例变量 :是指由类声明的对象。
严格来讲, 上图中的 int count
是一个成员变量。 而 NSString * name
是一个实例变量。 至于id data
应该属于实例变量。因为 id 是OC特有的类型。从本质上讲, id 等同于 (void *)。
18.@public,@private,@protected,@package的区别?
@public:在任何地方都能直接访问对象的成员变量;
@private:只能在当前类的对象方法中直接访问,如果子类要访问需要在父类中实现set和get方法,在子类中调用;
@protected:可以在当前类及其子类对象方法中直接访问(系统默认下是用它来修饰的);
@package:在同一个包下就可以直接访问,比如说在同一个框架。
19.if和switch的使用?
switch通过编译成一个分支表来达到优化的目的,是通过空间的代价来换取时间。if-else语句会对一个个条件按顺序进行查找,直到找到符合条件的"入口"。
大多数情况下switch比if-else运行的要快,但只有当数量条件很大时,才快得明显。当条件增加的时候,if-else性能负担增加的程度比switch明显得多。因此我们倾向于在条件数量比较少的情况下使用if-else,而在条件数量较大的时候使用switch。
20.dispatch_get_main_queue 与 performSelectorOnMainThread 的区别?
都是获取主线程,但是他们的RunLoopMode不一样。
dispatch_get_main_queue:在当前的runloopmode中执行 。所有模式下都可运行。
performSelectorOnMainThread:会在defaultMode中执行,。如果运行循环处于另一种模式(例如跟踪模式),则在运行循环切换回默认模式之前它不会运行。可以使用该变体来解决此问题-performSelectorOnMainThread:withObject:waitUntilDone:modes:(通过传递您希望它在其中运行的所有模式)。
21.@protocol除了用作代理还能干什么?
面向协议开发
(为了方法定义)
做逻辑抽象
(辑抽象就是为了某一类行为做个抽象比如有啥功能做方法定义抽象,但是不是具体的实现类,然后根据自己具体实现类来遵守协议实现对应方法。比如支付,分享)
服务实现插件
非必要实现的功能或者属性都可以用@protocol声明利于后面扩展
22.APNS的推送原理?
1.应用程序的服务器端把要发送的消息、目的iPhone的标识打包,发给APNS。
2.APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的iPhone,并把消息发送到iPhone。
3.iPhone把发来的消息传递给相应的应用程序,并且按照设定弹出Push通知。
1.app注册接收推送时,系统会发送请求给APNS服务。APNS服务收到请求后会生成一个deviceToken(UDID + App's Bundle Identifier))。
2.app接收deviceToken。
3.app将deviceToken发送给app的后台服务端。
4.后台服务端将消息发送给APNS服务。
5.APNS服务将消息发送给app。
23.SDWebImage的实现?
SDImageCache负责图片缓存相关的工作,SDWebImageDownloader负责图片下载相关的工作,SDWebImageManager则是将前两者结合起来完成整个工作流程。
首先进行请求的url对应的urlkey去查找内存缓存。如果内存当中查找到了就返回给调用方。
如果内存缓存中查找不到,就去查找磁盘缓存,磁盘查找找到了,就返回给调用方。如果仍然查找不到,就发起新的网络请求下载。
24.AFNetworking 底层原理分析?
25.frame和bounds区别?
frame: 该view在父view坐标系统中的位置和大小。(参照点是父类的坐标系统)
bounds:该view在本地坐标系统中的位置和大小。(参照点是本地坐标系统,就相当于ViewB自己的坐标系统,以0,0点为起点)。
经典面试题汇总
《招聘一个靠谱的iOS》面试题参考答案(上)
《招聘一个靠谱的iOS》面试题参考答案(下)
部分内容摘自于网络,如有侵权,可通过简书联系我,谢谢!