iOS开发UI篇-合理使用重用
一、
一些objects的初始化很慢,比如NSDateFormatter和NSCalendar。然而,你又不可避免地需要使用它们,比如从JSON或者XML中解析数据。
想要避免使用这个对象的瓶颈你就需要重用他们,可以通过添加属性到你的class里或者创建静态变量来实现。
注意如果你要选择第二种方法,对象会在你的app运行时一直存在于内存中,和单例(singleton)很相似。
下面的代码说明了使用一个属性来延迟加载一个date formatter. 第一次调用时它会创建一个新的实例,以后的调用则将返回已经创建的实例:
还需要注意的是,其实设置一个NSDateFormatter的速度差不多是和创建新的一样慢的!所以如果你的app需要经常进行日期格式处理的话,你会从这个方法中得到不小的性能提升
- (NSDateFormatter *)formatter {
if (! _formatter) {
_formatter = [[NSDateFormatter alloc] init];
_formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
}
return _formatter;
}
二、
一个开发中常见的错误就是没有给UITableViewCells, UICollectionViewCells,甚至是UITableViewHeaderFooterViews设置正确的reuseIdentifier。
为了性能最优化,table view用 `tableView:cellForRowAtIndexPath:` 为rows分配cells的时候,它的数据应该重用自UITableViewCell。 一个table view维持一个队列的数据可重用的UITableViewCell对象。
不使用reuseIdentifier的话,每显示一行table view就不得不设置全新的cell。这对性能的影响可是相当大的,尤其会使app的滚动体验大打折扣。
自iOS6起,除了UICollectionView的cells和补充views,你也应该在header和footer views中使用reuseIdentifiers。
UITableViewCell有两种重用的方法
1.UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:indentifier forIndexPath:indexPath];
但是需要注册cell
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:indentifier];
2.UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:indentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:indentifier];
}
三、页眉,页脚的重用
拿UICollectionView举例
//注册页眉
[self.collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"header"];
//注册页脚
[self.collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"footer"];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:indentifier];
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
UICollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"header" forIndexPath:indexPath];
view.backgroundColor = [UIColor yellowColor];
return view;
} else {
UICollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"footer" forIndexPath:indexPath];
view.backgroundColor = [UIColor blueColor];
return view;
}
return nil;
}
四、重用Views
更多的view意味着更多的渲染,也就是更多的CPU和内存消耗,对于那种嵌套了很多view在UIScrollView里边的app更是如此。
这里我们用到的技巧就是模仿`UITableView`和`UICollectionView`的操作: 不要一次创建所有的subview,而是当需要时才创建,当它们完成了使命,把他们放进一个可重用的队列中。
这样的话你就只需要在滚动发生时创建你的views,避免了不划算的内存分配。
@interface ScrollViewController ()
@property (nonatomic, strong) UIScrollView *scrollViews;
@property (nonatomic, strong) NSMutableSet *set;
@property (nonatomic) CGFloat xIndex;
@end
@implementation ScrollViewController
-(NSMutableSet *)set {
if (!_set) {
self.set = [NSMutableSet setWithCapacity:1];
}
return _set;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.scrollViews = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 40, [UIScreen mainScreen].bounds.size.width, 300)];
self.scrollViews.contentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width * 16, 300);
self.scrollViews.contentOffset = CGPointMake([UIScreen mainScreen].bounds.size.width, 0);
self.scrollViews.pagingEnabled = YES;
self.scrollViews.delegate = self;
self.scrollViews.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.scrollViews];
[self createViews];
}
- (void)createViews {
NSArray *colorArr = @[[UIColor redColor], [UIColor blueColor], [UIColor greenColor]];
for (int i = 0; i < 3; i++) {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(i * [UIScreen mainScreen].bounds.size.width, 0, [UIScreen mainScreen].bounds.size.width, 300)];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 150, 50)];
label.backgroundColor = colorArr[i];
label.text = [NSString stringWithFormat:@"这是第%d个视图",i + 1];
[view addSubview:label];
NSLog(@"%@", view);
//三个view的tag值从左至右分别为100, 101, 102
view.tag = 100 + i;
// view.backgroundColor = colorArr[i];
[self.scrollViews addSubview:view];
}
}
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
self.xIndex = scrollView.contentOffset.x;
}
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (scrollView.contentOffset.x > self.xIndex) {
//将最左边的视图从俯视图上移除并放入重用集合里,并从重用集合里取view,放置到当前视图右边视图的位置
UIView *fView = [self.scrollViews viewWithTag:100];
[self.set addObject:fView];
[fView removeFromSuperview];
UIView *sView = [self.scrollViews viewWithTag:101];
sView.tag = 100;
UIView *tView = [self.scrollViews viewWithTag:102];
tView.tag = 101;
UIView *view = [self getViewsWithReuseIdentifier];
[view setFrame:CGRectMake(tView.frame.origin.x + [UIScreen mainScreen].bounds.size.width, 0, [UIScreen mainScreen].bounds.size.width, 300)];
view.tag = 102;
[self.scrollViews addSubview:view];
NSLog(@"%@", view);
}else if(scrollView.contentOffset.x < self.xIndex){
//将最右边的视图从俯视图上移除并放入重用集合里,并从重用集合里取view,放置到当前视图左边视图的位置
UIView *tView = [self.scrollViews viewWithTag:102];
[self.set addObject:tView];
[tView removeFromSuperview];
UIView *sView = [self.scrollViews viewWithTag:101];
sView.tag = 102;
UIView *fView = [self.scrollViews viewWithTag:100];
fView.tag = 101;
UIView *view = [self getViewsWithReuseIdentifier];
[view setFrame:CGRectMake(fView.frame.origin.x - [UIScreen mainScreen].bounds.size.width, 0, [UIScreen mainScreen].bounds.size.width, 300)];
view.tag = 100;
[self.scrollViews addSubview:view];
NSLog(@"%@", fView);
}
}
- (UIView *)getViewsWithReuseIdentifier {
if ([self.set anyObject] == nil) {
return [[UIView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 300)];
}else {
UIView *returnView = [self.set anyObject];
[self.set removeObject:returnView];
return returnView;
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}