iOS奇思怪巧着重记忆

iOS开发UI篇-合理使用重用

2015-11-30  本文已影响1500人  河南蓝鸥科技有限公司

一、

一些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.

}

上一篇下一篇

猜你喜欢

热点阅读