iOS面试相关

iOS面试题 - 横扫千军之战胜篇

2018-03-05  本文已影响366人  大王叫我来巡山_Cong
横扫千军赵子龙.jpg

1.谈谈 tableview 的重用机制。

2.静态库的原理是什么?你有没有自己写过静态编译库,遇到了哪些问题?

一些坑

3.谈谈你对HTTP 、TCP、 IP、socket 协议的理解。

HTTP

TPC/IP

(1)客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。
(2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
(3)服务器B关闭与客户端A的连接,发送一个FIN给客户端A。
(4)客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。

socket

4.谈谈你对iOS中沙盒机制的理解。

5.请你谈谈你对视频播放器/直播的理解。如果封装一个视频播放器你会怎么做?封装中遇到哪些问题?你是怎么解决的?

视频播放器/直播的理解:

直播现在大部分都是引用第三方服务的。简单封装播放器如下

#import < UIKit/UIKit.h >
#import < MediaPlayer/MediaPlayer.h >
#import < AVKit/AVKit.h >

@interface ZrMoviePlayer : UIView

@property (nonatomic,strong) MPMoviePlayerController *moviePlayer;//视频播放控制器

/**
 *  播放资源地址
 */
@property (nonatomic,strong) NSString *videoUrl;

/**
 *  播放资源缩略图
 */
@property (nonatomic,strong) UIImageView *caver;

/**
 *  初始化
 *
 *  @param frame frame
 *  @param url   videoUrl
 *
 *  @return self
 */
-(instancetype)initWithFrame:(CGRect)frame videoUrl:(NSString *)url;

/**
 *  播放资源缩略图
 *
 *  @param url picUrl
 */
- (void)showCoverWithUrl:(NSString *)url;

@end

.m

#import "ZrMoviePlayer.h"

@interface ZrMoviePlayer ()

@end

@implementation ZrMoviePlayer

-(instancetype)initWithFrame:(CGRect)frame videoUrl:(NSString *)url{
    
    self.videoUrl = url;
    self = [super initWithFrame:frame];
    
    if (self) {
        
       /** [用户手动点击播放 默认暂停]*/
        //[self.moviePlayer play];
        
        [selfaddNotification];
        
     /** 获取缩略图 */
        //[self thumbnaiImageRequest];
        
        [self addGest];
    }
    return self;
}

/**
 *  添加手势
 */
-(void)addGest {
    
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(moviePicCoverTapGesture)];
    [self addGestureRecognizer:tapGesture];
}

/**
 *  移除缩略图 播放视频
 */
-(void)moviePicCoverTapGesture{
    
    [self.caver removeFromSuperview];
    [self.moviePlayer play];
}

- (void)dealloc{
    
   /** 移除所有通知监控 */
    [[NSNotificationCenter defaultCenter]removeObserver:self];
}

#pragma mark -- 私有方法

/**
 *  获得本地文件路径
 *
 *  @return 文件路径
 */
- (NSURL *)getFileUrl{
    
    NSString *urlStr = [[NSBundle mainBundle]pathForResource:@"" ofType:nil];
    return [NSURL URLWithString:urlStr];
}

/**
 *  取得网络文件路径
 *
 *  @return 文件路径
 */
- (NSURL *)getNetworkUrl{
    
    NSString *urlStr = _videoUrl;
    urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    return [NSURL URLWithString:urlStr];
}

/**
 *  创建媒体播放器
 *
 *  @return moviePlayer
 */
- (MPMoviePlayerController *)moviePlayer{
    
    if (!_moviePlayer) {
        
        NSURL *url = [self getNetworkUrl];
        _moviePlayer = [[MPMoviePlayerController alloc]initWithContentURL:url];
        _moviePlayer.view.frame = self.bounds;
        _moviePlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
        [self addSubview:_moviePlayer.view];
    }
    
    return _moviePlayer;
}

/**
 *  获取视频缩略图
 */
- (void)thumbnaiImageRequest{
    
/** 获取13.0s、21.5s的缩略图 */
    [self.moviePlayer requestThumbnailImagesAtTimes:@[@13.0,@21.5] timeOption:MPMovieTimeOptionNearestKeyFrame];
}

#pragma mark - 控制器通知

/**
 *  添加通知监控多媒体控制器状态
 */
- (void)addNotification{
    
    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
    
    [notificationCenter addObserver:self
                           selector:@selector(mediaPlayerPlaybackFinished:)
                               name:MPMoviePlayerPlaybackDidFinishNotification
                             object:self.moviePlayer];
    
    [notificationCenter addObserver:self
                           selector:@selector(mediaPlayerPlaybackStateChange:)
                               name:MPMoviePlayerPlaybackStateDidChangeNotification
                             object:self.moviePlayer];
    
    [notificationCenter addObserver:self
                           selector:@selector(mediaPlayerThumbnailRequestFinished:)
                               name:MPMoviePlayerThumbnailImageRequestDidFinishNotification
                             object:self.moviePlayer];
}

/**
 *  播放状态改变,注意播放完成时的状态是暂停
 *
 *  @param notify 通知对象
 */
- (void)mediaPlayerPlaybackStateChange:(NSNotification *)notify{
    
    switch (self.moviePlayer.playbackState) {
            
        case MPMoviePlaybackStatePlaying:
            
            NSLog(@"正在播放");
//            [[NSNotificationCenter defaultCenter] postNotificationName:MoviePlayNotification object:nil];

            break;
        case MPMoviePlaybackStatePaused:
            
            NSLog(@"暂停播放");
//            [[NSNotificationCenter defaultCenter] postNotificationName:MoviePauseOrStopNotification object:nil];

            break;
        case MPMoviePlaybackStateStopped:
            
            NSLog(@"停止播放");
//            [[NSNotificationCenter defaultCenter] postNotificationName:MoviePauseOrStopNotification object:nil];

            break;
        default:
            
            NSLog(@"播放状态:%ld",(long)self.moviePlayer.playbackState);
            break;
    }
}
 
- (void)mediaPlayerPlaybackFinished:(NSNotification *)notify{
    
    NSLog(@"播放完成");
}

/**
 *  缩略图请求完成,此方法每次截图成功后都会调用一次
 *
 *  @param notify 通知对象
 */
- (void)mediaPlayerThumbnailRequestFinished:(NSNotification *)notify{
    
    NSLog(@"视频截图成功");
    
    UIImage *image = notify.userInfo[MPMoviePlayerThumbnailImageKey];
    // 保存图片到相册(首次调用会请求用户获访问相册权限)
    UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
}

/**
 *  播放资源缩略图
 *
 *  @param url picUrl
 */
- (void)showCoverWithUrl:(NSString *)url
{
    UIImageView *caver = [[UIImageView alloc] init];
    _caver = caver;
    [caver sd_setImageWithURL:[NSURL URLWithString:url]placeholderImage:[UIImage imageNamed:@"videoPlaceHolder"]];
    [self addSubview:caver];
    
    UIImageView *pause = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    pause.image = [UIImage imageNamed:@"zr_pause"];
    [caver addSubview:pause];
    
    caver.sd_layout
    .leftSpaceToView(self,0)
    .topSpaceToView(self,0)
    .rightSpaceToView(self,0)
    .bottomSpaceToView(self,0);
    
    pause.center = CGPointMake(self.center.x, self.center.y-20);
}

@end

这篇文章讲解了 得到喜马拉雅FM 的播放器的实现。

喜马拉雅FM的头文件点击下载 密码: u4uu

6.谈谈你对JSON XML的理解。

7.AFNetworking你使用过是哪几个版本?他们有什么区别?使用过程中应该注意哪些问题?

AFNetworking支持HTTP请求和基于REST的网络服务(包括GET、POST、 PUT以及DELETE等),支持ARC。AFNetworking项目中还包含一些列单元测试。

AFNetworking 2.0开始使用NSURLConnection的基础API ,以及较新基于NSURLSession的API的选项。

AFNetworking 3.0现已完全基于NSURLSession的API,删除了了对 NSURLConnection的封装内容

这是因为NSURLSession能够完全替代NSURLConnection,并且具有很多优点:

3.0版本最低支持版本是从iOS7

废弃的类

废弃对NSURLConnection的支持
被删除的类:

用以替代的是下面的类:

进行修改的类:

如果你之前的开发是基于AFHTTPRequestOperationManager的网络请求现在你应该转到AFHTTPSessionManager下去进行.
UIKit的迁移

图片下载已经被重构,以遵循AlamofireImage架构与新的AFImageDownloader类。这个类的图片下载职责的代理人是UIButton与UIImageView的类目,并且提供了一些方法,在必要时可以自定义。类别中,下载远程图片的实际方法没有改变。

UIWebView的类目被重构为使用AFHTTPSessionManager作为其网络请求。

UIAlertView的类目被废弃
从AFNetworking 3.0后UIAlertView的类目因过时而被废弃。并没有提供UIAlertController类目的计划,因为这是应用程序应处理的逻辑,而不是这个库。

下面进行新旧对比:

AFNetwork 2.x

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
  //设置网络请求超时时间
  [manager.requestSerializer willChangeValueForKey:@"timeoutInterval"];
  manager.requestSerializer.timeoutInterval = 30.0f;
  [manager.requestSerializer didChangeValueForKey:@"timeoutInterval"];
  [manager.requestSerializer setValue:@"application/x-www-form-urlencoded;" forHTTPHeaderField:@"Content-Type"];
  [self addHeaderParams:manager];
  manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json",@"text/html",nil];
  [manager POST:self.requestURL
     parameters:self.requestParams
        success:^(AFHTTPRequestOperation *operation, id responseObject) {

           (@"success-POST:%@",responseObject);

        }
        failure:^(AFHTTPRequestOperation *operation, NSError *error) {
          DebugLog(@"failurePOST:%@",error.description)             
        }];

AFNetworking 3.x

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    [manager.requestSerializer willChangeValueForKey:@"timeoutInterval"];
    manager.requestSerializer.timeoutInterval = 30.0f;//30.0f
    [manager.requestSerializer didChangeValueForKey:@"timeoutInterval"];
    [manager.requestSerializer setValue:@"application/x-www-form-urlencoded;" forHTTPHeaderField:@"Content-Type"];
    [self addHeaderParams:manager];    
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json",@"text/html",nil];

    [manager POST:self.requestURL parameters:self.requestParams progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {

    NSLog(@"success-POST:%@",responseObject);

} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

}];

即:每次开启一个网络请求时,首先新建一个AFHTTPSessionManager,然后将相关的requestSerializer和reponseSerializer赋值;最后发起相应的GET/POST等请求。

如果是直接采用NSURLSession来请求网络,写法如下:

NSURLSession *session =  [NSURLSession 
    sessionWithConfiguration:
    [NSURLSessionConfiguration defaultSessionConfiguration]
     delegate:nil
      delegateQueue:[NSOperationQueue mainQueue]];

      NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
      completionHandler:completionHandler];

    [dataTask resume];

即:新建一个Session(多个请求要用共享的SessionManager/Session),然后新建task,激活task,完成网络请求。

共享原因:

共享的Session将会复用TCP的连接,而每次都新建Session的操作将导致每次的网络请求都开启一个TCP的三次握手,共享会提升网络速度

AFNetworking 3.x 提供的post方法:

 [manager POST: parameters: constructingBodyWithBlock: progress: success: failure:]

 [manager POST: parameters: progress: success: failure:]

不建议使用的方法:

[manager POST: parameters: success: failure:];
[manager POST: parameters: constructingBodyWithBlock: success: failure:]

AFNetworking缓存

AFNetworking实际上使用了两个独立的缓存机制:

(1)AFImagecache:一个提供图片内存缓存的类,2.x时继承自NSCache,3.x不再使用NSCache。AFImagecache3.x之前存在于UIImageView+AFNetwork,之后存在于AFAutoPurgingImageCache中。

(2)NSURLCache:仍使用原生缓存机制:NSURLCache。NSURLConnection’s默认的URL缓存机制,用于存储NSURLResponse对象:一个默认缓存在内存,通过配置可以缓存到磁盘的类。NSURLCache对每个NSURLRequest对象都会遵守缓存策略(NSURLRequestCachePolicy)。

注意:NSCache与NSURLCache没有任何关系

3.0之前的缓存方法:存在于AFURLConnectionOperation类文件中。

- (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block;

3.0之后:在类AFURLSessionManager中

- (void)setDataTaskWillCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block;

苹果系统缓存存储策略:

{   
    NSURLCacheStorageAllowed,  //默认,可以存在内存(重启设备清除),可以存储磁盘(代码清除)
    NSURLCacheStorageAllowedInMemoryOnly,
    NSURLCacheStorageNotAllowed,
    } NSURLCacheStoragePolicy;

请求缓存策略:

{
  NSURLRequestUseProtocolCachePolicy = 0, //默认策略
  NSURLRequestReloadIgnoringLocalCacheData = 1,//忽略本地缓存,从源加载
  NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // 忽略本地&服务器缓存,从源加载
  NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,
  NSURLRequestReturnCacheDataElseLoad = 2,  //先从缓存加载,如果没有缓存,从源加载
  NSURLRequestReturnCacheDataDontLoad = 3,  //离线模式,加载缓存数据(无论是否过期),不从源加载
  NSURLRequestReloadRevalidatingCacheData = 5 // 存在的缓存数据先确认有效性,无效的话从源加载 
};
 typedef NSUInteger NSURLRequestCachePolicy;

清除所有的URL缓存Response:

[[NSURLCache sharedURLCache] removeAllCachedResponses];

8.谈谈你对算法的理解,在工作中你都应用了哪些算法来解决问题

基础算法

快速排序算法

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。

快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。

算法步骤:

1 从数列中挑出一个元素,称为 “基准”(pivot),
2 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
3 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。


快速排序.gif

堆排序算法

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
堆排序的平均时间复杂度为Ο(nlogn) 。

算法步骤:
创建一个堆H[0..n-1]
把堆首(最大值)和堆尾互换

  1. 把堆的尺寸缩小1,并调用shift_down(0),目的是把新的数组顶端数据调整到相应位置
  2. 重复步骤2,直到堆的尺寸为1


    堆排序算法.gif

归并排序

归并排序(Merge sort,台湾译作:合并排序)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

算法步骤:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
  4. 重复步骤3直到某一指针达到序列尾
  5. 将另一序列剩下的所有元素直接复制到合并序列尾


    归并算法.gif

二分查找算法

二分查找算法是一种在有序数组中查找某一特定元素的搜索算法。搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。折半搜索每次把搜索区域减少一半,时间复杂度为Ο(logn) 。

加密算法原理

MD5加密

MD5加密是最常用的加密方法之一,是从一段字符串中通过相应特征生成一段32位的数字字母混合码。

MD5主要特点是 不可逆,相同数据的MD5值肯定一样,不同数据的MD5值不一样(也不是绝对的,但基本是不能一样的)。MD5算法还具有以下性质:
1、压缩性:任意长度的数据,算出的MD5值长度都是固定的。
2、容易计算:从原数据计算出MD5值很容易。
3、抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
4、弱抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
5、强抗碰撞:想找到两个不同的数据,使它们具有相同的MD5值,是非常困难的。
MD5虽然说是不可逆的,但是由于有网站http://www.cmd5.com的存在,专门用来查询MD5码 所以有的简单的MD5码是可以在这里搜到源码的。为了让MD5码更加安全 涌现了很多其他方法 如加盐。 盐要足够长足够乱 得到的MD5码就很难查到。

SHA1加密

安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要。

SHA1有如下特性:不可以从消息摘要中复原信息;两个不同的消息不会产生同样的消息摘要。

HMAC加密

此加密方法需要先生成密钥,然后再对密码进行MD5和HMAC加密,数据库中需要存放当时使用的密钥和密码加密后的密文,在用户登陆时 再次对填入的密码用密钥进行加密 并且还要加上当前时间(精确到分钟) 再次HMAC加密,服务器里也会拿出以前存放的密文加上时间再次加密。所以就算黑客在中途截取了密码的密文 也在能在1分钟只能破译才能有效,大大加强了安全性。服务器为了考虑到网络的延迟一般会多算一种答案,如23分过来的密码 他会把23分和22分的都算一下和用户匹配只要对上一个就允许登陆。

base64加密

在MIME格式的电子邮件中,base64可以用来将binary的字节序列数据编码成ASCII字符序列构成的文本。使用时,在传输编码方式中指定base64。使用的字符包括大小写字母各26个,加上10个数字,和加号“+”,斜杠“/”,一共64个字符,等号“=”用来作为后缀用途。

完整的base64定义可见RFC 1421和RFC 2045。编码后的数据比原始数据略长,为原来的4/3。

对称加密算法

优点:算法公开、计算量小、加密速度快、加密效率高、可逆
缺点:双方使用相同钥匙,安全性得不到保证
现状:对称加密的速度比公钥加密快很多,在很多场合都需要对称加密,相较于DES和3DES算法而言,AES算法有着更高的速度和资源使用效率,安全级别也较之更高了,被称为下一代加密标准
nECB :电子代码本,就是说每个块都是独立加密的nCBC :密码块链,使用一个密钥和一个初始化向量 (IV)对数据执行加密转换
ECB和CBC区别:CBC更加复杂更加安全,里面加入了8位的向量(8个0的话结果等于ECB)。在明文里面改一个字母,ECB密文对应的那一行会改变,CBC密文从那一行往后都会改变。

RSA加密

RSA非对称加密算法
非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密
特点:非对称密码体制的特点:算法强度复杂、安全性依赖于算法与密钥,但是由于其算法复杂,而使得加密解密速度没有对称加密解密的速度快,对称密码体制中只有一种密钥,并且是非公开的,如果要解密就得让对方知道密钥。所以保证其安全性就是保证密钥的安全,而非对称密钥体制有两种密钥,其中一个是公开的,这样就可以不需要像对称密码那样传输对方的密钥了
基本加密原理:
(1)找出两个“很大”的质数:P & Q
(2)N = P * Q
(3)M = (P – 1) * (Q – 1)
(4)找出整数E,E与M互质,即除了1之外,没有其他公约数
(5)找出整数D,使得E*D除以M余1,即 (E * D) % M = 1

经过上述准备工作之后,可以得到:E是公钥,负责加密D是私钥,负责解密N负责公钥和私钥之间的联系加密算法,假定对X进行加密(X ^ E) % N = Yn根据费尔马小定义,根据以下公式可以完成解密操作(Y ^ D) % N = X
但是RSA加密算法效率较差,对大型数据加密时间很长,一般用于小数据。常用场景:分部要给总部发一段报文,先对报文整个进行MD5得到一个报文摘要,再对这个报文摘要用公钥加密。然后把报文和这个RSA密文一起发过去。总部接收到报文之后要先确定报文是否在中途被人篡改,就先把这个密文用私钥解密得到报文摘要,再和整个报文MD5一下得到的报文摘要进行对比 如果一样就是没被改过。

9.谈谈你对React Native 、weex、Web、NativeAPP 和Hybrid APP 的看法

Native APP开发模式

Web App(网页应用)

Hybrid APP

React Native

React Native 和 Weex 的原理。

了解更多 React Native 请点击这里

了解更多 Weex 请点击这里

没有说哪种方式最好,只能根据需求以及时机来选择符合当前业务的开发方式是最好的,毛爷爷说过:不论是白猫还是黑猫,逮到老鼠就是好猫。

以上的知识点什么的很多都是借鉴网上的文章来的。只是我做了一个总结,有什么不对的请指出来。大家共同进步。谢谢观看!!!

上一篇下一篇

猜你喜欢

热点阅读