iOS
中是否存在野指针的状况?野指针指向一个已删除的对象或未申请访问受限内存区域的指针。特别要指出的是野指针不是空指针。ios
Block
一提到 Block
你们确定都知道要说的是循环引用。在 ARC
中,若是两个对象相互持有对方,就会形成循环引用,致使内存没法释放。在 Block
中,最经常使用的场景则是,self
持有 block
, block
中又持有了 self
。例以下方一段代码:缓存
@property (nonatmaic, copy) Block dataChanged;
- (void)setUpModel{
XYModel *model = [XYModel new];
model.dataChanged = ^(NSString *title) {
self.titleLabel.text = title;
};
self.model = model;
}
复制代码
上面的这段代码就会形成循环引用。那咱们怎么破除呢?一般的作法都是使用 weakSelf
来处理,即:bash
- (void)setUpModel {
XYModel *model = [XYModel new];
__weak typeof(self) weakSelf = self;
model.dataChanged = ^(NSString *title) {
weakSelf.titleLabel.text = title;
};
self.model = model;
}
复制代码
或许你还看到另一种不是很同样的版本:less
- (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
呢?async
考虑一下下面的代码,oop
__weak __typeof__(self) weakSelf = self;
dispatch_group_async(_operationsGroup, _operationsQueue, ^{
[weakSelf doSomething];
[weakSelf doSomethingElse];
});
复制代码
在 doSomething
时, weakSelf
不会被释放,可是在 doSomethingElse
时,weakSelf
有可能被释放。ui
这个时候就遇到了野指针问题,回答了一开始的题目。spa
在这里就须要用到 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种方案:
咱们不使用
imageNamed:
加载图片,使用其余的方法,好比imageWithContentsOfFile:
咱们本身解码图片,能够把这个解码过程放到子线程
其实第一种方式无法避免卡顿。这就引出了为何 SDWebImage
中须要本身解码图片。
在咱们使用
UIImage
的时候,建立的图片一般不会直接加载到内存,而是在渲染的时候再进行解压并加载到内存。这就会致使UIImage
在渲染的时候效率上不是那么高效。为了提升效率经过decodedImageWithImage
方法把图片提早解压加载到内存,这样这张新图片就再也不须要重复解压了,提升了渲染效率。这是一种空间换时间的作法。
你也能够关注个人公众号,获取更多文章。