iOS经典面试题大全

1.INTERVIEW 共勉

做为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个个人iOS交流群:638302184,无论你是小白仍是大牛欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 与2800+iOS开发者一块儿交流学习成长!

2.INTERVIEW 准备

3.iOS developers 方向

4.INTERVIEW QUESTION

4.1深copy和浅copy

  • 浅拷贝:
    1.对内存地址的复制,让目标对象指针和源对象指向同一片内存空间.
    2.内存销毁的时候,指向这片空间的指针须要从新定义才可使用,要否则会成为野指针
    3.拷贝指向原来对象的指针,使原对象的引用计数加+1
    4.至关于建立了一个指向原对象的新指针,并无建立一个新的对象.

  • 深拷贝:
    1.拷贝对象的具体内容,而内存地址是自主分配的
    2.拷贝结束以后,两个对象存在的值是相同的,内存地址是不同的
    3.两个对象没有任何关系
  • 本质区别:
    1.深拷贝是内容拷贝,浅拷贝是指针拷贝
    2.是否有新的内存地址
    3.是否影响内存地址的引用计数.
  • 案例一
NSString * str1 = @"copyStr";

    NSMutableString *str2 = [str1 copy];

    NSMutableString *str3 = [str1 mutableCopy];

    NSLog(@"str1:%p--%@",str1,str1);

    NSLog(@"str1:%p--%@",str2,str2);

    NSLog(@"str1:%p--%@",str3,str3);
2018-04-14 14:50:54.117652+0800 MutyCopy-Copy[2644:63575] str1:0x109a48068--copyStr
2018-04-14 14:50:54.117885+0800 MutyCopy-Copy[2644:63575] str1:0x109a48068--copyStr
2018-04-14 14:50:54.118010+0800 MutyCopy-Copy[2644:63575] str1:0x600000259a40--copyStr

1.str1,str2地址相同,而Str3地址不一样
2.NSString的copy是浅拷贝,copy返回的对象是不可变对象
3.mutablecopy是深拷贝web

*案例二:面试

NSMutableString * str1 = [NSMutableString stringWithString:@"mutableStr"];

    NSMutableString * str2 = [str1 copy];

    NSMutableString * str3 = [str1 mutableCopy];

    NSLog(@"str:%p-----%@",str1,str1);

    NSLog(@"str:%p-----%@",str2,str2);

    NSLog(@"str:%p-----%@",str3,str3);
2018-04-14 15:04:50.092820+0800 MutyCopy-Copy[2685:70866] str:0x60000025b210-----mutableStr
2018-04-14 15:04:50.093059+0800 MutyCopy-Copy[2685:70866] str:0x60000022ca40-----mutableStr
2018-04-14 15:04:50.093217+0800 MutyCopy-Copy[2685:70866] str:0x60000025b540-----mutableStr

1.str1,str2,str3地址都不一样
2.NSMutableString对象copy与mutableCopy都是深拷贝
3.copy返回的对象是不可变对象编程

4.2 iOS程序的启动过程

  • 首先找到程序入口,执行main函数
  • main -->> UIApplicationMain
  • 建立UIApplication对象
  • 建立UIApplication的代理对象,给UIApplication对象代理属性赋值
  • 开启主运行循环,做用接收事件,让程序一直运行
  • 加载info.plist,判断有没有指定main.storyboard,若是指定就去加载.

4.3 loadView

  • 何时被调用?
    每次访问VC的view并且view为nil,loadView方法被调用
  • 做用
    loadView方法是用来负责建立VC的view.
  • 默认实现是怎样的?
    默认实现即[spuer loadView]
    1.它会先去查找与UIViewController相关联的xib文件,经过加载xib文件来建立VC的view.
    2.若是在初始化VC指定了xib文件名,就会根据传入的xib文件名加载对应的xib文件.若是没有明显xib文件名,就会加载跟本身同名的xib文件.
    3.若是没有找到关联的xib文件,就会建立一个空白的UIView,而后赋值给VC的view属性

4.4 单例模式

4.5 多线程

  • 进程
    1.进程是指系统中正在运行的一个应用程序
    2.每一个进程之间是独立的,每一个进程均运行在其专用且受保护的内存空间内.
  • 线程
    1.1个进程要想执行任务,必须得有线程,每1个进程至少要有1条线程
    2.线程是进程的基本执行单元
    3.一个进程的素有任务都在线程中执行
  • 多线程
    1.1个进程中能够开启多条线程,每条线程能够并行(同时)执行不一样的任务
    2.进程--工厂,线程--工厂工人
    3.多线程能够提升程序的执行效率

好比,咱们同时开启2条线程下载文件A,文件B.设计模式

  • 多线程的原理
    1.同一时间,CPU只能处理1条线程,只有1条线程在工做
    2.多线程并发执行,是CPU快速地在多条线程之间调度切换
注意:若是线程很是很是多,会发生什么状况?
cpu会在多个多线程之间进行调度,消耗大量的CPU资源.这样的话,每条线程被调度执行的频次会下降,线程执行效率下降.
  • 多线程的优缺点
    1.优势:
    1.1.能适当的提升程序的执行效率
    1.2.能适当调高资源利用率
    2.缺点:
    2.1.开启线程须要占用必定的内存空间,若是开启大量的线程,会占用大量的内存空间,下降程序的性能
    2.2线程越多,CPU在调度线程上的开销就越大.
  • 多线程应用
    1.什么是主线程?
    一个iOS程序运行后,默认会开启1条线程 ,称为主线程
    2.主线程的主要做用?
    2.1.显示/刷新UI界面
    2.2.处理UI事件
    3.主线程使用注意?
    3.1.别将比较耗时的操做放到主线程中
    3.2.耗时操做会卡住主线程,严重影响UI的流畅度
    4.多线程实现技术方案?
    pthread,NSThread,GCD,NSOperation四中方案.

4.6 NSThread

  • 建立,启动线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadFun) object:nil];
[thread start];

线程一启动,就会告诉CPU准别就绪,能够随时接受CPU调度.CPU调度当前线程以后,就会在线程thread中执行self的run方法安全

  • 主线程用法

  • 其余方式建立线程
    建立线程后自动启动线程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];

隐式建立并启动线程服务器

[self performSelectorInBackground:@selector(run) withObject:nil];
  • 线程状态

1.启动线程,start.就绪状态-->>运行状态.当新厂任务执行完毕,自动进入死亡状态
2.阻塞(暂停)线程,进入阻塞状态网络

+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

3.强制中止线程
进入死亡状态多线程

注意:一旦线程中止了,就不能再次开启任务.
  • 多线程的安全隐患
    1.资源共享
    一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源.当多线程访问同一块资源时,很容易引起数据错乱和数据安全问题.
    2.如图,

若是,多个线程访问同一块资源的话,会形成数据错乱的.
咱们应该怎么解决呢?并发

image

3.如图,
线程A和线程B同时访问资源变量Integer,
为了防止抢夺资源,
线程A在读取资源变量Integer以前先加一把锁,
而后读取Integer的数据并在线程A中完成数据操做(17+1=18),
而后把数据写入Integer中,
最后开锁Unlock.在线程A对Integer操做的过程当中,
线程B是无权访问Integer的,
只有线程A_Unlock后,线程B才能够访问资源变量Integer.
4.互斥锁使用格式
@synchronized(self){//须要锁定的代码}异步

注意: 锁定1分代码只用1把锁,用多把锁是无效的

5.互斥锁的优缺点
互斥锁的使用前提:多条线程抢夺同一块资源
优势:能有效防止因多线程抢夺资源形成的数据安全问题
缺点:须要消耗大量的CPU

6.nonatomic和atomic
atomic:
原子属性,为setter方法加锁(默认就是atomic)
线程安全,须要消耗大量的资源
nonatomic:
非原子属性,不会为setter方法加锁
非线程安全,适合内存小的移动设备

4.7 GCD

  • 什么是GCD?
    全程Grand Central Dispatch,中枢调度器
    纯C语言,提供了很是多强大的函数
  • GCD的优点
    1.GCD是苹果公司为多核的并行运算提出的解决方案
    2.GCD会自动利用更多的CPU内核
    3.GCD自动管理线程的生命周期(建立线程,调度任务,销毁线程)
  • GCD有2个核心概念
    1.任务:执行什么操做
    2.队列;用来存听任务
  • 任务和队列

1.执行任务
GCD中有2个用来执行任务的函数
1.1用同步的方式执行任务

dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
queue:队列
block:任务

1.2用异步的方式执行任务

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

1.3同步和异步的区别
同步:只能在当前线程中执行任务,不具有开启新线程的能力
异步:能够再新的线程中执行任务,具有开启新线程的能力

  • 队列的类型
    GCD的队列能够分为2大类型
    并发队列:可让多个任务并发执行(并发功能只能在异步函数下才有效)
    串行队列:让任务一个接着一个地执行
  • 容易混淆的术语
    有4个术语比较容易混淆:
    同步,异步,并发,串行

注意: 同步函数 + 主队列 == 死锁
  • 并发队列
    GCD默认已经提供了全局的并发队列,供整个应用使用,不须要手动建立
    使用dispatch_get_global_queue函数得到全局的并发队列
dispatch_queue_t dispatch_get_global_queue(
dispatch_queue_priority_t priority,  队列的优先级
unsigned long flags);
全局并发队列
dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

  • 串行队列
    GCD中得到串行的2中途径
    1.使用dispatch_queue_create函数建立串行队列
dispatch_queue_t =
dispatch_queue_create(const char*label,  队列名称 
dispatch_queue_attr_t attr);  队列属性,通常用NULL便可

2.使用主队列
放在主队列中的任务,都会放到主线程中执行
使用dispatch_get_main_queue()得到主队列

dispatch_queue_t queue = dispatch_get_main_queue();
  • 从子线程回到主线程
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      执行耗时的异步操做...
      dispatch_async(dispatch_get_main_queue(), ^{
      回到主线程,执行UI刷新操做
        });
});
  • 延时执行

设定好延迟的时间后,它会先执行后边的代码,2秒后再调用self的run方法(而且不会卡主线程,在主线程调最后会回到主线程,在子线程调最后会回到子线程)

withObject:参数
afterDelay:延迟的时间
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];

使用GCD函数(2秒后自动开启新线程 执行block中的代码,不会卡主当前的线程,在主/子线程调用均可以使用)

DISPATCH_TIME_NOW:如今开始的意
2.0 * NSEC_PER_SEC:设置的秒数(直接更改数字便可)
dispatch_get_main_queue():主队列的意思
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    2秒后执行这里的代码... 在哪一个线程执行,跟队列类型有关  
});

3.会卡住主线程

[NSThread sleepForTimeInterval:3]
  • 只执行一次
    使用dispatch_once函数能保证某段代码在程序运行过程当中只被执行1次
    在设计模式中,单例模式也会用到
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{

     程序运行过程当中,永远只执行1次的代码(这里面默认是线程安全的)
});
  • 队列组
    需求:1.分别异步执行2个耗时的操做,其次,等2个异步操做都执行完毕后,再回到主线程执行操做
dispatch_group_t group =  dispatch_group_create();

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     执行1个耗时的异步操做
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     执行1个耗时的异步操做
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
     等前面的异步操做都执行完毕后,回到主线程...
});
  • GCD的建立和释放
    在iOS6.0以前,在GCD中每当使用带creat单词的函数建立对象以后,都应该对其进行一次release操做.
    在iOS6.0以后,GCD被归入到了ARC的内存管理机制中,在使用GCD的时候咱们就像对待普通OC对象同样对待GCD,所以再也不须要咱们调用release方法.
  • GCD 的基本使用

    image

    1.异步函数+并发队列

1.建立队列(并发队列)

    dispatch_queue_t queue = dispatch_queue_create("com.baidu.www", DISPATCH_QUEUE_CONCURRENT);

    异步函数
    dispatch_async(queue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
    });

2.异步函数+串行队列

1.建立队列(串行队列)
    dispatch_queue_t queue = dispatch_queue_create("com.baidu.www", DISPATCH_QUEUE_SERIAL);

    异步函数
    dispatch_async(queue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
    });

3.同步函数+串行队列

1.建立队列(串行队列)
    dispatch_queue_t queue = dispatch_queue_create("com.baidu.www", DISPATCH_QUEUE_SERIAL);

    同步函数
    dispatch_sync(queue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
    });

4.同步函数+并发队列

//得到全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   // 同步函数
    dispatch_sync(queue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
    });

5.异步函数+主队列

1.得到主队列
    dispatch_queue_t queue = dispatch_get_main_queue();

    异步函数
    dispatch_async(queue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
    });

6.同步函数+主队列

1.得到主队列
    dispatch_queue_t queue = dispatch_get_main_queue();

    dispatch_sync(queue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
    });

4.8 NSOperation

  • NSOperation做用?
    配合使用NSOperation 和NSOperationQueue也能实现多线程编程
  • NSOperation 和NSOperationoQueue实现多线程的具体步骤

  • NSOperation的子类
    NSOperation是个抽象类,并不具有封装操做的能力,必须使用它的子类
    子类的方式有3中:
    1.NSInvocationOperation
    2.NSBlockOperation
    3.自定义子类继承NSOperation,实现内部响应的方法
  • NSInvocationOperation
    1.建立对象
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;

2.调用start方法开始执行操做

- (void)start;

一旦执行操做,就会调用target的sel方法

默认状况下,调用了start方法后并不会开一条新线程去执行操做,而是在当前线程同步执行操做;只有将NSOperation放到一个NSOperationQueue中,才会异步执行操做
  • NSBlockOperation
    1.建立NSBlockOperation对象
+ (id)blockOperationWithBlock:(void (^)(void))block;

经过addExecutionBlock:方法添加更多的操做

- (void)addExecutionBlock:(void (^)(void))block;
只要NSBlockOperation封装的操做数 > 1,就会异步执行操做
  • NSOperationQueue做用
    若是将NSOperation添加到NSOperationQueue(操做队列)中,系统会自动异步执行NSOperationQueue中的操做
- (void)addOperation:(NSOperation *)operation;
- (void)addOperationWithBlock:(void (^)(void))block;
  • 最大并发数
    同时执行的任务数
    最大并发数相关的方法
-(NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
  • 自定义NSOperation
    重写-(void)main方法,在里面实现想执行的任务
    重写-(void)main方法的注意点:自动建立自动释放池,若是是异步操做,没法访问主线程的自动释放池

4.9 RunLoop

  • 若是没有RunLoop,程序输出后就退出了
int main(int argc, char * argv[]) {
    NSLog(@"main");
    return 0;
}
  • 若是有了RunLoop,因为main函数里面启动了RunLoop,因此程序并不会立刻退出,保持持续运行状态
int main(int argc, char * argv[]) {
    BOOL run = YES;
    do{
         //执行各类任务,处理各类事件
    }while(run);
    return 0;
}
  • main函数中的RunLoop,UIApplicationMaiin函数内部就启动了一个RunLoop,因此UIApplicationMain函数一直没有返回,保持了程序的持续运行,这个默认启动的RunLoop跟主线程相关联
int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
  • RunLoop与线程
    1.每条线程都有惟一的与之对应的RunLoop对象
    2.主线程的RunLoop已经自动建立好了,子线程的RunLoop须要主动建立
    3.RunLoop在第一次获取时建立,在线程结束时销毁
  • 得到RunLoop对象

  • RunLoop相关类

Core Foundation中关于RunLoop的5个类:
CFRunLoopRef:它本身,也就表明一个RunLoop对象
CFRunLoopModeRef:RunLoop的运行模式
CFRunLoopSourceRef:事件源
CFRunLoopTimerRef:时间的触发器
CFRunLoopbaserverRef:观察者 监听CFRunLoopRef的状态

  • CFRunLoopModeRef
    系统默认注册了5个Mode模式:
    kCFRunLoopDefaultMode:App的默认Mode,一般主线程是在这个Mode下运行
    UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其余 Mode 影响
    UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就再也不使用
    GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,一般用不到
    kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode
  • RunLoop处理逻辑

    image

1.通知观察者,即将进入Loop
2.通知观察者,将要处理定时器
3.通知观察者,将要处理非基于端口的源
4.处理非基于端口的源
5.若是有基于端口的源准备好并处于等待状态,当即启动,跳到第9步
6.通知观察者,线程即将休眠
7.休眠,等待唤醒
8.通知观察者,线程刚被唤醒
9.处理唤醒时收到的消息,以后跳到第2步
10.通知观察者,即将推出Loop

  • RunLoop应用

  • RunLoop面试题
    1.什么是RunLoop?
    字面意思运行循环
    其实它内部就是do-while循环,这个循环内部不断处理各类任务(好比Source,Timer,Observer);
    一个线程对应一个RunLoop,主线程的RunLoop默认启动,子线程的RunLoop手动启动;
    RunLoop只能选择一个Mode启动,若是当前Mode中没有任何Source,Timer,那么就直接退出RunLoop.

4.10 HTTP通讯过程-请求/响应

HTTP协议规定:1个完整的由客户端发给服务器的HTTP请求中包含如下内容

文章来源于网络,若有侵权,请联系小编删除。

相关文章
相关标签/搜索