您好,欢迎来到二三四教育网。
搜索
您的当前位置:首页面试 2018

面试 2018

来源:二三四教育网

2018年3月13

SD源码

1、placeholderImage 显示
2、先从内存图片缓存查找是否有图片,
如果内存中已经有图片缓存,回调前端展示图片
如果内存缓存中没有,从硬盘查找图片是否已经缓存。
3、如果从硬盘缓存目录读取不到图片,
说明所有缓存都不存在该图片,需要下载图片,SDWebImageDownloader开始下载图片
4、图片下载由 NSURLConnection 来做,
实现相关 delegate 来判断图片下载中、下载完成和下载失败。

5、数据下载完成后交给 SDWebImageDecoder 做图片解码处理。图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,
最好也在这里完成,效率会好很多。

链表 哈希表

数组

上体育课的时候,老师说:你们站一队,每个人记住自己是第几个,我喊到几,那个人就举手,这就是。

链表

老是说,你们每个人记住自己前面的人和后面的人,然后老师只知道第一人是谁。 然后你们各自由活动,老是要找某一个人,是不是每次都是从第一个开始往自己身后的人开始传达?这就是。

哈希

老师说: 大家1,2,3,4报数,凡是报1,为1队,凡是报2的为2队。。。。 这就是散列(哈希)。而这个4就相当于预定义好的桶的个数。

数组和链表的区别:

1,数组是将元素在内存中连续存放。

链表中的元素在内存中不是顺序存储的,而是通过存在元素中的指针联系到一起。

2,数组必须事先定义固定的长度,不能适应数据动态的增减的情况。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成内存浪费;

  链表动态地进行存储分配,可以适应数据动态地增减的情况。

3,(静态)数组从栈中分配空间,对于程序员方便快速,但是自由度小;

  链表从堆中分配空间,自由度大但是申请管理比较麻烦。

数组和链表在存储数据方面到底谁好?根据数组和链表的特性,分两种情况讨论:

1,当进行数据查询时,数组可以直接通过下标迅速访问数组中的元素。

 而链表则需要从第一个元素开始一直找到需要的元素位置,

   显然,数组的查询效率会比链表的高。

2,当进行增加或删除元素时,在数组中增加一个元素,需要移动大量元素,在内存中空出一个元素的空间,然后将要增加的元素放在其中。同样,如果想删除一个元素,需要移动大量去填掉被移动的元素,而链表只需改动元素中的指针即可实现增加或删除元素。

哈希表

所谓的hash,简单的说就是散列,即将输入的数据通过hash函数得到一个key值,输入的数据存储到数组中下标的key值的数组单元中去。

链表

定义:链式存储的线性表,简称链表。

节点:链表由多个链表元素组成,这些元素称为节点。结点之间通过逻辑连接,形成链式存储结构。存储结点的内存单元,可以是连续的也可以是不连续的。逻辑连接与物理存储次序没有关系。

**链表分为两个域: **
值域:用于存放结点的值
链域:用于存放下一个结点的地址或位置
从内存角度出发: 链表可分为 静态链表、动态链表。
从链表存储方式的角度出发:链表可分为 单链表、双链表、以及循环链表。

静态链表:
把线性表的元素存放在数组中,这些元素可能在物理上是连续存放的,也有可能不是连续的,它们之间通过逻辑关系来连接,数组单位存放链表结点,结点的链域指向下一个元素的位置,即下一个元素所在的数组单元的下标。显然静态链表需要数组来实现。
引出的问题:数组的长度定义的问题,无法预支。

**动态链表:(实际当中用的最多) **
改善了静态链表的缺点。它动态的为节点分配存储单元。当有节点插入时,系统动态的为结点分配空间。在结点删除时,应该及时释放相应的存储单元,以防止内存泄露。

**单链表: **
单链表是一种顺序存储的结构。
有一个头结点,没有值域,只有连域,专门存放第一个结点的地址。
有一个尾结点,有值域,也有链域,链域值始终为NULL。
所以,在单链表中为找第i个结点或数据元素,必须先找到第i - 1 结点或数据元素,而且必须知道头结点,否者整个链表无法访问。

循环链表:
循环链表,类似于单链表,也是一种链式存储结构,循环链表由单链表演化过来。单链表的最后一个结点的链域指向NULL,而循环链表的建立,不要专门的头结点,让最后一个结点的链域指向链表结点。

循环链表与单链表的区别
区别一、链表的建立。单链表需要创建一个头结点,专门存放第一个结点的地址。单链表的链域指向NULL。而循环链表的建立,不要专门的头结点,让最后一个结点的链域指向链表的头结点。
区别二、链表表尾的判断。单链表判断结点是否为表尾结点,只需判断结点的链域值是否是NULL。如果是,则为尾结点;否则不是。而循环链表盘判断是否为尾结点,则是判断该节点的链域是不是指向链表的头结点。

遇到tableView卡顿嘛?会造成卡顿的原因大致有哪些?

1.最常用的就是cell的重用, 注册重用标识符

2.避免cell的重新布局

3.提前计算并缓存cell的属性及内容

当我们创建cell的数据源方法时,编译器并不是先创建cell 再定cell的高度

而是先根据内容一次确定每一个cell的高度,高度确定后,再创建要显示的cell,滚动时,每当cell进入凭虚都会计算高度,提前估算高度告诉编译器,编译器知道高度后,紧接着就会创建cell,这时再调用高度的具体计算方法,这样可以方式浪费时间去计算显示以外的cell

4.减少cell中控件的数量

尽量使cell得布局大致相同,不同风格的cell可以使用不用的重用标识符,初始化时添加控件,

不适用的可以先隐藏

5.不要使用ClearColor,无背景色,透明度也不要设置为0 尽量使所有的view opaque,包括Cell自身

渲染耗时比较长

6.使用局部更新

如果只是更新某组的话,使用reloadSection进行局部更

7.加载网络数据,下载图片,使用异步加载,并缓存

8.尽量少用addView给Cell动态添加View,可以初始化时就添加,然后通过hide来控制是否显示

9.按需加载cell,cell滚动很快时,只加载范围内的cell

11.缓存行高:只要是固定行高就写预估行高来减少行高调用次数提升性能。如果是动态行高就不要写预估方法了,用一个行高的缓存字典来减少代码的调用次数即可

12.不要做多余的绘制工作。在实现drawRect:的时候,它的rect参数就是需要绘制的区域,这个区域之外的不需要进行绘制。例如上例中,就可以用CGRectIntersectsRect、CGRectIntersection或CGRectContainsRect判断是否需要绘制image和text,然后再调用绘制方法。

13.预渲染图像。当新的图像出现时,仍然会有短暂的停顿现象。解决的办法就是在bitmap context里先将其画一遍,导出成UIImage对象,然后再绘制到屏幕;

14.使用正确的数据结构来存储数据。

编译过程做了哪些事情?

Objective C采用Clang作为前端,而Swift则采用swift()作为前端,二者LLVM(Low level vritual machine)作为编译器后端。所以简单的编译过程如图

编译器前端的任务是进行:

语法分析,语义分析,生成中间代码(intermediate representation )。在这个过程中,会进行类型检查,如果发现错误或者警告会标注出来在哪一行。

编译器后端

编译器后端会进行机器无关的代码优化,生成机器语言,并且进行机器相关的代码优化。iOS的编译过程,后端的处理如下

LVVM优化器会进行BitCode的生成,链接期优化等等
LLVM机器码生成器会针对不同的架构,比如arm64等生成不同的机器码。

内存分区

堆区(heap) 由程序员分配和释放,如果程序员不释放,程序结束时,可能会由操作系统回收 ,比如在ios 中 alloc 都是存放在堆中。
优点是灵活方便,数据适应面广泛,但是效率有一定降低

全局区(静态区) (static) 全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量存放在一块区域,未初始化的全局变量和静态变量在相邻的另一块区域,程序结束后有系统释放。

文字常量区 存放常量字符串,程序结束后由系统释放

代码区 存放函数的二进制代码

比如申请后的系统是如何响应的?

栈:存储每一个函数在执行的时候都会向操作系统索要资源,栈区就是函数运行时的内存,栈区中的变量由编译器负责分配和释放,内存随着函数的运行分配,随着函数的结束而释放,由系统自动完成。

注意:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。

堆:

1.首先应该知道操作系统有一个记录空闲内存地址的链表。

2.当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。

3 .由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中

申请大小的限制是怎样的?

栈是向低地址扩展的数据结构,是一块连续的内存的区域。是栈顶的地址和栈的最大容量是系统预先规定好的,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数 ) ,如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。

堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

Runtime

load innitialize

消息转发:

import objc/message.h

import objc/runtime.h

RunLoop

GCD

同步异步 并行 串行

GCD 线程间的通信
GCD 栅栏方法:dispatch_barrier_async
GCD 一次性代码(只执行一次):dispatch_once
GCD 延时执行方法:dispatch_after
GCD 快速迭代方法:dispatch_apply

6.5 GCD 队列组:dispatch_group dispatch_group_enter、dispatch_group_leave

dispatch_group_wait
dispatch_group_notify

6.6 GCD 信号量:dispatch_semaphore
dispatch_semaphore_create dispatch_semaphore_signal dispatch_semaphore_wait
线程同步

加密

Https

优化你是从哪几方面着手?

启动过程中做的事情越少越好(尽可能将多个接口合并)

不在UI线程上作耗时的操作(数据的处理在子线程进行,处理完通知主线程刷新节目)

在合适的时机开始后台任务(例如在用户指引节目就可以开始准备加载的数据)

尽量减小包的大小

辅助工具(友盟,听云,Flurry)

数据的分页(后端数据多的话,就要分页返回,例如网易新闻,或者 微博记录)

数据压缩(大数据也可以压缩返回,减少流量,加快反应速度)

内容缓存(例如网易新闻的最新新闻列表都是要缓存到本地,从本地加载,可以缓存到内存,或者数据库,根据情况而定)

延时加载tab(比如app有5个tab,可以先加载第一个要显示的tab,其他的在显示时候加载,按需加载)

算法的优化(核心算法的优化,例如有些app 有个 联系人姓名用汉语拼音的首字母排序)

Tableview 优化(tableview cell的加载优化)

ViewController加载优化(不同view之间的跳转,可以提前准备好数据)

四、数据库的优化:

数据库设计上面的重构

查询语句的优化

分库分表(数据太多的时候,可以分不同的表或者库)

五、服务器端和客户端的交互优化:

客户端尽量减少请求

服务端尽量做多的逻辑处理

服务器端和客户端采取推拉结合的方式(可以利用一些同步机制)

通信协议的优化。(减少报文的大小)

电量使用优化(尽量不要使用后台运行)

六、非技术性能优化

产品设计的逻辑性

界面交互的规范(每个模块的界面的交互尽量统一,符合操作习惯)

代码规范(这个可以隐形带来app 性能的提高,比如 用if else 还是switch ,或者是用!还是 ==)

code review(坚持code Review 持续重构代码。减少代码的逻辑复杂度)

SD源码

1、传参进去 url 默认图
2、有默认图 显示默认图
3、url 查内存缓存 查磁盘缓存
4、下载器 下载 nsurlConnection
5、解码
6、回调显示
7、内存缓存,磁盘缓存
8、内存警告 释放内存缓存

内存分区

算法

26、怎么解决sqlite锁定的问题

加锁synchroized

Block中内存情况

2、如果访问了栈区或者堆区的变量,那就会被存在堆区(实际存在栈区,ARC下会自动拷贝到堆区)

插入 n
冒泡

排序

插入排序(Insertion Sort)

最佳情况:T(n) = O(n) 最坏情况:T(n) = O(n2) 平均情况:T(n) = O(n2)

选择排序(Selection Sort)

最佳情况:T(n) = O(n2) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(n2)

冒泡排序(Bubble Sort)

最佳情况:T(n) = O(n) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(n2)

快速排序

最佳情况:T(n) = O(nlogn) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(nlogn)

希尔排序(Shell Sort)

设计模式

装饰器(Decorator)模式
装饰器模式在不修改原来代码的情况下动态的给对象增加新的行为和职责,它通过一个对象包装被装饰对象的方法来修改类的行为,这种方法可以做为子类化的一种替代方法。

在Objective-C中,存在两种非常常见的实现:Category(类别)和Delegation(委托)。
Category(类别)

MVC模式 MVVM

类别 类扩展

iOS 自己封装的SDK

排序 需要手写一下
手写代码
让判断IP地址的合法性,这种题是让手写的。
runloop
怎么解决比如手势冲突啦之类的
问你怎么去设计一个缓存类,这个百度特别爱考,如果去百度面试,你可以注意下。
所有 简单的题,其实考的不是算法思路,是考的对细节的把控
问OC的内存机制,isEqual的实现原理,常见算法
1,你知道ios里面存数据有哪些方法吗?
3,然后又问我在ios里面实现微博的下拉刷新列表怎么实现。
响应链
有10个cell需要下载图片,那有几个线程在同时下载?SDWebImage开了几个线程?

如果是你考虑怎么优化内

图片圆角

手写一个单例

本地数据存储都有哪几种方式

性能优化

instrument

Activity Monitor(活动监视器)
Allocations(内存分配):

跟踪过程的匿名虚拟内存和堆的对象提供类名和可选保留/释放历史;

参考:

MLeaksFinder
FBRetainCycleDetector

1.Time Profiler:CPU分析工具分析代码的执行时间。

2.Core Animation:离屏渲染,图层混合等GPU耗时。

3.Leaks:内存检测,内存泄漏检测工具。

4.Energy Log:耗电检测工具。


Activity Monitor(活动监视器)

组件化欠缺

二叉树

单元测试

18、NSOperatinQueue和CGD的区别,什么情况下用NSOperationQueue,什么情况下用GCD

1> GCD是纯C语言的API 。NSOperationQueue是基于GCD的OC的封装。

2> GCD只支持FIFO队列,NSOperationQueue可以方便设置执行顺序,设置最大的并发数量。

3> NSOperationQueue可是方便的设置operation之间的依赖关系,GCD则需要很多代码。

4> NSOperationQueue支持KVO,可以检测operation是否正在执行(isExecuted),是否结束(isFinished),是否取消(isCanceled)

5>GCD的执行速度比NSOperationQueue快。

使用场合:

任务之间不太相互依赖:GCD

任务之间有依赖或要监听任务的执行情况:NSOperationQueue

响应链

你是如何组件化解耦的

单元测试

RN

JS

weak 原理

OS开发 之 不要告诉我你真的懂isEqual与hash!

手写 排序 2种 单例

Timer 循环引用问题

二分

抖音

opengl流程

直播

AVFoundation

Copyright © 2019- how234.cn 版权所有 赣ICP备2023008801号-2

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务