内容简介:由于cell复用的原因,直接在在方法2、
reloadData
是一个异步方法,并不会等待 UITableView
或者 UICollectionView
(后面统称 listView
)真正刷新完毕后才执行后续代码,而是立即执行后续代码。我们执行 reloadData
的本意是刷新 listView
,随后会进入一系列的DataSource和Delegate回调,有些是和reloadData同步发生的,有些是异步发生的。
-
同步:
numberOfSectionsInCollectionView和numberOfItemsInSection -
异步:
cellForItemAtIndexPath -
同步+异步:
sizeForItemAtIndexPath
问题:
由于cell复用的原因,直接在 reloadData
后执行代码是有可能出问题的。比如在 reloadData
前保留了一个cell,在 reloadData
后,对这个cell(已经不是原来的cell了)进行某些操作,会出现一些异常问题。
解决办法:
在 reloadData
前不是保留cell,二是保留当前cell对应的 NSIndexPath
,然后在 reloadData
完毕( listView
真正刷新完毕)后通过方法 cellForItemAtIndexPath:
重新获取cell,然后进行相应的操作。
获取listView真正刷新完毕的时机的几种方法
方法1、通过layoutIfNeeded方法,强制重绘并等待完成。
[self.collectionView reloadData];
[self.collectionView layoutIfNeeded];
// 刷新完成,执行后续需要执行的代码
if ( self.didPlayIdx ) {
MyCell* cell = (MyCell*)[self.collectionView cellForItemAtIndexPath:self.didPlayIdx];
if (cell) {
[cell playWithPlayer:self.player];
}
}
复制代码
方法2、 reloadData
方法会在主线程执行,通过GCD,使后续操作排队在 reloadData
后面执行。一次runloop有两个机会执行GCD dispatch main queue中的任务,分别在休眠前和被唤醒后。设置 listView
的 layoutIfNeeded
为YES,在即将进入休眠时执行异步任务,重绘一次界面。
[self.collectionView reloadData];
dispatch_async(dispatch_get_main_queue(), ^{
// 刷新完成,执行后续代码
if ( self.didPlayIdx ) {
MyCell* cell = (MyCell*)[self.collectionView cellForItemAtIndexPath:self.didPlayIdx];
if (cell) {
[cell playWithPlayer:self.player];
}
}
});
复制代码
知识点关联:GCD死锁、Runloop
// 发生死锁,永远不会执行任务2和3
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
复制代码
方法3、自定义UICollectionView、UITableView,layoutSubviews之后当作reloadData完成(复杂,但可以更好的理解方法一)
#import "MyTableView.h"
@interface MyTableView()
@property (nonatomic, copy) void (^reloadDataCompletionBlock)();
@end
@implementation MyTableView
- (void)reloadDataWithCompletion:(void (^)())completionBlock {
self.reloadDataCompletionBlock = completionBlock;
[super reloadData];
}
- (void)layoutSubviews {
[super layoutSubviews];
if (self.reloadDataCompletionBlock) {
self.reloadDataCompletionBlock();
self.reloadDataCompletionBlock = nil;
}
}
@end
// 调用的时候
[self.tableView reloadDataWithCompletion:^{
NSLog(@"完成刷新");
}];
复制代码
引申:更新UI放在主线程的原因
原因一:安全+效率
因为UIKit框架不是线程安全的,当多个线程同时操作UI的时候,抢夺资源,导致崩溃,UI异常等问题。假如在两个线程中设置了同一张背景图片,很有可能就会由于背景图片被释放两次,使得程序崩溃。或者某一个线程中遍历找寻某个subView,然而在另一个线程中删除了该subView,那么就会造成错乱。apple有对大部分的绘图方法和诸如UIColor等类改写成线程安全可用,可还是建议将UI操作保证在主线程中。例如说,我们需要在子线程中读取一个image对象,使用接口 [UIImage imageNamed:]
,但 imageNamed:
实际上在 iOS9
以后才是线程安全的, iOS9
之前都需要在主线程获取。所以,我们需要从子线程切换到主线程获取image,然后再切回子线程拿到这个image,这里我们必须使用sync。
__block UIImage *image;
dispatch_sync_on_main_queue(^{
image = [UIImage imageNamed:@"Resource/img"];
});
attachment.image = image;
// YYKit中提供了一个同步扔任务到主线程的安全方法:
/**
Submits a block for execution on a main queue and waits until the block completes.
*/
static inline void dispatch_sync_on_main_queue(void (^block)()) {
if (pthread_main_np()) {
block();
} else {
dispatch_sync(dispatch_get_main_queue(), block);
}
}
复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
生态战略:设计未来企业新模式
周文艺 / 机械工业出版社 / 2017-3 / 49.00
思想影响战略,战略决定组织。在充满高度不确定性的今天,企业要生存和发展,必须不断进行组织变革与进化,跨越不连续性的鸿沟。本书分析了大量互联网生态型企业的案例,从生态思维进化、生态战略构建和生态组织变革三个角度出发,全面阐述了企业的进化之路。 本书认为,生态是企业进化的核心思想,企业须重新定义增长模式,从封闭的企业链转向开放的价值网,不断创新文化、技术和连接,培育新物种,实现企业从技术生态位到......一起来看看 《生态战略:设计未来企业新模式》 这本书的介绍吧!