zoukankan      html  css  js  c++  java
  • 2021年3-5年经验iOS面试总结

    求职之路可谓是“一把辛酸一把泪”。除了经验和阅历,完全没有优势。但还是收到几家公司的面试邀请,但之后便没有后续。趁面试之余将面试室所遇到提问总结整理一下,以供大家参考:

    内存管理

    软件运行时会分配和使用设备的内存资源,因此,在软件开发的过程中,需要进行内存管理,以保证高效、快速的分配内存,并且在适当的时候释放和回收内存资源。

    一、Objective-C内存管理的对象

    IOS开发中,内存中的对象主要有两类,一类是值类型,比如int、float、struct等基本数据类型,另一类是引用类型,也就是继承自NSObject类的所有的OC对象。前一种值类型不需要我们管理,后一种引用类型是需要我们管理内存的,一旦管理不好,就会产生非常糟糕的后果。

    为什么值类型不需要管理,而引用类型需要管理呢?****那是因为他们分配内存方式不一样。

    1、值类型会被放入栈中,他们依次紧密排列,在内存中占有一块连续的内存空间,遵循先进后出的原则。

    2、引用类型会被放到堆中,当给对象分配内存空间时,会随机的从内存当中开辟空间,对象与对象之间可能会留有不确定大小的空白空间,因此会产生很多内存碎片,需要我们管理。

    作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:834688868,不管你是大牛还是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!

    如果你正在面试,或者正准备跳槽不妨动动小手,添加一下咱们的交流群:834688868来获取一份详细的大厂面试资料 为你的跳槽加薪多一份保障

    3、栈内存与堆内存从性能上比较,栈内存要优于堆内存,这是因为栈遵循先进后出的原则,因此当数据量过大时,存入栈会明显的降低性能。

    4、值类型和引用类型之间是可以相互转化的,把值类型转化为引用类型的过程叫做装箱,比如把int包装为NSNumber,这个过程会增加程序的运行时间,降低性能。在拆箱的过程中,我们一定要注意数据原有的类型,如果类型错误,可能导致拆箱失败,因此会存在安全性的问题。手动的拆箱和装箱,都会增加程序的运行时间,降低代码可读性,影响性能。

    5、在IOS开发过程中,栈内存中的值类型系统会自动管理,堆内存中的引用类型是需要我们管理的。如果引用计数为0,对象回收,不为0不回收。当对象执行alloc、new或者retain时,引用计数加1,release时,引用计数减1。

    二、Objective-C管理内存的方式

    Objective-c中提供了两种内存管理机制MRC(Mannul Reference Counting)和ARC(Automatic Reference Counting),分别提供对内存的手动和自动管理,来满足不同的需求。

    1.MRC(人工引用计数),手动管理内存。

    MRC模式下,所有的对象都需要手动的添加retain、release代码来管理内存。使用MRC,需要遵守谁创建,谁回收的原则。当引用计数为0的时候,必须回收,引用计数不为0,不能回收,如果引用计数为0,但是没有回收,会造成内存泄露。如果引用计数为0,继续释放,会造成野指针。为了避免出现野指针,我们在释放的时候,会先让指针=nil。

    2.ARC(自动引用计数),自动管理内存。

    ARC是IOS5推出的新功能,通过ARC,可以自动的管理内存。在ARC模式下,只要没有强指针(强引用)指向对象,对象就会被释放。在ARC模式下,不允许使用retain、release、retainCount等方法。并且,如果使用dealloc方法时,不允许调用[super dealloc]方法。

    ARC模式下的property变量修饰词为strong、weak,相当于MRC模式下的retain、assign。strong :代替retain,缺省关键词,代表强引用。weak:代替assign,声明了一个可以自动设置nil的弱引用,但是比assign多一个功能,指针指向的地址被释放之后,指针本身也会自动被释放。

    三、与内存有关的修饰符

    strong :强引用,ARC中使用,与MRC中retain类似,使用之后,计数器+1。

    weak :弱引用 ,ARC中使用,如果只想的对象被释放了,其指向nil,可以有效的避免野指针,其引用计数为1。

    readwrite : 可读可写特性,需要生成getter方法和setter方法时使用。

    readonly : 只读特性,只会生成getter方法 不会生成setter方法,不希望属性在类外改变。

    assign :赋值特性,不涉及引用计数,弱引用,setter方法将传入参数赋值给实例变量,仅设置变量时使用。

    retain :表示持有特性,setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1。

    copy :表示拷贝特性,setter方法将传入对象复制一份,需要完全一份新的变量时。

    nonatomic :非原子操作,不加同步,多线程访问可提高性能,但是线程不安全的。决定编译器生成的setter getter是否是原子操作。

    atomic :原子操作,同步的,表示多线程安全,与nonatomic相反。

    四、MRC与ARC混编

    MRC与ARC理论上是不能兼容的,也就是你如果创建的项目是ARC模式的,在你的代码中是不能使用release,否则会出现内存问题。现在大部分程序都会选择ARC的方式,但是很多第三方的框架是MRC模式,如果想把这些第三方的文件加到自己项目中,需要进行标识,否则编译的时候会出现错误。

    在ARC的项目中,对MRC的文件可以添加编译选项-fno-objc-arc的标识;在MRC的项目中,对ARC的文件可以添加编译选项 -fobjc-arc的标识。把MRC文件转为ARC,实际上是去掉文件中的retain、release。

    参考链接:https://www.jianshu.com/p/66b5d43b6ac4

    RunLoop

    一. RunLoop简介

    运行循环,在程序运行过程中循环做一些事情,如果没有Runloop程序执行完毕就会立即退出,如果有Runloop程序会一直运行,并且时时刻刻在等待用户的输入操作。RunLoop可以在需要的时候自己跑起来运行,在没有操作的时候就停下来休息。充分节省CPU资源,提高程序性能。

    二. RunLoop基本作用:

    保持程序持续运行,程序一启动就会开一个主线程,主线程一开起来就会跑一个主线程对应的RunLoop,RunLoop保证主线程不会被销毁,也就保证了程序的持续运行

    处理App中的各种事件(比如:触摸事件,定时器事件,Selector事件等)

    节省CPU资源,提高程序性能,程序运行起来时,当什么操作都没有做的时候,RunLoop就告诉CUP,现在没有事情做,我要去休息,这时CUP就会将其资源释放出来去做其他的事情,当有事情做的时候RunLoop就会立马起来去做事情

    三. RunLoop在哪里开启

    UIApplicationMain函数内启动了Runloop,程序不会马上退出,而是保持运行状态。因此每一个应用必须要有一个runloop,

    我们知道主线程一开起来,就会跑一个和主线程对应的RunLoop,那么RunLoop一定是在程序的入口main函数中开启。

    四. RunLoop对象

    Fundation框架  (基于CFRunLoopRef的封装)

    NSRunLoop对象

    CoreFoundation

    CFRunLoopRef对象

    因为Fundation框架是基于CFRunLoopRef的一层OC封装,这里我们主要研究CFRunLoopRef源码

    如何获得RunLoop对象

    Foundation[NSRunLoop currentRunLoop];// 获得当前线程的RunLoop对象[NSRunLoop mainRunLoop];// 获得主线程的RunLoop对象Core FoundationCFRunLoopGetCurrent();// 获得当前线程的RunLoop对象CFRunLoopGetMain();// 获得主线程的RunLoop对象

    五. RunLoop和线程间的关系

    每条线程都有唯一的一个与之对应的RunLoop对象,RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value,主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建,RunLoop在第一次获取时创建,在线程结束时销毁。

    参考链接:https://www.jianshu.com/p/de752066d0ad

    runtime

    Runtime的特性主要是消息(方法)传递,如果消息(方法)在对象中找不到,就进行转发。

    1.1系统首先找到消息的接收对象,然后通过对象的isa找到它的类。

    1.2在它的类中查找method_list,是否有selector方法。

    1.3没有则查找父类的method_list。

    1.4找到对应的method,执行它的IMP。

    1.5转发IMP的return值。

    Runtime应用

    Runtime简直就是做大型框架的利器。它的应用场景非常多,下面就介绍一些常见的应用场景。

    2.1关联对象(Objective-C Associated Objects)给分类增加属性

    2.2方法魔法(Method Swizzling)方法添加和替换和KVO实现

    2.3消息转发(热更新)解决Bug(JSPatch)

    2.4实现NSCoding的自动归档和自动解档

    2.5实现字典和模型的自动转换(MJExtension)

    参考链接:https://www.jianshu.com/p/6ebda3cd8052

    多线程

    主要有三种:NSThread、NSoperationQueue、GCD

    1. NSThread:轻量级别的多线程技术

    是我们自己手动开辟的子线程,如果使用的是初始化方式就需要我们自己启动,如果使用的是构造器方式它就会自动启动。只要是我们手动开辟的线程,都需要我们自己管理该线程,不只是启动,还有该线程使用完毕后的资源回收。

    NSThread*thread=[[NSThread alloc]initWithTarget:selfselector:@selector(testThread:)object:@"我是参数"];// 当使用初始化方法出来的主线程需要start启动[thread start];// 可以为开辟的子线程起名字thread.name=@"NSThread线程";// 调整Thread的权限 线程权限的范围值为0 ~ 1 。越大权限越高,先执行的概率就会越高,由于是概率,所以并不能很准确的的实现我们想要的执行顺序,默认值是0.5thread.threadPriority=1;// 取消当前已经启动的线程[thread cancel];// 通过遍历构造器开辟子线程[NSThread detachNewThreadSelector:@selector(testThread:)toTarget:selfwithObject:@"构造器方式"];

    performSelector...只要是NSObject的子类或者对象都可以通过调用方法进入子线程和主线程,其实这些方法所开辟的子线程也是NSThread的另一种体现方式。

    在编译阶段并不会去检查方法是否有效存在,如果不存在只会给出警告

    //在当前线程。延迟1s执行。响应了OC语言的动态性:延迟到运行时才绑定方法[selfperformSelector:@selector(aaa)withObject:nil afterDelay:1];// 回到主线程。waitUntilDone:是否将该回调方法执行完在执行后面的代码,如果为YES:就必须等回调方法执行完成之后才能执行后面的代码,说白了就是阻塞当前的线程;如果是NO:就是不等回调方法结束,不会阻塞当前线程[selfperformSelectorOnMainThread:@selector(aaa)withObject:nil waitUntilDone:YES];//开辟子线程[selfperformSelectorInBackground:@selector(aaa)withObject:nil];//在指定线程执行[selfperformSelector:@selector(aaa)onThread:[NSThread currentThread]withObject:nil waitUntilDone:YES]

    需要注意的是:如果是带afterDelay的延时函数,会在内部创建一个 NSTimer,然后添加到当前线程的Runloop中。也就是如果当前线程没有开启runloop,该方法会失效。在子线程中,需要启动runloop(注意调用顺序)

    [selfperformSelector:@selector(aaa)withObject:nil afterDelay:1];[[NSRunLoop currentRunLoop]run];

    而performSelector:withObject:只是一个单纯的消息发送,和时间没有一点关系。所以不需要添加到子线程的Runloop中也能执行。

    2、GCD 对比 NSOprationQueue

    我们要明确NSOperationQueue与GCD之间的关系

    GCD是面向底层的C语言的API,NSOpertaionQueue用GCD构建封装的,是GCD的高级抽象。

    1、GCD执行效率更高,而且由于队列中执行的是由block构成的任务,这是一个轻量级的数据结构,写起来更方便

    2、GCD只支持FIFO的队列,而NSOperationQueue可以通过设置最大并发数,设置优先级,添加依赖关系等调整执行顺序

    3、NSOperationQueue甚至可以跨队列设置依赖关系,但是GCD只能通过设置串行队列,或者在队列内添加barrier(dispatch_barrier_async)任务,才能控制执行顺序,较为复杂

    4、NSOperationQueue因为面向对象,所以支持KVO,可以监测operation是否正在执行(isExecuted)、是否结束(isFinished)、是否取消(isCanceld)

    实际项目开发中,很多时候只是会用到异步操作,不会有特别复杂的线程关系管理,所以苹果推崇的且优化完善、运行快速的GCD是首选

    如果考虑异步操作之间的事务性,顺序行,依赖关系,比如多线程并发下载,GCD需要自己写更多的代码来实现,而NSOperationQueue已经内建了这些支持

    不论是GCD还是NSOperationQueue,我们接触的都是任务和队列,都没有直接接触到线程,事实上线程管理也的确不需要我们操心,系统对于线程的创建,调度管理和释放都做得很好。而NSThread需要我们自己去管理线程的生命周期,还要考虑线程同步、加锁问题,造成一些性能上的开销

    参考链接:https://www.jianshu.com/p/594d15d6c6a7https://www.jianshu.com/p/361e8a0a4e7e

    如何查看iOS已上架app的崩溃情况

    一、先分析app的崩溃的分布情况 这个需要有(iTunes connect账号),通过分析我们可以查看到自己的app奔溃主要发生在那些机型上。 如果没有账号,别着急,直接走第二步。

    二、打开xcode,下载崩溃日志,直接定位出问题代码行

    1、找到开发者账号进入iTunes connect

    2、进入app分析,查看app查看情况,app下载量,app崩溃情况

    3、这里我们先关注app崩溃的情况 可以看到历史累计崩溃11次点击这个崩溃的情况进入分析实际上的app的崩溃的分布版本和手机系统版本

    Xcode下载崩溃日志

    1、打开xcode 先要下载崩溃日志  Window -> Organizer

    2、选择crashes

    3、查看相应版本的崩溃日志

    4、点击对应的崩溃日志,后面会出现一个箭头,点击箭头就会进入到Xcode中对应的代码行,就可以根据自己的程序逻辑做出相应修改。

  • 相关阅读:
    poj 1113 Wall 凸包的应用
    NYOJ 78 圈水池 (入门级凸包)
    Monotone Chain Convex Hull(单调链凸包)
    poj Sudoku(数独) DFS
    poj 3009 Curling 2.0(dfs)
    poj 3083 Children of the Candy Corn
    Python join()方法
    通过FISH和下一代测序检测肺腺癌ALK基因融合比较
    华大病原微生物检测
    NGS检测ALK融合大起底--转载
  • 原文地址:https://www.cnblogs.com/iOSer1122/p/15338698.html
Copyright © 2011-2022 走看看