OC 和 Swift 问题 (随时补充)

问: 关键字static的作用是什么?

    1. 函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
    1. 在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
    1. 在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;

问: 关键字const是什么含义? 分别解释下列语句中const的作用?

const int a;
int const a;
const int *a;
int * const a;
int const * a const;

含义

    1. 欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
    1. 对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
    1. 在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
    1. 对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;

作用

const int a;            //a是一个常整型数
int const a;            //a是一个常整型数
const int *a;           //a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)
int * const a;          //a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)
int const * a const;    //a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)

问: 使用nonatomic一定是线程安全的吗?

    1. nonatomic: 非原子性,set方法的实现不加锁,不安全,性能高
    1. atomic:atomic性能低,atomic通过锁定机制来确保其原子性,但只是读/写安全,不能绝对保证线程的安全,当多线程同时访问的时候,会造成线程不安全。可以使用线程锁来保证线程的安全。

问: 对于语句NSString *obj = [[NSData alloc] init]; ,编译时和运行时obj分别是什么类型?

    1. 编译时是NSString类型
    1. 运行时是NSData类型

问: Objective-C如何对内存管理的,说说你的看法和解决方法?

    1. 每个对象都有一个引用计数器,每个新对象的计数器是1,当对象的计数器减为0时,就会被销毁
    1. 通过retain可以让对象的计数器+1、release可以让对象的计数器-1
    1. 还可以通过autorelease pool管理内存
    1. 如果用ARC,编译器会自动生成管理内存的代码

注意:不管是MRC还是ARC都是在编译时完成的


问: iOS数据持久化有哪些?

为何要持久化:iOS 开发可以没有持久化,持久化更多的是业务需求;比如记录用户是否登陆,下次进应用不需要再登陆。
因为 iOS 的 沙盒机制,所以持久化分为两类:沙盒内 和 沙盒外。

    1. 沙盒内
      (1) NSKeyedArchiver: 只要遵循了 NSCoding 协议并正确实现了initWithCoder 和 encodeWithCoder 方法的类都可以通过NSKeyedArchiver 来序列化。
      归档使用 archiveRootObject ,解归档使用 unarchiveObjectWithFile ;需要指定文件路径。
      (2) NSUserDefaults: [NSUserDefaults standardUserDefaults]获取NSUserDefaults对象,以key-value方式进行持久化操作。
      (3) plist: 写入使用writeToFile,读取使用xxxWithContentsOfFile;需要指定文件路径。
      (4) 数据库: sqlite、CoreData和Realm等
      (5) 文件: 这里要和plist区分一下,plist方式是字典/数组数据格式写入文件;而这里的文件方式不限数据格式。
    1. 沙盒外
      沙盒内的方式在应用被删除后数据都会丢失,如果想要不丢失则需要使用KeyChain。
      KeyChain本质是一个sqlite数据库,其保存的所有数据都是加密过的。
      KeyChain分为私有和公有,公有则需要指定group,一个group中的应用可以共享此KeyChain。
      使用KeyChain过程中要理解下面几个问题:
      ①:自己使用的KeyChain和系统自带的KeyChain数据是隔离的,内部应该是不同数据库文件;
      ②:KeyChain数据可备份到iCloud中;
      ③:不需要联网,也不用登陆iCloud账号;一个设备一个sqlite数据库,但是不同应用组不共享数据;
      ④:要在另一台设备上使用当前设备存储的KeyChain信息,需要当前设备进行数据备份,再在另一设备上复原数据;比较常用的是iCloud备份方式;
      ⑤:系统自带的KeyChain中账号密码分类数据可在系统设置->账号与密码里面看到,你退出iCloud账号还是存在,只是iCloud会帮你备份如果你设置了的话;这个和照片是一样的道理。

问: id和NSObject*的区别?

  • id可以指向OC中的任何对象,而NSObject*只能指向NSObject及子类对象

问: strong 和 weak 的区别?


问: (堆和栈) 哪些数据是放在堆上的,哪些是放在栈上的?

  • 栈:由系统自动分配,速度较快,不会产生内存碎片,
  • 堆:是由alloc分配的内存,速度比较慢,而且容易产生内存碎片,不过用起来最方便。

问: UITableView 优化?

    1. cell复用
      我们经常在注意 cellForRowAtIndexPath: 中为每一个 cell 绑定数据,实际上在调用 cellForRowAtIndexPath: 的时候 cell 还没有被显示出来,为了提高效率我们应该把数据绑定的操作放在 cell 显示出来后再执行,可以在tableView:willDisplayCell:forRowAtIndexPath:(以后简称willDisplayCell)方法中绑定数据。
      注意 willDisplayCell 在 cell 在 tableview 展示之前就会调用,此时 cell 实例已经生成,所以不能更改 cell 的结构,只能是改动 cell 上的 UI 的一些属性(例如 label 的内容等)。
    1. cell高度的计算
      (1)定高的 cell,应该采用如下方式:self.tableView.rowHeight = 88;
      (2)动态高度的cell:tableView: tableViewheightForRowAtIndexPath:,该方法实现后,上面的 rowHeight 的设置将会变成无效。在这个方法中,我们需要提高 cell 高度的计算效率,来节省时间。
      自从 iOS8 之后有了 self-sizing cell 的概念,cell 可以自己算出高度,使用 self-sizing cell 需要满足以下三个条件:
      ① 使用 Autolayout 进行 UI 布局约束(要求 cell.contentView 的四条边都与内部元素有约束关系)。
      ② 指定 TableView 的 estimatedRowHeight 属性的默认值。
      ③ 指定 TableView 的 rowHeight 属性为 UITableViewAutomaticDimension。
    1. 渲染
      (1)当有图像时,预渲染图像,在 bitmap context 先将其画一遍,导出成 UIImage 对象,然后再绘制到屏幕,这会大大提高渲染速度。具体内容可以自行查找“利用预渲染加速显示 iOS 图像”相关资料。
      (2)渲染最好时的操作之一就是混合( blending )了,所以我们不要使用透明背景,将 cell 的 opaque 值设为 Yes,背景色不要使用 clearColor,尽量不要使用阴影渐变等。
      (3)由于混合操作是使用 GPU 来执行,我们可以用 CPU 来渲染,这样混合操作就不再执行。可以在 UIView 的 drawRect 方法中自定义绘制。
    1. 减少视图的数目
      我们在 cell 上添加系统控件的时候,实际上系统都会调用底层的接口进行绘制,大量添加控件时,会消耗很大的资源并且也会影响渲染的性能。当使用默认的 UITableViewCell 并且在它的 ContentView 上面添加控件时会相当消耗性能。所以目前最佳的方法还是继承 UITableViewCell ,并重写 drawRect 方法。
    1. 减少多余的绘制操作
      在实现 drawRect 方法的时候,它的参数 rect 就是我们需要绘制的区域,在 rect 范围之外的区域我们不需要进行绘制,否则会消耗相当大的资源。
    1. 不要给cell动态添加subView
      在初始化 cell 的时候就将所有需要展示的添加完毕,然后根据需要来设置 hide 属性显示和隐藏。
    1. 异步化UI,不要阻塞主线程
      我们时常会看到这样一个现象,就是加载时整个页面卡住不动,怎么点都没用,仿佛死机了一般。原因是主线程被阻塞了。所以对于网路数据的请求或者图片的加载,我们可以开启多线程,将耗时操作放到子线程中进行,异步化操作。这个或许每个 iOS 开发者都知道的知识,不必多讲。
    1. 滑动时按需加载对应的内容
      如果目标行与当前行相差超过指定行数,只在目标滚动范围的前后指定3行加载。
      滑动很快时,只加载目标范围内的cell,这样按需加载(配合SDWebImage),极大提高流畅度。

问: 消息列表页面如何优化?

首先我们发消息时候观察一下消息列表的特性,当发送一条消息时候,消息的数量会变化,列表会出现在最上边的位置,列表内的内容会发生变化。从消息列表的特性,我们就可以分析出要优化的点了。通过这些点,我们做了一些优化:

    1. 如果列表消息从没显示过需要刷新列表,创建好一个cell后,将cell插入到第一位上,cell插入的性能要高于刷新tableview的性能。
    1. 如果消息已经显示过了,但是并不是第一位,则需要刷新列表。
    1. 如果消息已经显示,并且是第一位,则只需要cell的内容变化。
    1. 只修改cell里的内容,不进行刷新cell整体,这里要注意的是,一定要最小化刷新。刷新点越小,性能损耗越小。我们项目架构是MVVM,采用了ReactiveCocoa框架,针对每个cell上的可变化的控件数据进行了监听,每一个cell上对应一个vm,这样当vm上的数据变化时候,cell上的数据也就跟着变了。做到了最小化刷新。
    1. 避免使用autolayout计算位置,这个很重要,在性能要求高的情况下,autolayout计算会很耗时间,尤其在算tableview高度的时候可见一斑。可喜的是消息列表的高度是固定的,所以在计算高度时候我们并未花费时间。
    1. 使用(__kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath而不使用-(nullable __kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier来查找cell,因为下边的方法会多查一次,耗时更长一点。
    1. cellForRowAtIndexPath方法只负责创建cell,willDisplayCell方法才给cell进行赋值操作。从方法名字就可以看出来原因
    1. 当来消息轰炸时候,必然会是不同的人发来的消息,会导致tableview不可避免的刷新,如果不加处理必然会卡顿,要知道,机器也是有瓶颈的。这里我们做的优化是根据cpu的使用率选择性的刷新tableview。后来我们发现微信也是有这个现象,并不是实时的刷新,我们猜测也是类似处理。
    1. 重绘制系统控件,相信你也发现了消息列表里,主要有两个控件,一个是头像,一个是label。而系统的UIImageView用来显示头像,未免有点重。我们的处理是使用UIView,设置View得layer.content来处理。针对layer层做的setImageWithUrl的第三方库也不少,大家可以自行查询。另一个就是label,如果你能集成UIView自己绘制一个label,我想也许会有一点效果。

问: 聊天界面如何优化?

聊天界面的优化算是比较繁琐的了,但是优化点跟回话列表的优化差不多。上边提到回话列表里最耗时的tableview的高度是固定的,而聊天界面的几乎每条消息的高度都可能不一样,所以我们在优化聊天界面时候最重要的一点就是计算tableviewcell的高度。而我们在计算tableview的高度是怎么做的呢?

主要有两个准则:
(1) 第一个是能在后台线程执行的都放在后台线程里。
(2) 第二个计算高度要放在显示之前。

    1. 巧妙的选择控件。(比如上个问题提到的用UIView的layer.content来代替UIImageView, 图片加点击也可以用view,然后监听view的touch事件,像button这种重量级的控件在性能为主的app面前,我对他们都是弃之如敝履。)
    1. 减少使用layer层的cornerRadius,mask等圆角的绘制,这会引发离屏渲染,增加cpu的占用率。如果业务需要的话,我们可以通过UIBezierPath来drawInRect它。
    1. 避免设置透明
    1. 避免autolayout设定控件位置
    1. 尽可能的减少视图的层级,如果你能把所有的控件都绘制到一个View上,可想而知性能会爆棚。

问: Swift的可选类型?


问: Swift 中 Struct 和 Class的区别 ?

    1. property初始化的不同:
      主要的差別就是 class 在初始化时不能直接把 property 放在 默认的constructor 的参数里,而是需要自己创建一个带参数的 constructor
    1. 变量赋值方式不同(深浅copy)
      struct 赋值“=”的时候,会copy一份完整相同的內容给另一個变量 -> 【开辟了新的内存地址】 (深拷贝)
      class 赋值“=”的时候,不会copy一份完整的内容给另一個变量,只是增加了原变量内存地址的引用而已 -> 【没有开辟了新的内存地址】 (浅拷贝)
    1. immutable 变量
      Swift 语言的特色之一就是可变动内容和不可变内容用 var 和 let 來甄别,如果初始为let的变量再去修改会发生编译错误。
      struct也遵循这一特性
      class不存在这样的问题
    1. struct 和 class 的差別是 struct 的 function 要去改变 property 的值的时候要加上 mutating,而 class 不用。
    1. struct不能继承,class可以继承。
    1. struct分配在栈中,class分配在堆中。

Swift 用 Struct 作为数据模型时需要注意什么问题?

优点:
    1. 安全性:因为 Struct 是用值类型传递的,它们没有引用计数。
    1. 内存:由于他们没有引用数,他们不会因为循环引用导致内存泄漏。
    1. 速度:值类型通常来说是以栈的形式分配的,而不是用堆。因此他们比 Class 要快很多!
    1. 拷贝:Objective-C 里拷贝一个对象,你必须选用正确的拷贝类型(深拷贝、浅拷贝),而值类型的拷贝则非常轻松!
    1. 线程安全:值类型是自动线程安全的。无论你从哪个线程去访问你的 Struct ,都非常简单。
缺点: (需要注意的地方)
    1. Objective-C:当你的项目的代码是 Swift 和 Objective-C 混合开发时,你会发现在 Objective-C 的代码里无法调用 Swift 的 Struct。因为要在 Objective-C 里调用 Swift 代码的话,对象需要继承于 NSObject。
      Struct 不是 Objective-C 的好朋友。
    1. 继承:Struct 不能相互继承。
    1. NSUserDefaults:Struct 不能被序列化成 NSData 对象。
总结:如果模型较小,并且无需继承、无需储存到 NSUserDefault 或者无需 Objective-C 使用时,建议使用 Struct。

最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,851评论 1赞 32
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 16,791评论 4赞 61
  • 近年来,中国美妆市场取得了高速增长,私护产业就在当中占了很大的份额,对于20岁到50岁的女性来说,私护产品的存在就...
    多情怨阅读 218评论 0赞 0
  • 计算一个二进制数中的1的个数 1.循环法 首先二进制数有这么一个性质,当一个数减去1再与原来的数字去&运算,得到的...
    senninha阅读 895评论 0赞 0
  • 在这次运动会中,无论是成功者,还是失败者,都体会到了什么是悲喜交加,喜怒哀乐,生活就是这样,有苦有甜。只要你认真,...
    卫校一五助产阅读 554评论 0赞 0

友情链接更多精彩内容