iOS面试——项目篇

作者: 经天纬地 | 来源:发表于2019-05-19 22:30 被阅读89次

    性能相关

    1- 项目中造成Tableview等列表滑动卡顿的原因有哪些?你是如何优化的?

    答:
    造成卡顿的原因主要由两方面引起:CPU和GPU
    CPU:
    1- 对象的创建:对象的创建会分配内存、调整属性、甚至还有读取文件等操作,比较消耗 CPU 资源。尽量用轻量的对象代替重量的对象,可以对性能有所优化,比如用CALayer代替UIView。适当的场景用懒加载
    2- 对象调整:对象的调整也经常是消耗 CPU 资源的地方。当视图层次调整时,UIView、CALayer 之间会出现很多方法调用与通知,所以在优化性能时,应该尽量避免调整视图层次、添加和移除视图。
    3- 对象销毁:对象的销毁虽然消耗资源不多,但累积起来也是不容忽视的。当容器内有大量对象要销毁时,可以放在后台线程销毁。
    4- 布局计算:尽量提前计算好布局,在需要时一次性调整好对应属性,而不要多次、频繁的计算和调整这些属性。
    5- Autolayout:Autolayout 对于复杂视图来说常常会产生严重的性能问题。对于复杂试图可用frame计算布局。可以使用ComponentKit、AsyncDisplayKit(现已更换成Texture) 等框架。
    6- 文本计算:常见的文本控件 (UILabel、UITextView 等),其排版和绘制都是在主线程进行的,当显示大量文本时,CPU 的压力会非常大。解决的方法只能通过自定义文本控件,用 TextKit 或最底层的 CoreText 对文本异步绘制。
    7- 图片的解码:后台线程先把图片绘制到 CGBitmapContext 中,然后从 Bitmap 直接创建图片。
    8- 图像的绘制:把图像绘制任务放在后台线程中。例:

    - (void)display {
        dispatch_async(backgroundQueue, ^{
            CGContextRef ctx = CGBitmapContextCreate(...);
            // draw in context...
            CGImageRef img = CGBitmapContextCreateImage(ctx);
            CFRelease(ctx);
            dispatch_async(mainQueue, ^{
                layer.contents = img;
            });
        });
    }
    

    GPU:
    1- 纹理的渲染:尽量减少在短时间内大量图片的显示,尽可能将多张图片合成为一张进行显示。
    2- 视图的混合:尽量减少视图数量和层次,并在不透明的视图里标明 opaque 属性以避免无用的 Alpha 通道合成。
    3- 图形的生成:避免离屏渲染,对于圆角,可以用一张已经绘制好的圆角图片覆盖到原本视图上面来模拟相同的视觉效果。最彻底的解决办法,就是把需要显示的图形在后台线程绘制为图片,避免使用圆角、阴影、遮罩等属性。

    2- 如何实现列表多个定时器

    答:定义一个数组存放列表的倒计时结束时间,创建一个GCD定时器,取出列表中每个时间进行倒计时,得到的定时器时间再对cell赋值即可。
    参考demo:https://github.com/zhYes/YSTimeCountDown

    3- 如何优化视频播放,如何实现自动根据网速选择合适清晰度

    答:
    1.监控网速:

    导入头文件:
    #include <ifaddrs.h>
    #include <arpa/inet.h>
    #include <net/if.h>
    /*获取网络流量信息*/
    - (long long) getInterfaceBytes
    {
        struct ifaddrs *ifa_list = 0, *ifa;
        if (getifaddrs(&ifa_list) == -1)
        {
            return 0;
        }
        
        uint32_t iBytes = 0;
        uint32_t oBytes = 0;
        
        for (ifa = ifa_list; ifa; ifa = ifa->ifa_next)
        {
            if (AF_LINK != ifa->ifa_addr->sa_family)
                continue;
            
            if (!(ifa->ifa_flags & IFF_UP) && !(ifa->ifa_flags & IFF_RUNNING))
                continue;
            
            if (ifa->ifa_data == 0)
                continue;
            
            /* Not a loopback device. */
            if (strncmp(ifa->ifa_name, "lo", 2))
            {
                struct if_data *if_data = (struct if_data *)ifa->ifa_data;
                
                iBytes += if_data->ifi_ibytes;
                oBytes += if_data->ifi_obytes;
            }
        }
        freeifaddrs(ifa_list);
        
        NSLog(@"\n[getInterfaceBytes-Total]%d,%d",iBytes,oBytes);
        return iBytes + oBytes;
    }
    

    内存优化相关

    1- 如何加快app启动速度

    答:
    app启动分冷启动和热启动两种,冷启动就是app从0开始启动,热启动是app退回桌面,没有被杀死的情况下启动。我们一般只需要对冷启动进行优化。
    App启动过程:

    ①解析Info.plist 
    加载相关信息,例如闪屏
    沙箱建立、权限检查
    ②Mach-O加载 
    如果是胖二进制文件,寻找合适当前CPU架构的部分
    加载所有依赖的Mach-O文件(递归调用Mach-O加载的方法)
    定位内部、外部指针引用,例如字符串、函数等
    加载类扩展(Category)中的方法
    C++静态对象加载、调用ObjC的 +load 函数
    执行声明为__attribute__((constructor))的C函数
    ③程序执行 
    调用main()
    调用UIApplicationMain()
    调用applicationWillFinishLaunching
    
    main()函数之前耗时的影响因素
    1. 动态库加载越多,启动越慢。
    2. ObjC类越多,启动越慢
    3. C的constructor函数越多,启动越慢
    4. C++静态对象越多,启动越慢
    5. ObjC的+load越多,启动越慢
    对应的方法:
    1. 减少动态库,换成静态库。
    2. 多个动态库合成一个。
    3. 减少无用类和无用方法。
    4. 尽量不要写__attribute__((constructor))的C函数。
    5. 尽量不要用到C++的静态对象。
    6. initlaze方法代替load方法。
    7. 合并功能类似的分类和扩展。
    8. 压缩资源图片
    main()函数之后耗时的影响因素
    1. 执行main()函数的耗时
    2. 执行applicationWillFinishLaunching的耗时
    3. rootViewController及其childViewController的加载、view及其subviews的加载
    对应的方法:
    1. 将不需要马上在applicationWillFinishLaunching执行的代码延后执行
    2. rootViewController的加载,适当将某一级的childViewController或subviews延后加载
    3. 如果你的App可能会被后台拉起并冷启动,可考虑不加载rootViewController

    安全相关

    1- 项目中如何对敏感信息加密

    答:
    信息加密常见的有两类:
    第一类无需解密:例如系统登录密码加密,通过加密算法对用户输入密码进行加密后存放在数据库中,用户再次登录时依然拿相同的加密算法对用户输入密码进行加密,拿加密后的结果和数据库中存放的结果做对比,整个过程中都不需要知道用户输入的原始密码是什么,MD5是处理此类加密最常用的加密算法

    第二类需要解密:例如我们写在项目代码中连接数据库的账号密码,项目代码中以密文方式存储,当需要连接数据库的时候,要对密文进行解密,拿到原始未加密的账号密码去连接数据库,与MD5单向加密不同,这类加密需要能对加密后的密文进行解密,此类加密方法目前最常用的加密算法为RSA

    RSA加密流程:

    1. 加密算法需要生成一对RSA秘钥,分别为公钥和私钥
    2. 拿公钥对密码进行加密得到加密后的字符串配置在项目代码中,需要使用原始密码的时候拿私钥对加密后的字符串进行解密得到原始密码
    3. 私钥不能直接放项目中,而是由服务器分发

    另外,使用HTTPS能加强数据安全

    2- 如何防抓包、防反调试?

    防抓包:
    可以判断用户是否设置了代理:

    - (BOOL)getProxyStatus {
        NSDictionary *proxySettings =  (__bridge NSDictionary *)(CFNetworkCopySystemProxySettings());
        NSArray *proxies = (__bridge NSArray *)(CFNetworkCopyProxiesForURL((__bridge CFURLRef _Nonnull)([NSURL URLWithString:@"http://www.baidu.com"]), (__bridge CFDictionaryRef _Nonnull)(proxySettings)));
        NSDictionary *settings = [proxies objectAtIndex:0];
        
        NSLog(@"host=%@", [settings objectForKey:(NSString *)kCFProxyHostNameKey]);
        NSLog(@"port=%@", [settings objectForKey:(NSString *)kCFProxyPortNumberKey]);
        NSLog(@"type=%@", [settings objectForKey:(NSString *)kCFProxyTypeKey]);
        
        if ([[settings objectForKey:(NSString *)kCFProxyTypeKey] isEqualToString:@"kCFProxyTypeNone"]){
            //没有设置代理
            return NO;
        }else{
            //设置代理了
            return YES;
        }
    }
    

    防反调试:

    1. 防止Ptrace调试器依附。
    2. 越狱检测。
    3. 敏感字符串安全。
    4. 代码混淆。
    5. 关键代码使用C甚至汇编实现。

    3- 项目开发阶段和上线阶段,你是如何收集崩溃信息的?又是如何解决的?有什么方法可以减少项目崩溃率?

    答:

    1. 直接调用系统函数获取崩溃时的栈信息
    //把收集到的信息上传到第三方crash统计工具,如Bugly等
    NSUncaughtExceptionHandler *handler = NSGetUncaughtExceptionHandler( );
    
    
    1. 使用PLCrashReporter、KSCrash等第三方框架

    3- 加密的方式有几种?你在项目中具体的加密流程是怎样的?

    1. MD5加密
    2. RSA加密

    网络相关

    1- 接入HTTPS流程

    1. 申请SSL证书
    2. 设置AFN请求管理者的时候 添加 https ssl 验证。
    // 1.获得请求管理者
    
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    
    // 2.加上这个函数,https ssl 验证。
    
    [manager setSecurityPolicy:[self customSecurityPolicy]];
    
    // https ssl 验证函数
    
    - (AFSecurityPolicy *)customSecurityPolicy {
    
    // 先导入证书
    
    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"xxx" ofType:@"cer"];//证书的路径
    
    NSData *cerData = [NSData dataWithContentsOfFile:cerPath];
    
    // AFSSLPinningModeCertificate 使用证书验证模式
    
    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
    // allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO
    
    //validatesDomainName 是否需要验证域名,默认为YES;
    }
    

    2- TCP和UDP的区别

    3- TCP/IP 3次握手的流程

    设计、架构相关

    1- MVVM的思想是什么?你在项目中是如何设计的

    相关文章

      网友评论

        本文标题:iOS面试——项目篇

        本文链接:https://www.haomeiwen.com/subject/xjrtzqtx.html