iOS精品文章ios专题iOS面试题

2.IOS面试集

2018-04-15  本文已影响375人  木子心语

1.INTERVIEW 共勉

2.INTERVIEW 准备

Interview preparation.png

3.iOS developers 方向

iOS developers.png

4.INTERVIEW QUESTION

4.1深copy和浅copy

浅拷贝.png 深拷贝.png
    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是深拷贝

*案例二:

    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程序的启动过程

iOS程序的启动过程.png

4.3 loadView

4.4 单例模式

可以看我简书地址:
https://www.jianshu.com/p/9e9b405b4d94

4.5 多线程

多线程.png

比如,我们同时开启2条线程下载文件A,文件B.

注意:如果线程非常非常多,会发生什么情况?
cpu会在多个多线程之间进行调度,消耗大量的CPU资源.这样的话,每条线程被调度执行的频次会降低,线程执行效率降低.

4.6 NSThread

NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadFun) object:nil];
[thread start];

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

主线程相关用法.png
[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.强制停止线程
进入死亡状态

注意:一旦线程停止了,就不能再次开启任务.

如果,多个线程访问同一块资源的话,会造成数据错乱的.
我们应该怎么解决呢?


安全隐患解决-互斥锁.png

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

任务和队列.png

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同步和异步的区别
同步:只能在当前线程中执行任务,不具备开启新线程的能力
异步:可以再新的线程中执行任务,具备开启新线程的能力

同步,异步,并发,串行.png

注意: 同步函数 + 主队列 == 死锁

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);
全局并发队列的优先级.png
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]
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
 
     程序运行过程中,永远只执行1次的代码(这里面默认是线程安全的)
});
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(), ^{
     等前面的异步操作都执行完毕后,回到主线程...
});
  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

步骤.png
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;

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

- (void)start;

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

默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作;只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作

+ (id)blockOperationWithBlock:(void (^)(void))block;

通过addExecutionBlock:方法添加更多的操作

- (void)addExecutionBlock:(void (^)(void))block;

只要NSBlockOperation封装的操作数 > 1,就会异步执行操作

- (void)addOperation:(NSOperation *)operation;
- (void)addOperationWithBlock:(void (^)(void))block;
-(NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;

4.9 RunLoop

int main(int argc, char * argv[]) {
    NSLog(@"main");
    return 0;
}
int main(int argc, char * argv[]) {
    BOOL run = YES;
    do{
         //执行各种任务,处理各种事件
    }while(run);
    return 0;
}
int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

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

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

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

HTTP通信过程.png

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


HTTP通信过程-请求.png
HTTP通信-响应.png
上一篇 下一篇

猜你喜欢

热点阅读