iOS的那些面试

笔试面试是程序员应聘绕不过的坎,想进大厂就一定要有知识储备量,而这个却是最佳的考察方式。笔者分享一些收集的iOS面试题,答案仅供参考,如有错误,还请指正。

第一部分

1.Objective-C语言的基本特点?

Objective-C语言是C语言的一个超集,只是在C的基础之上添加了面向对象(OO)的特性;Objective-C与Java语言相同都是单继承,这一点与C++语言不同(多重继承),这一点可以参看之前有关讲解继承的文章;Objective-C不支持命名空间,取而代之的是在类名之前添加前缀,以此来区分。

2.#include与#import的区别,#import与@class的区别?

1)#include和#import效果相同,只是后者不会引起交叉编译,确保头文件只会被导入一次。

2)#import会包含这个类的所有信息,包括实体变量和方法,而@class只是告诉编译器,其后面声明的名称是类的名称,至于这些类是如何定义的,暂时不用考虑,后面会再告诉你。使用#import编译效率高,防止相互包含的编译错误。

3.简述类目优缺点,如果覆盖本类或者父类的方法会出现什么问题?

优点:不需要通过增加子类而增加现有类的行为(方法),且类目中的方法与原始类方法基本没有区别。

缺点:无法向类目添加实例变量。覆盖原始类方法后,原始类的方法没办法调用。

4.简述内存管理基本原则?

如果使用alloc、copy(mutableCopy)或者retain一个对象时,你就有义务向它发送一条release或autorelease消息。其他方法创建的对象,不需要由你来管理内存。

5.什么是ARC技术?与GC是否相同?

ARC是Automatic Reference Counting的简称,我们称之为自动引用计数,是iOS5.0之后推出的内存管理的新特性。本质上还是使用引用计数来管理对象,只是我们在编写代码时,不需要向对象发送release或autorelease方法,也不可以调用dealloc方法,编译器会在合适的位置自动给用户生成release(autorelease)消息。GC是Garbage Collection,内存垃圾回收机制,ARC比GC性能好。

6.iOS中是否支持垃圾回收机制?

iOS开发只支持MRC与ARC,Mac开发支持GC垃圾回收机制,10.8之后弃用了GC,推荐使用ARC。

7.深、浅复制的基本概念以及区别?

浅复制:只复制对象本身,不对里面的属性复制。

深复制:不仅复制对象本身,对象持有的属性对象也做复制。

8.内存中的堆区和栈区的差别?

栈区(stack)由编译器自动分配释放,存放方法(函数)的参数值,局部变量的值等。

堆区(heap)一般由程序员分配与释放,若程序员不释放,则会内存溢出。

9.用户自定义了一个对象,如何实现拷贝(可变和不可变拷贝)?

必须实现copying和mutableCopying协议,表示返回一个不可变和可变的对象。否则,程序将会出现异常。

1
2
3
4
5
6
7
-(id)copyWithZone:(NSZone *)zone {
Person *person = [[self Class] allocWithZone:zone];
person->age = self.age;
person->name = self.name;
return person;
}
-(id)mutableCopyWithZone:(NSZone *)zone;

10.定义属性时,什么时候用assign、retain、copy和nonatomic?

assign:普通赋值,一般常用于基本数据类型,常见委托设计模式,以此来防止循环引用。

retain:保留计数,获得了对象的所有权。引用计数在原有基础上加1。

copy:用来复制对象,一般字符串使用,Foundation中的不可变对象使用,效果相当于retain,只是引用计数加1。

nonatomic:非原子性访问,不加同步,多线程并发访问会提高性能。

11.strong和weak,_unsafe_unretained与weak的区别?

strong:强引用,在ARC中告诉编译器帮助我们自动插入retain。

weak:弱引用,是普通赋值,相当于手动管理内存的assign。

_unsafe_unretained:与weak功能一致,区别在于当指向的对象销毁后,weak会将变量置为nil,防止调用野指针。

12.ARC存在内存泄露吗?

ARC中如果内存管理不当的话,同样会存在内存泄露。例如,ARC中也会循环引用导致内存泄露;Objective-C对象与CoreFoundation类之间桥接时,管理不当也会产生内存泄露。

13.当我们释放对象时,为什么需要调用[super dealloc]方法?

子类是继承自父类,那么子类中有一些实例变量(对象),是继承自父类的,因此,我们需要调用父类方法,将父类所拥有的实例进行释放。

14.自动释放池是什么,如何工作?

自动释放池是NSAutorelease类的一个实例,当向一个对象发送autorelease消息时,该对象会自动入池,待池销毁时,将会向池中所有对象发送一条release消息,释放对象。

15.为什么delegate(代理)属性都是assign而不是retain的?

防止循环引用,以至对象无法得到正确的释放。

16.delegate使用weak还是strong?为什么?苹果有delegate使用strong的类吗?为什么?

delegate一般使用weak,主要是为了避免出现循环引用。苹果官方NSURLSession类中的delegate使用strong引用。因为苹果要求所有的网络请求都用同一个对象去发送,那么这个对象就封装为一个单例对象。

第二部分

1.iOS开发中数据持久性有哪几种?

plist文件写入,对象归档,sqlite3数据库,CoreData。

2.什么是KVC和KVO?它们之间的关系是什么?

KVC:键值编码,是一种间接访问对象实例变量的机制,该机制可以不通过存取方法就可以访问对象的实例变量。

KVO:键值观察,是一种能使得对象获取到其他对象属性变化的通知机制。
实现KVO键值观察模式,被观察的对象必须使用KVC键值编码来修改它的实例变量,这样才能被观察者观察到。因此,KVC是KVO的基础或者说KVO的实现是建立在KVC的基础之上的。

3.简述常见的设计模式?

单例模式、代理设计、观察者(通知)、工厂方法、模板方法。

4.内存管理在dealloc方法中用release方法与self.xx=nil哪个好?

使用self.xx=nil更好,因为先调用了release方法,而且还将变量设置为nil,这样就更安全的释放对象,防止野指针调用。

5.线程与进程的区别和联系?

一般的应用程序是单个进程,也有多进程(Chrome),进程是个静态的容器,里面容纳了很多个线程,线程是一系列方法的线性执行路径。

6.Objective-C语言的优缺点?

优点:类目、动态识别、支持C语言、Objective-C与C++可以混编。

缺点:不支持命名空间、不支持运算符重载、不支持多重继承。

7.代理delegate、通知Notification与block的使用区别?

delegate和block一般是用于两个对象一对一之间的通信交互,delegate需要定义协议方法,代理对象实现协议方法,并且需要建立代理关系才可以实现通信。block更加简洁,不需要定义繁琐的协议方法,但是如果通信事件比较多的话,建议使用delegate。
Notification主要用于一对多情况下通信,而且,通信对象之间不需要建立关系,但是使用通知,代码可读性差。

8.控制器ViewController的loadView、viewDidLoad、viewWillApear和viewDidUnload分别是在什么时候调用?

loadView:当控制器的根视图view为空,且此view被访问时调用。

viewDidLoad:loadView调用之后被调用。

viewWillApear:当控制器根视图view被添加到父视图上时调用。

viewDidUnload:iOS6.0之前,当内存警告时,先卸载视图,再调用
viewDidUnload来释放内存。

9.@synthesize和@dynamic的区别?

@synthesize:系统自动生成getter和setter属性声明。

@dynamic:告诉编译器,属性的获取与赋值方法由用户自己实现,不自动生成。

10.事件响应者链的概念?

响应者链表示一系列的响应者对象。事件被交由第一响应者对象处理,如果第一响应者不处理,事件被沿着响应者链向上传递,交给下一个响应者。一般来说,第一响应者是个视图对象或者其子类对象,当其被触摸后事件交由它处理,如果它不处理,事件会被传递给它的视图控制器对象(如果存在),然后是它的父视图对象(如果存在),以此类推,直到顶层视图。接下来会沿着顶层视图到窗口(UIWindow对象),再到程序(UIApplication对象)。如果整个过程都没有响应这个事件,该事件被丢弃。一般情况下,在响应者链中只要有对象处理事件,事件就停止传递。但有时候可以在视图的响应方法中根据一些条件判断来决定是否需要继续传递事件。

11.C语言中的static变量和static函数有什么作用?

1)表示变量是静态存储变量,表示变量存放在静态存储区。

2)加在函数前面的时候表示该函数是内部连接,只在本文件中有效,别的文件中不能使用该函数。

12.指针题

1
2
3
4
5
6
7
8
1)int a; //一个数值。
2)int *a; //一个指向数值的指针。
3)int **a; //一个指向指针的指针。
4)int a[10]; //一个有10个指针的数组,该指针是指向一个整型数的。
5)int *a[10]; //一个有10个指针的数组,该指针是指向一个整型数的。
6)int (*a)[10]; //一个指向有10个整型数数组的指针。
7)int (*a)(int); //一个指向函数的指针,该函数有一个整型参数并返回一个整型数。
8)int (*a[10])(int); //一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数。

13.写一个NSString类的实现?

1
2
3
4
5
6
+(id)initWithCString:(const char *)nullTerminatedCString encoding:(NSStringEncoding)encoding {
NSString *obj;
obj = [self allocWithZone:NSDefaultMallocZone()];
obj = [obj initWithCString:nullTerminatedCString encoding:encoding];
return autorelease(obj);
}

14.Objective-C与C如何混用?Objective-C与C++如何混用?

实现文件的扩展名.m改成.mm即可,但cpp文件必须只能使用C/C++代码,而且cpp文件include的头文件中,也不能出现Objective-C的代码,因为cpp只能写C++的代码。

15.Objective-C中有私有方法和私有变量吗?

使用private修饰的全局变量是私有的变量。

Objective-C在语法上没有设计私有的方法,不过可以通过延展在实现文件里定义方法作为私有的方法。但不是真正意义上的私有方法,也是可以调用的,只是外部不可见而已。

第三部分

1.下面关键字const有什么含义?

1
2
3
4
const int a;
int const a;
const int *a;
int * const a;

前两个的作用是一样的,a是一个常整型数。

第三个意味着a是一个指向常整型数的指针(即整型数是不可修改的,但指针可以)。

第四个意味着a是一个指向整型数的常指针(即指针指向的整型数是可以修改的,但指针是不可修改的)。

2.C语言中extern的作用,extern “C”的作用?

1)extern可以置于变量或者函数前,以标示变量或函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。

2)C++语言在编译的时候为了解决函数的多态问题,会将函数名和参数联合起来生成一个中间的函数名称,而C语言则不会,因此会造成链接时找不到对应函数的情况,此时C函数就需要用extern “C”进行链接指定,这告诉编译器,请保持我的名称,不要给我生成用于链接的中间函数名。

3.Objective-C是如何管理内存的?

Objective-C语言是使用引用计数来管理一个对象的生命周期,当对象被其他“人”使用的时候引用计数加1,不使用时减1,当此对象的引用计数为0时,则对象被系统回收。

4.什么是动态绑定(多态)?

动态绑定是面向对象特性之一多态的体现,声明一个变量,在运行的时候可以绑定不同的对象,比如在方法的参数中声明这样一个变量UIView *view,运行的时候,我们可以根据不同的应用场景给view变量传入不同的对象,可以传递一个UIButton对象,也可以传入UIImageView对象。

5.如何理解delegate?

delegate,又称为委托或代理,它是一种设计模式。delegate主要用于两个对象之间的通信交互,并且解除两个通信对象的耦合性,iOS中大量使用了delegate设计,主要用于视图与使用对象之间的通信交互。

6.block与函数有何异同?block有何优点?

1)block类似于C里面的函数指针,都可以作为参数进行传递,用于回调。但是block的实现可以定义在方法中,函数则不可以。

2)block语法简洁,可以在方法中定义实现,这样可以访问方法中的局部变量,使代码更加的紧凑,结构化。

7.为什么标准头文件都有类似以下的结构?

1
2
3
4
5
6
7
8
9
10
#ifndef __INCvxWorksh
#define __INCvxWorksh
#ifdef __cplusplus
extern "C" {
#endif
/*...*/
#ifdef __cplusplus
}
#endif
#endif /*__INCvxWorksh*/

显然,头文件中的编译宏“#ifndef __INCvxWorksh、#define __INCvxWorksh、#endif”的作用是防止该头文件被重复引用。

8.CocoaTouch框架?

UIKit、Foundation、CoreGraphic、CoreAnimation

1)音频和视频:Core Audio、OpenAL、Media Library、AV Foundation

2)数据管理:Core Data、SQLite

3)图形和动画:Core Animation、OpenGL ES、Quartz 2D、Core Graphic

4)用户应用:Address Book、Core Location、Map Kit、Store Kit

9.栈结构与队列的区别?

栈(stack):限定只能在表的一端进行插入和删除操作的线性表。

队列(queue):限定只能在表的一端插入和在另一端进行删除操作的线性表。

1)队列先进先出,栈先进后出。

2)对插入和删除操作的“限定”不同。

3)遍历数据速度不同。队列遍历数据的速度要快得多。

10.Objective-C中有无多重继承?如何实现多重继承?

Objective-C是没有多重继承的。

Cocoa中所有的类都是NSObject的子类,多继承在这里是用protocol委托代理来实现的。

面向对象语言大部分都是单继承,例如:Java、C#。

C++支持多继承。

11.static全局变量与普通的全局变量的区别?局部变量呢?函数呢?

1)static全局变量与普通全局变量的区别:static全局变量只初始化一次,防止在其他文件单元中被引用。

2)static局部变量与普通局部变量的区别:static局部变量只被初始化一次,下一次依据上一次结果值。

3)static函数与普通函数的区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝。

12.什么是类目与延展?

类目:为已知的类增加新的方法。

延展:通知在本类的定义里使用类目来声明私有方法。

13.Objective-C中的协议和Java中的接口区别?

协议定义的方法,可以设置可选实现和必须实现。

接口则是必须全部实现。

14.在一个对象的方法里self.name=”object”和name=”object”有什么不同?

前者调用的是设置器方法,后者是普通赋值。

15.单例设计模式的实现?为什么使用单例设计?

1
2
3
4
5
6
7
8
9
10
11
12
static File *instance = nil;
@implementation File
//获取单例的方法
+(id)shareInstance {
@synthesize (self) {
if(instance == nil) {
instance = [[File alloc] init];
}
}
return instance;
}
//覆写allocWithZone、copyWithZone、retain、release和autorelease方法,目的是限制这个类只创建一个对象

为什么使用单例设计?

1)单例设计是用来限制一个类只能创建一个对象,那么此对象中的属性可以存储全局共享的数据,所有的类都可以访问、设置此单例对象中的属性数据。

2)如果一个类创建的时候非常的耗费性能,那么此类如果能满足要求,可以设置为单例节约性能。

第四部分

1.层CALayer和UIView的区别是什么?

两者最大的区别是,图层不会直接渲染到屏幕上。UIView是iOS系统中界面元素的基础,所有的界面元素都是继承自它,它本身是由CoreAnimation来实现的,它真正绘图的部分是由一个CALayer类来管理的。UIView本身更像是一个CALayer的管理器。一个UIView上可以有多个CALayer,每个CALayer显示一种东西,增强UIView的展现能力。

2.什么是GCD?

GCD是Apple开发的一个多核编程的较新的解决方法。在Mac OS X 10.6雪豹中首次推出,并引入到iOS4.0。GCD是一个替代诸如NSThread等技术的很高效和强大的技术。GCD完全可以处理诸如数据锁定和资源泄漏等复杂的异步编程问题。

3.TCP/UDP的区别和联系?

1)TCP的全称为传输控制协议,这种协议可以提供面向连接的、可靠的、点到点的通信。

2)UDP的全称为用户数据报协议,它可以提供非连接的不可靠的点到多点的通信。

3)用TCP还是UDP,需要看程序注重哪一个方面,需要可靠还是快速。

4.Socket连接和Http连接的区别?

Http连接:Http连接就是所谓的短链接,即客户端向服务器发送一次请求,服务器响应后连接即会断掉。

Socket连接:Socket连接就是所谓的长连接,理论上客户端和服务器端一旦建立起连接将不会主动断掉。

5.什么是TCP连接的三次握手?

第一次握手:客户端发送SYN包(SYN=j)到服务器,并进入SYN_SEND状态,等待服务器确认。

第二次握手:服务器收到SYN包,必须确认客户的SYN(ACK=j+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。

第三次握手:客户端接收到服务器的SYN+ACK包,向服务器发送确认包ACK(ACK=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

6.frame和bounds的区别?

frame指的是:该view在父view坐标系统中的位置大小(参照点是父视图的坐标系统)。

bounds指的是:该view在本身坐标系统中的位置和大小(参照点是本身的坐标系统)。

7.如何理解MVC设计模式?

MVC是一种架构设计,M表示数据模型,V表示视图,C表示控制器。

数据模型:负责存储、定义、操作数据。

视图:用来展示数据给用户,和用户进行操作交互。

控制器:它是M与V的协调者,控制器获取数据,将数据交给视图去展示。

8.控制器的xib是怎么加载的?

当UIViewController的loadView被调用时,在此方法中,通过NSBundle加载xib,先通过控制器的类名作为xib的文件名加载此xib文件。如果找到此xib文件,则加载为view对象作为控制器的根视图,如果没有xib文件,则通过alloc创建一个view对象作为根视图。

9.控制器如何处理系统内存不足警告?

内存不足时,系统会调用控制器didReceiveMemoryWaring方法通知控制器内存不足。iOS6.0与6.0之前的处理方式不一样。

1)iOS6.0之前:调用didReceiveMemoryWaring后,将self.view设置为nil,并且再调用viewDidUnload方法,在此方法中我们应该释放子视图。

2)iOS6.0之后:调用didReceiveMemoryWaring后,不再调用viewDidUnload方法,则应该在didReceiveMemoryWaring方法中手动将self.view=nil,并且释放子视图。

10.iOS中对象间有哪些通信方式?

代理delegate、block、通知和KVO。

11.block在内存管理上的特点?需要注意循环引用,如何解决循环引用?

1)block中使用了局部对象,则会将此对象retain,引用了当前对象的属性或者方法,则会将当前对象retain。

2)解决循环引用:将当前对象赋值给一个局部变量,并且使用__block关键字修饰该局部变量,使用该变量访问当前对象的属性和方法。

12.Objective-C中有线程池(线程队列)吗?NSOperationQueue是如何管理线程的?

NSOperationQueue是Objective-C的线程池,线程池中可以存放多个线程。

NSOperationQueue可以管理线程的并发数,还可以管理线程间的优先级。

13.timer的间隔周期准吗?为什么?怎样实现一个精准的timer?

定时器timer一般都是准确的,但是当主线程有些时候难免会出现堵塞情况,这样就有可能导致定时器timer会延迟从而不准确。我们可以开发一个多线程,在此多线程上运行定时器,这样多线程只运行定时器,不会因堵塞而导致误差。

14.Http协议的特点?GET与POST的区别?什么是Https协议?

1)Http协议是短链接,都是客户端主动发送请求,服务器做出响应,服务器响应之后,连接则断开。

2)GET请求没有请求体,POST含有请求体,请求参数可以放入请求体中,所以,POST可以提交大量的数据给服务器,更加安全。

3)HTTPS(Secure Hypertext Transfer Protocol)安全超文本传输协议,它是一个安全通信通道,它基于HTTP开发,用于在客户计算机和服务器之间交换信息。它使用安全套接字层(SSL)进行信息交换,简单来说它是HTTP的安全版。

15.XML数据的解析方式各有什么不同?JSON解析有哪些框架?

XML数据解析有两种方式:DOM解析和SAX解析。

DOM解析:必须完成DOM树的构造,在处理规模较大的XML文档时就很耗费内存,占用资源较多。

SAX解析:与DOM不同的是,SAX是用事件驱动模型,解析XML文档时每遇到一个开始或者结束标签、属性或一条指令时,程序就产生一个事件来进行相应的处理。因此,SAX相对于DOM来说更适合操作大的XML文档。

JSON解析:现在性能比较好的主要是第三方的JSONKit和iOS自带的JSON解析类,自带的JSON解析性能是最高的,但是只能是iOS5之后才可以使用。

第五部分

1.tableView是如何复用的?

如果屏幕上能显示10个单元格,则tableView只会创建11个单元格,也就是n+1,当滑到第12个单元格时就会复用第1个单元格对象。tableView中有个单元格池的概念,tableView调用协议方法获取单元格时,先从池子中查找是否有可复用的单元格,如果有则复用,如果没有则创建一个单元格对象。

2.如何优化tableView的滑动速度?

1)复用单元格。

2)使用不透明的视图,单元格中尽量少使用动画。

3)图片加载使用异步加载,并且设置图片加载的并发数。

4)滑动时不加载图片,停止滑动开始加载。

5)文字、图片可直接drawInRect绘制。

6)如非必要,减少reloadData全部cell,只reloadRowsAtIndexPaths。

7)如果cell是动态行高度,计算出高度后缓存。

8)cell高度固定的话直接用cell.rowHeight设置高度。

3.谈谈对Runloop的理解?

Run loops是线程相关的基础框架的一部分。一个run loop就是一个事件处理的循环,用来不停的调度工作以及处理输入事件。使用run loop的目的是让你的线程在有工作的时候忙于工作,而没有工作的时候处于休眠状态。

4.如何调用drawRect方法与layoutSubView方法?这两个方法的作用?

通过setNeedsLayout方法异步调用layoutSubView方法。

通过setNeedsDisplay方法异步调用drawRect方法。
drawRect方法用于绘图,layoutSubView方法用于布局子视图。

5.UIView与UIViewController的关系?

每个控制器都有一个根视图,控制器UIViewController用来管理此视图的加载和显示。

6.iOS中有哪些手势?

轻击、捏合、平移、轻扫、旋转和长按。

7.Http和Socket的区别?

Http是客户端用Http协议进行请求,发送请求时需要封装http请求头,并绑定请求的数据,服务器一般有Web服务器配合(当然也非绝对)。Http请求方式为客户端主动发起请求,服务器才能给响应,一次请求完毕后则断开连接,以节省资源。服务器不能主动给客户端响应(除非采用http长连接技术)。iPhone主要使用的类是NSURLConnection。

Socket是客户端跟服务器直接使用Socket套接字进行连接,并没有规定连接后断开,所以客户端和服务器可以保持连接通道,双方都可以主动发送数据。一般在游戏开发或股票开发,这种要求即时性很强并且保持发送数据量比较大的场合使用。主要使用的类是CFSocketRef。

8.storyboard有什么特点?

storyboard是iOS5新增的特性,是对xib的升级版本,引入了一个容器用于管理多个xib文件,和它们之间的跳转交互。

优点:不用再为每个控制器创建xib文件;可以使用静态cell,当cell固定且不多时,使用起来比较方便。

缺点:storyboard单个文件,不利于团队协作开发。

9.如何打包静态库?

新建一个Framework&Library的项目,编译的时候会将项目中的代码文件打包成一个.a静态库文件。

10.APP发布的上架流程?

1)在苹果官网的开发者中心,新建一个APP,填写此APP相关的一些描述信息。

2)下载安装发布证书。

3)选择发布证书,使用Archive编译发布包。

4)使用Xcode提交发布包。

11.iOS5有哪些新特性?

iCloud、定制UI、storyboard、ARC、CoreImage滤镜、新增JSON解析类。

12.iOS6有哪些新特性?

UIRefreshControl水滴效果下拉刷新、UICollectionView控件的使用、SLComposeViewController新浪微博控件、PassKit中Passbook、AutoLayout自动布局。

13、iOS7有哪些新特性?

系统:全新的UI设计与交互;Control Center控制中心;全新的多任务Multitasking;AirDrop共享文件;全新的Safari浏览器交互式体验。

SDK:动态UIKit,新增了UIDynamicItem委托,用来为UIView制定动态行为;新增游戏引擎Sprite Kit Framework框架,类似于Cocos2D。

14.ARC有什么特点?

ARC是编译器特性,iOS5.0新添加的特性,使用ARC开发者不需要再retain、release、autorelease,因为编译器会在合适的地方自动插入retain、release。

ARC不会因少release而导致内存泄漏,过度使用release导致程序崩溃,

ARC可以产生更简洁的代码和更健壮的应用。

15.SVN、Git协作开发,怎么防止代码文件冲突?

防止代码冲突:不要多人同时修改同一个文件。例如,A、B都修改同一个文件,先让A修改,然后提交到服务器,然后B更新下来,再进行修改。
服务器上的项目文件xcodeproj,仅让一个人管理提交,其他人只更新。防止此文件产生冲突。

第六部分

1.什么是ARC?

ARC是automatic reference counting自动引用计数,在程序编译时自动加入retain/release。在对象被创建时retain count+1,在对象被release时count-1,当count=0时,销毁对象。程序中加入autoreleasepool对象会由系统自动加上autorelease方法,如果该对象引用计数为0,则销毁。那么ARC是为了解决MRC手动管理内存存在的一些而诞生的。

MRC下内存管理的缺点:

1)释放一个堆内存时,首先要确定指向这个堆空间的指针都被release了。(避免提前释放)

2)释放指针指向的堆空间,首先要确定哪些指向同一个堆,这些指针只能释放一次。(避免释放多次,造成内存泄露)

3)模块化操作时,对象可能被多个模块创建和使用,不能确定最后由谁释放。

4)多线程操作时,不确定哪个线程最后使用完毕。
虽然ARC给我们编程带来的很多好多,但也可能出现内存泄露。如下面两种情况:

1)循环参照:A有个属性参照B,B有个属性参照A,如果都是strong参照的话,两个对象都无法释放。

2)死循环:如果有个ViewController中有无限循环,也会导致即使ViewController对应的view消失了,ViewController也不能释放。

2.block一般用哪个关键字修饰,为什么?

block一般使用copy关键之进行修饰,block使用copy是从MRC遗留下来的“传统”,在MRC中,方法内容的block是在栈区的,使用copy可以把它放到堆区。但在ARC中写不写都行:编译器自动对block进行了copy操作。

3.用@property声明的NSString(或NSArray,NSDictionary)经常

使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
用@property声明 NSString、NSArray、NSDictionary 经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。

如果我们使用是strong,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性。

copy此特质所表达的所属关系与strong类似。然而设置方法并不保留新值,而是将其“拷贝” (copy)。 当属性类型为NSString时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例。这个类是NSString的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。

4.runloop、autorelease pool以及线程之间的关系?

每个线程(包含主线程)都有一个Runloop。对于每一个Runloop,系统会隐式创建一个Autorelease pool,这样所有的release pool会构成一个像callstack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的Autorelease pool会被销毁,这样这个pool里的每个Object会被release。

5.@property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的?

“属性”(property)有两大概念:ivar(实例变量)、存取方法(access method=getter),即@property = ivar + getter + setter。
例如下面的这个类:

1
2
3
4
@interface WBTextView :UITextView  
@property (nonatomic,copy)NSString *placehold;
@property (nonatomic,copy)UIColor *placeholdColor;
@end

类完成属性的定以后,编译器会自动编写访问这些属性的方法(自动合成autosynthesis),上述代码写出来的类等效与下面的代码:

1
2
3
4
5
6
@interface WBTextView :UITextView  
- (NSString *)placehold;
-(void)setPlacehold:(NSString *)placehold;
-(UIColor *)placeholdColor;
-(void)setPlaceholdColor:(UIColor *)placeholdColor;
@end

详细介绍见:http://blog.csdn.net/jasonjwl/article/details/49427377

6.分别写一个setter方法用于完成@property (nonatomic,retain)NSString *name@property (nonatomic,copy) NSString *name

retain属性的setter方法是保留新值并释放旧值,然后更新实例变量,令其指向新值。顺序很重要。假如还未保留新值就先把旧值释放了,而且两个值又指向同一个对象,先执行的release操作就可能导致系统将此对象永久回收。

1
2
3
4
5
6
7
8
9
-(void)setName:(NSString *)name{
[name retain];
[_name release];
_name = name;
}
-(void)setName:(NSString *)name{
[_name release];
_name = [name copy];
}

7.说说assign和weak,_block和 _weak的区别?

assign适用于基本数据类型,weak是适用于NSObject对象,并且是一个弱引用。

assign其实也可以用来修饰对象,那么为什么不用它呢?因为被assign修饰的对象在释放之后,指针的地址还是存在的,也就是说指针并没有被置为nil。如果在后续内存分配中,刚巧分到了这块地址,程序就会崩溃掉。而weak修饰的对象在释放之后,指针地址会被置为nil。

_block是用来修饰一个变量,这个变量就可以在block中被修改。

_block:使用_block修饰的变量在block代码块中会被retain(ARC下,MRC下不会retain)。

_weak:使用_weak修饰的变量不会在block代码块中被retain。

8.请说出下面代码是否有问题,如果有问题请修改?

1
2
3
4
5
6
@autoreleasepool {
for (int i=0; i<largeNumber; i++) {
Person *per = [[Person alloc] init];
[per autorelease];
}
}

内存管理的原则:如果对一个对象使用了alloc、copy、retain,那么你必须使用相应的release或者autorelease。咋一看,这道题目有alloc,也有autorelease,两者对应起来,应该没问题。但autorelease虽然会使引用计数减一,但是它并不是立即减一,它的本质功能只是把对象放到离他最近的自动释放池里。当自动释放池销毁了,才会向自动释放池中的每一个对象发送release消息。这道题的问题就在autorelease。因为largeNumber是一个很大的数,autorelease又不能使引用计数立即减一,所以在循环结束前会造成内存溢出的问题。

解决方案如下:

1
2
3
4
5
6
7
8
@autoreleasepool {
for (int i=0; i<100000; i++) {
@autoreleasepool {
Person *per = [[Person alloc] init];
[per autorelease];
}
}
}

在循环内部再加一个自动释放池,这样就能保证每创建一个对象就能及时释放。

9.请问下面代码是否有问题,如有问题请修改?

1
2
3
4
5
6
7
8
9
@autoreleasepool {
NSString *str = [[NSString alloc] init];
[str retain];
[str retain];
str = @"jxl";
[str release];
[str release];
[str release];
}

这道题跟第8题一样存在内存泄露问题,

1)内存泄露 ;

2)指向常量区的对象不能release。

指针变量str原本指向一块开辟的堆区空间,但是经过重新给str赋值,str的指向发生了变化,由原来指向堆区空间,到指向常量区。常量区的变量根本不需要释放,这就导致了原来开辟的堆区空间没有释放,造成内存泄露。

10.什么情况下使用weak关键字,相比assign有什么不同?什么情况使用weak关键字?

1)在ARC中,在有可能出现循环引用的时候,往往要通过让其中一端使用weak来解决。比如delegate代理。

2)自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用weak,自定义控件属性一般也使用weak。

不同点:

1)weak此特性表明该属性定义了一种“非拥有关系”。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特性与assign一样,然而在属性所指的对象遭到销毁时,属性值也会清空。而assign的“设置方法”只会执行针对“纯量类型” (scalar type,例如 CGFloat 或 NSlnteger 等)的简单赋值操作。

2)assign可以用非OC对象,而weak必须用于OC对象。

11.内存管理语义(assign、strong、weak等的区别)?

1)assign “设置方法” 只会执行针对“纯量”的简单赋值操作。

2)strong 此特性表明该属性定义了一种“拥有关系”。为这种属性设置新值时,设置方法会先保留新值,并释放旧值,然后再将新值设置上去。

3)weak 此特性表明该属性定义了一种“非拥有关系”。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特性同assign类似,然而在属性所指的对象遭到销毁时,属性值也会清空。

4)unsafe_unretained 此特性的语义和assign相同,但是它适用于“对象类型”,该特性表达一种“非拥有关系”,当目标对象遭到销毁时,属性值不会自动清空,这一点与weak有区别。

5)copy 此特性所表达的所属关系与strong类似。然而设置方法并不保留新值,而是将其“拷贝”。当属性类型为NSString*时,经常用此特性来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例。这个类是NSString的子类,表示一种可以修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变”的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的”,就应该在设置新属性值时拷贝一份。