iOS程序员面试分类模拟26_第1页
iOS程序员面试分类模拟26_第2页
iOS程序员面试分类模拟26_第3页
iOS程序员面试分类模拟26_第4页
iOS程序员面试分类模拟26_第5页
已阅读5页,还剩9页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

iOS程序员面试分类模拟26简答题1.

View和View之间的传值方式有哪些?正确答案:以将控制器VCSource中的值传到VCTarget中为例,可以有以下6种传值方法。

1)通过视图类(江南博哥)对象的public属性变量直接传值:可以在VCSource的头文件中定义public属性变量,这样在VCTarget中只要有VCSource的引用,即可直接访问其中的public属性变量值。

2)公有方法或代理方法的参数传值:在VCSource中调用VCTarget的公有方法并传入参数,即实现了在VCTarget中访问VCSource中的参数值;在VCTarget中实现VCSource的带参数代理方法即可获取VCSource中的值。

3)NSUserDefault简单数据存储全局共享传值:即在VCSource中存值,然后在VCTarget中将值取出进行访问使用。

4)利用通知中心NSNotificationCenter:即让VCSource向通知中心发出通知并传入参数值,同时VCTarget监听该通知从而访问VCSource传来的参数值。

5)block块传值:利用block块在视图间传值的主要应用是定义接口和回调。例如,在VCSource中定义带有block回调的对外接口方法,并将回调参数值传入block中,这样在VCTarget中调用VCSource中的该接口方法时即可在block回调中取得VCSource传来的参数值。

/*VCSource中的Login接口方法及block回调*/

+(void)Login:(void(^)(NSInteger))callback{

callback(1);

//传入围调值

}

/*在VCTarget中调用Login接口方法并访问回调参数*/

[VCSourceLogin:^(NSIntegerresult){

NSLog(@"%li",(long)result);

}];

6)extern全局变量传值:即在VCSource中定义extem全局变量暴露给外部访问。

2.

如何实现Cocoa中多线程的安全?正确答案:Cocoa中多线程编程的实现如下:

1)Objective-C语言本身的NSThread。

2)基于C语言封装的GCD。

3)GCD的Objective-C封装NSOperation(NSOperationQueue)。

4)兼容C语言的pthread。

多线程的应用场景如下:

1)在避免主线程阻塞,影响界面流畅性时,会利用多线程将耗时操作放到后台等其他线程执行,执行结束后再同步到主线程。

2)大任务的分割并行要利用多线程实现,例如下载一张大图片,可以将图片分割成多个部分在多个线程上并行下载,下载完成后再拼接起来提高效率。

3)并发任务的实现需要多线程,指的是某些任务并非先后排队执行,而必须是并发的、时间上会重合的,就需要将这些任务放置在不同的线程上执行(这里从多线程的使用层次来说,不考虑系统级的分时复用)。

多线程安全的解决办法如下:

1)对于UI界面的刷新访问操作要保证在主线程上,防止不能及时响应造成意想不到的结果。

2)应用多线程要防止数据资源的抢夺,可通过使用@synchronized进行加锁在临界区安全地访问数据。

3)尽量使用GCD开发多线程程序,防止线程安全的问题,因为GCD是经过优化的且很多函数默认就是线程安全的,可以大大减少开发过程中的线程安全问题。

3.

Cocoa中有虚基类的概念吗?正确答案:Cocoa中没有虚基类的概念。虚基类是C++语言中为了解决多重继承二义性问题的,而Objective-C中只有单继承。

要实现类似C++语言中的多继承,可以通过protocal来简单实现,因为一个类可以实现多个协议,类似于Java中一个类可以实现多个接口。

4.

NSNotification、Delegate、Block和KVO的区别是什么?正确答案:Delegate(代理)是一种回调机制,是一对一的关系;而通知是基于观察者模式的一对多的关系,消息会发送给所有注册为事件观察者的对象;Delegate比Notification的执行效率要高。

Block和Delegate一样通常也是一对一的通知,使用场景相同,可以说Block是Delgate的另一种形式,但Block更加简洁直接且轻便灵活,不需要像Delegate那样需要定义协议很多方法,而且代理对象要实现协议方法,还需要建立对象间的代理关系才可以通信。在通信事件比较多的情况下,还是建议使用Delegate,Delegate的定义实现形式更加直观清楚。

KVO就是Cocoa框架实现的观察者模式,一般同KVC搭配使用,通过KVO可以监测一个值的变化。例如,View的高度变化是一对多的关系,一个值的变化会通知所有的观察者。NSNotification是通知,也是一对多的使用场景。在某些情况下,KVO和NSNotification是一样的,都是状态变化之后告知对方。

NSNotification的特点是需要被观察者先主动发出通知,然后观察者注册监听后再来进行响应,比KVO多了发送通知的一步,但是其优点是监听不局限于属性的变化,还可以对多种多样的状态变化进行监听,监听范围广,使用也更灵活。

KVO一般的使用场景是数据,需求是数据变化,例如股票价格变化一般使用KVO(观察者模式)实现。Delegate一般的使用场景是行为,需求是需要别人帮我做一件事情,例如买卖股票就一般使用:Delegate来实现。Notification一般是进行全局通知,例如好消息一出,通知大家去买入。Delegate是强关联,就是委托和代理双方互相知道,你委托别人买股票你就需要知道经纪人,经纪人也需要知道自己的顾客。Notification是弱关联,利好消息发出,你不需要知道是谁发的也可以做出相应的反应,同理发消息的人也不需要知道接收的人就可以正常发出消息。

5.

对于语句NSString*testObject=[[NSDataalloc]init];,testObject在编译时和运行时分别是什么类型的对象?正确答案:testObject是一个指向某个对象的指针,不论何时指针的空间大小都是固定的。

编译时:指针的类型为NSString,即编译时会被当成一个NSString实例来处理,编译器在类型检查的时候,如果发现类型不匹配则会给出黄色警告,由于该语句给指针赋值用的是一个NSData对象,所以编译时会给出类型不匹配警告。但编译时如果testObject调用NSString的方法,那么编译器会认为是正确的,既不会警告也不会报错。

运行时:运行时指针指向的实际是一个NSData对象,因此如果指针调用了NSString的方法,那么虽然编译时通过了,但运行时会崩溃,因为NSData对象没有该方法。另外,虽然运行时指针实际指向的是NSData,但编译时编译器并不知道(前面说了编译器会把指针当成NSString对象处理),因此如果试图用这个指针调用NSData的方法,那么会直接编译不通过,给出红色报错,程序也运行不起来。

6.

MVVM设计模式能够减少总体的代码量吗?能够在开发中代替MVC设计模式吗?正确答案:事实上,MVVM设计模式在实际使用中,虽然能够将诸多非业务逻辑从Controller中抽离,减少了代码的复杂性,但是总体的代码量不会减少,甚至会有些许增加。

MVVM设计模式作为一种新颖的编程框架,能够帮助开发者解决一些旧编程框架带来的问题,但是也会带来一些新问题。例如:

1)MVVM将Model通过ViewModel与View绑定使得bug很难被调试。你看到界面异常了,有可能是你View的代码有bug,也可能是Model的代码有问题。数据绑定使得一个位置的bug被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。

2)对于过大的项目,数据绑定需要花费更多的内存。某种意义上来说,数据绑定使得MVVM设计模式变得复杂和难用了。

综上所述,在iOS应用程序开发过程中,MVC设计模式和MVVM设计模式都有适合自己的应用场景,应该根据具体的业务需要,客观评估项目的具体情况,既不能守旧,又不能盲目追赶新技术,这样才能应对技术的变化。

7.

CocoaPods是什么?工作原理是什么?正确答案:CocoaPods是用来管理iOS项目中第三方框架的工具,主要是通过建立Podfile文件,在文件中指定项目中需要的第三方框架,然后使用podinstall安装框架。

其主要原理是:Pods项目中的第三方框架最终会编译成一个名为libPods.a的文件,主项目依赖这个.a文件即可;对于资源文件,CocoaPods提供了一个名为Pods-resources.sh的bash脚本,该脚本在每次项目编译的时候都会执行,将第三方的各种资源文件复制到目标目录中;CocoaPods通过一个名为Pods.xcconfig的文件在编译时设置所有的依赖和参数。

8.

static关键字的作用是什么?static全局变量与普通全局变量的区别是什么?static局部变量与普通变量的区别是什么?static函数与普通函数的区别是什么?正确答案:static关键字是C/C++语言中都存在的关键字,它具有以下特性:

1)static全局变量的作用域范围是有限制的,即如果一个变量被声明为静态的,那么该变量可以被模块内所有函数访问,但不能被模块外其他函数访问,它是一个本地的全局变量。而普通全局变量能被其他模块访问。

2)在函数体内,静态变量具有“记忆”功能,即一个被声明为静态的变量在这一函数调用结束后,它的值仍然被保存着,当这个函数下一次被调用的时候,这个静态变量的值仍然是上次调用后的结果(需要注意的是,函数中的静态变量只初始化一次),而函数体内的普通变量没有记忆功能。示例代码如下:

#include<stdio.h>

voidfun1(inti)

{

staticintvalue=i;

printf("%d",++value);

}

voidfun2(inti)

{

intvalue=i;

printf("%d

",++value);

}

intmain()

{

printf("fun1:");

fun1(0);

fun1(4);

fun1(7);

printf("\nfun2:");

fun2(0);

fun2(4);

fun2(7);

return0;

}

程序的运行结果为:

fun1:1

2

3

fun2:1

5

8

分析:函数fun1中把value定义为静态变量,它会在第一次调用的时候初始化为0,由于其具有记忆功能,只会被初始化一次。因此,在调用函数fun1(0)的时候,函数fun1内部的value被初始化为0,语句printf("%d",++value);输出结果为1;当调用函数fun1(4)的时候,语句staticintvalue=i;不会再被执行(只能初始化一次),此时value的值为1,打印语句printf("%d

",++value)的输出结果为2。同理,第三次调用函数fun1(7)时,输出的结果为3。而对于普通变量而言,没有记忆功能,每次被调用的时候都需要初始化。调用fun2函数的时候,value每次都会被初始化为传入的参数,因此每次输出++value的值就是实参+1。

3)如果一个函数被声明为静态的,那么该函数与普通函数的作用域不同,静态函数的作用域仅在本文件中,它只能被这一模块内的其他函数调用,不能被模块外的其他函数调用。也就是说,这个函数被限制在声明它的模块的本地范围内使用。而普通函数可以被其他模块使用。

9.

什么是线程?线程与进程有什么区别?为什么要使用多线程?正确答案:线程指程序在执行过程中,能够执行程序代码的一个执行单元。线程主要有4种状态:运行、就绪、挂起、结束。

进程指一段正在执行的程序。而线程有时候也被称为轻量级进程,是程序执行的最小单元。一个进程可以拥有多个线程,各个线程之间共享程序的内存空间(代码段、数据段和堆空间)及一些进程级的资源(例如打开的文件),但是各个线程拥有自己的栈空间。进程与线程的关系如图所示。

进程与线程的关系

在操作系统级别上,程序的执行都是以进程为单位的,而每个进程中通常都会有多个线程互不影响地并发执行,那么为什么要使用多线程呢?其实,使用多线程为程序研发带来了巨大的便利。具体而言,有以下几个方面的内容:

1)使用多线程可以减少程序的响应时间。在单线程(单线程指程序执行过程中只有一个有效操作的序列,不同操作之间都有明确的执行先后顺序)的情况下,如果某个操作很耗时,或者陷入长时间的等待(如等待网络响应),那么此时程序将不会响应鼠标和键盘等操作,使用多线程后,可以把这个耗时的线程分配到一个单独的线程去执行,使得程序具备了更好的交互性。

2)与进程相比,线程的创建和切换开销更小。由于启动一个新的线程必须给这个线程分配独立的地址空间,建立许多数据结构来维护线程代码段、数据段等信息,而运行于同一进程内的线程共享代码段、数据段,所以线程的启动或切换的开销比进程要少很多。同时多线程在数据共享方面效率非常高。

3)多CPU或多核计算机本身就具有执行多线程的能力,如果使用单个线程,那么将无法重复利用计算机资源,造成资源的巨大浪费。因此,在多CPU计算机上使用多线程能提高CPU的利用率。

4)使用多线程能简化程序的结构,使程序便于理解和维护。一个非常复杂的进程可以分成多个线程来执行。

10.

子类初始化时为什么要调用self=[superinit]?正确答案:因为子类继承自父类,需要获得父类的实例和方法等,所以初始化子类之前要先保证父类已经初始化完毕,防止出错。当调用self=[superinit]方法时,如果父类初始化不成功,那么会返回nil,所以可以根据self是否为nil判断父类是否初始化成功,从而进行合理地处理,以便起到容错效果。

11.

项目中什么时候选择使用GCD?什么时候选择NSOperation?正确答案:下图简单概括了NSOperation和GCD各自的特点。NSOperation是对线程的高度抽象,提供Objective-C语言接口,具备面向对象特性(复用、封装),可子类化NSOperation进行操作对象的复用,使程序逻辑结构更清晰。比起GCD,NSOperation可以直接设置操作之间的依赖关系实现线程同步,而且可以通过调用cancel方法中途取消操作,而在GCD中不可取消。NSOperation的线程逻辑控制更加精细灵活,同时复杂度较GCD要高,因此建议在有特殊需求的复杂项目中使用。

NSOperation和GCD的特点

GCD的最大优点是非常简单、易用,经过了官方的优化更加安全高效(多数函数是线程安全的)。对于不复杂的多线程操作,会节省代码量,尤其block参数的使用,使代码更为易读,建议在简单项目中广泛使用。

12.

load和initialize的区别是什么?正确答案:相同点:load和initialize都是为了给应用运行前提供运行环境,并且每个类中不管是父类还是子类中最多只会调用一次。

不同点:load方法在某个类文件被引用时调用,子类会自动调用父类的load方法;initialize是在类或者其子类的第一个方法被调用前调用。所以,如果类没有被引用进项目,那么就不会有load调用,即使类文件被引用进来,但是没有使用,那么initialize也不会被调用。

13.

SDWebImage框架内部在清理磁盘缓存的时候clearDisk方法和cleanDisk方法有什么区别?正确答案:clearDisk方法清空磁盘缓存,删除缓存目录中的所有文件,然后创建一个新的同名空文件。

cleanDisk方法按照磁盘清理缓存策略清理缓存,没有直接将所有缓存删除,而是先遍历缓存文件并删除已经过期的文件,如果剩余缓存空间超出了限额,那么按照文件创建的时间从远到近依次删除,清理到低于期望的缓存限额为止。SDWebImage中的期望缓存限额为最大限额的一半。

14.

BOOL、int、float和指针变量与“零值”比较的if语句是什么(假设变量名为var)?正确答案:不同类型变量与“零佰”比较的if语句分别如下:BOOL型变量if(!var)if(var)int型变量if(var>0)if(var==0)if(var<0)float型变量/*定义精度*/constfloatEPSINON=0.00001;if(var=<EPSONON&&var>=-EPSINON)if(var<-EPSONON)if(var>EPSONON)指针变量if(var==NULL)if(var==nil)

15.

为什么iOS中提供UIView和CALayer这两个平行的层级结构呢?正确答案:iOS中提供UIView和CALayer这两个平行层级结构主要是为了做到职责分离,实现视图的绘制、显示和布局解耦,避免很多重复的代码。在iOS和MacOS两个平台上,事件和用户交互有很多地方并不相同,毕竟基于多点触控的用户界面和基于鼠标键盘有着本质的区别,这就是为什么iOS有UIKit和UIView,但是MacOS有Appkit和NSView的原因。它们功能虽然相似,但是在实现上有着显著的区别。创建两个层次结构就能够在iOS与MacOS之间共享代码,从而使得开发更加便捷。

16.

objective-C的优缺点有哪些?正确答案:Objective-C的优缺点见表。Objective-C的优缺点优点缺点1)Objective-C是C语言的超集,在C语言的基础上衍生了很多新的语言特性,封装得很完善而且方便使用,大大降低了编程复杂度,因此开发中使用起来会感觉方便高效1)不支持命名空间(都是通过加一些像NS或者UI这样的命名前缀来达到用命名空间防止命名冲突的作用,但这样会使变量的命名更长)2)Category(类别)的使用,可以快速扩展类的方法,同时使扩展的功能模块之间互不影响2)不支持运算符重载3)Posing(扮演)特性,[ParentClassposeAs:[ChildrenClassclass]];该语言特性使得父类无须定义和初始化子类对象,即可通过父类扮演子类进行操作3)不支持多重继承(C++语言通过virtual关键字防止二义性的出现,实现多重继承)4)动态语言特性,动态类型、动态绑定和动态加载等,将类型确定、方法调用和资源加载等任务推迟到运行时,大大提高了编程灵活度4)使用动态运行时类型,所有的方法都是通过消息传递机制方法调用,有其动态的优势,同时也使很多编译时的优化方法无法使用降低了性能,例如:内联方法等5)指针:Objective-C保留了C语言强大的指针特性

6)Objective-C与C/C++可在.mm文件中进行混合编程,灵活度更高

17.

setValHe:forKey:方法的底层实现是什么?正确答案:当一个对象发送setValue:forKey:消息时,方法内部会做以下操作:

1)查对象的类中是否存在与key相对应的访问器方法(即-set<key>),如果存在,那么就会直接调用访问器方法。

2)如果访问器方法不存在,那么就会继续查找与key的名称相同并且带“_”前缀的成员变量(即_key)。如果存在这样的成员变量并且类型是一个对象指针类型,那么就会先released成员变量的旧值,然后直接为对象的这个成员变量赋新值。

3)如果_key不存在,那么就会继续查找与key的名称相同的属性。如果有这样的属性,那么就会直接为对象的这个属性赋值。

4)如果访问器方法、成员变量和属性都没有找到,那么就会调用setValue:forUndefinedKey:方法,该方法的默认实现是抛出一个NSUndefinedKeyException类型的异常,但是可以根据需要重写setValue:forUndefinedKey:方法。

示例代码如下:

/*定义一个Person类*/

#import"Person.h"

@interfacePerson(){

NSString*_name;

int_age;

}

@property(nonatomic,assign)intage;

@property(nonatomic,copy)NSString*address;

@end

@implementationPerson

-(void)setName:(NSString*)name

{

NSLog(@"%s",__func__);

_name=name;

}

-(void)log{

NSLog(@"age=%iaddress=%@",__age,self.address);

}

/*编辑如下测试代码:*/

Person*p=[Personnew];

[psetValue:@"小强"forKey:@"name"];

[psetValue:@25forKey:@"age"];

[psetValue:@"合肥市"forKey:@"address"];

[plog];

程序的输出结果为:

2016-10-1017:47:59.886mianshibaodian[4408:519039]-[PersonsetName:]

2016-10-1017:47:59.887mianshibaodian[4408:519039]age=25

address=合肥市

2016-10-1018:07:15.081mianshibaodian[4471:531349]***Terminatingappduetouncaughtexception'NSUnknownKeyException',reason:'[<Person0x60800003dde0>setValue:forUndefinedKey:]:thisclassisnotkeyvaluecoding-compliantforthekeyundefineAddress.'

输出结果验证了setValue:forKey:方法会调用访问器方法(setName:),对name成员变量进行赋值。第二行输出验证了当不存在访问器方法时,setValue:forKey:方法会逐步从对象的类中寻找带“_”的成员变量和属性进行赋值,这里特别需要特别强调一点setValue:传入的参数类型必须是对象类型或者NSValue类型,否则会产生错误。最后一行验证了当访问器方法、成员变量和属性都没有找到时,就会抛出一个异常,当然可以在Person类中重写setValue:forUndefinedKey:方法,自己来处理这个异常,这里不做过多的解释。

18.

iOS中有哪几种从其他线程回到主线程的方法?正确答案:iOS中只有主线程才能立即刷新UI。如果是通过侦听异步消息,触发回调函数,或者调用异步方法,请求刷新UI,那么都会产生线程阻塞和延迟的问题。如果要在其他线程中执行刷新UI的操作,那么就必须切换回主线程。主要由以下3种方式。

1.NSThreadPerformAdditions协议

这个协议提供了两个切换到主线程的API。

-(void)performSeleetorOnMainThread:(SEL)aSelectorwithObject:

(nullableid)argwaitUntilDone:(BOOL)waitmodes:(nullableNSArray<NSString*>*)array;

-(void)performSelectorOnMainThread:(SEL)aSelectorwithObject:

(nullableid)argwaitUntilDone:(BOOL)wait;

2.GCD

使用GCD的dispatch_get_main_queue()方法可以获取主队列,主队列中的任务必定是在主线程中执行的。

dispacch_async(dispatch_get_main_queue(),^{

});

3.NSOperationQueue

和GCD一样,使用NSOperationQueue提供+mainQueue方法可以获取主队列,再将刷新UI的任务添加到主队列中。

[[NSOperationQueuemainQueue]addOperationWithBlock:^{

}];

示例代码如下:

-(void)goToMainThread{

/*开启子线程下载图片*/

dispatch_async(dispatch_get_global_queue(0,0),^{

NSData*imageData=[[NSDataalloc]initWithContentsOfURL:[NSURLURLWithString:@"/pe0060886.jpg"]];

_image=[UIImageimageWithData:imageData];

/*切换到主线程显示*/

//1.NSThread

//[selfperformSeteetorOnMainThread:@selector(changeBg)withObject:nilwaitUntilDone:NO];

//2.GCD

//dispatch_async(dispatch_get_main_queue(),^{

//[selfehangeBg];

//});

//3.NSOperationQueue

[[NSOperationQueuemainQueue]addOperationWithBlock:^{

[selfchangeBg];

}];

});

}

19.

block有哪几种定义的方式?正确答案:在Objective-C中,block定义包含了block的类型声明和实现,基本形式如下:

返回值类型(^block

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论