从两道面试题说起

栏目: IOS · 发布时间: 5年前

内容简介:野指针指向一个已删除的对象或未申请访问受限内存区域的指针。特别要指出的是野指针不是空指针。一提到上面的这段代码就会造成循环引用。那我们怎么破除呢?通常的做法都是使用

野指针指向一个已删除的对象或未申请访问受限内存区域的指针。特别要指出的是野指针不是空指针。

Block

一提到 Block 大家肯定都知道要说的是循环引用。在 ARC 中,如果两个对象相互持有对方,就会造成循环引用,导致内存无法释放。在 Block 中,最常用的场景则是, self 持有 blockblock 中又持有了 self 。例如下方一段代码:

@property (nonatmaic, copy) Block dataChanged;

- (void)setUpModel{
  XYModel *model = [XYModel new];
  model.dataChanged = ^(NSString *title) {
      self.titleLabel.text = title;                
  };
  self.model = model;
}
复制代码

上面的这段代码就会造成循环引用。那我们怎么破除呢?通常的做法都是使用 weakSelf 来处理,即:

- (void)setUpModel {
  XYModel *model = [XYModel new];
  __weak typeof(self) weakSelf = self;
  model.dataChanged = ^(NSString *title) {
      weakSelf.titleLabel.text = title;   
  };
  self.model = model;
}
复制代码

或许你还看到另外一种不是很一样的版本:

- (void)setUpModel {
  XYModel *model = [XYModel new];
  __weak typeof(self) weakSelf = self;
  model.dataChanged = ^(NSString *title) {
      __strong typeof(self) strongSelf = weakSelf;
      strongSelf.titleLabel.text = title;   
  };
  self.model = model;
}
复制代码

对比一下,多了一个 strongSelf 。那为什么又要多加一个 strongSelf 呢?

考虑一下下面的代码,

__weak __typeof__(self) weakSelf = self;
dispatch_group_async(_operationsGroup, _operationsQueue, ^{
  [weakSelf doSomething];
  [weakSelf doSomethingElse];
});
复制代码

doSomething 时, weakSelf 不会被释放,但是在 doSomethingElse 时, weakSelf 有可能被释放。

这个时候就遇到了野指针问题,回答了一开始的题目。

在这里就需要用到 strongSelf ,使用 __strong 确保在 Block 内, strongSelf 不会被释放。

小结

  • 在使用 Block 时,如遇到循环引用问题,可以使用 __weak 来破除循环引用。

  • 如果在 Block 内需要多次访问 __weak 变量,则需要使用 __strong 来保持变量不会被释放。

SDWebImage 中为什么要解码图片

要说明这么问题我们需要先了解一下在 iOS 中,图片显示的流程。

概括来说,从磁盘中加载一张图片,并将它显示到屏幕上,中间的主要工作流如下:

假设我们使用 imageWithContentsOfFile: 方法从磁盘中加载一张图片,这个时候的图片并没有解压缩;

然后将生成的 UIImage 赋值给 UIImageView

接着一个隐式的 CATransaction 捕获到了 UIImageView 图层树的变化;

在主线程的下一个 run loop 到来时, Core Animation 提交了这个隐式的 transaction ,这个过程可能会对图片进行 copy 操作,而受图片是否字节对齐等因素的影响,这个 copy 操作可能会涉及以下部分或全部步骤:

  • 分配内存缓冲区用于管理文件 IO 和解压缩操作;
  • 将文件数据从磁盘读到内存中;
  • 将压缩的图片数据解码成未压缩的位图形式,这是一个非常耗时的 CPU 操作;
  • 最后 Core Animation 使用未压缩的位图数据渲染 UIImageView 的图层。

在上面的步骤中,我们提到了图片的解压缩是一个非常耗时的 CPU 操作,并且它默认是在主线程中执行的。那么当需要加载的图片比较多时,就会对我们应用的响应性造成严重的影响,尤其是在快速滑动的列表上,这个问题会表现得更加突出。

这里顺便提一下 imageNamed:imageWithContentsOfFile: 的区别,这两个 API 都需要解码,并且工作流程都是一致的。不过 imageNamed: 会做缓存处理,在下一次用到相同的资源时,就会从缓存里面读取。而 imageWithContentsOfFile: 则不会。所以网上大多文章都会告诉你,多次使用的小图片使用 imageNamed: 加载,一次性使用的大图片使用 imageWithContentsOfFile: 加载。

对于上面引用的流程中最后提到,当有大量图片滑动时就会造成主线程的卡顿,原因就是解码图片在主线程中操作的。那有什么办法避免呢? 我在查询关于这个问题的相关资料时,发现有些博客给出了2种方案:

  1. 我们不使用 imageNamed: 加载图片,使用其他的方法,比如 imageWithContentsOfFile:

  2. 我们自己解码图片,可以把这个解码过程放到子线程

其实第一种方式没法避免卡顿。这就引出了为什么 SDWebImage 中需要自己解码图片。

在我们使用 UIImage 的时候,创建的图片通常不会直接加载到内存,而是在渲染的时候再进行解压并加载到内存。这就会导致 UIImage 在渲染的时候效率上不是那么高效。为了提高效率通过 decodedImageWithImage 方法把图片提前解压加载到内存,这样这张新图片就不再需要重复解压了,提高了渲染效率。这是一种空间换时间的做法。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

程序员修炼之道

程序员修炼之道

Andrew Hunt、David Thomas / 马维达 / 电子工业出版社 / 2011-1 / 55.00元

《程序员修炼之道:从小工到专家》内容简介:《程序员修炼之道》由一系列独立的部分组成,涵盖的主题从个人责任、职业发展,知道用于使代码保持灵活、并且易于改编和复用的各种架构技术,利用许多富有娱乐性的奇闻轶事、有思想性的例子及有趣的类比,全面阐释了软件开发的许多不同方面的最佳实践和重大陷阱。无论你是初学者,是有经验的程序员,还是软件项目经理,《程序员修炼之道:从小工到专家》都适合你阅读。一起来看看 《程序员修炼之道》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码