iOS开发:对MKMapView的性能优化

   最近做的项目主要是LBS这块 主打成员定位功能 我们的UI设计是这样的

开发:对MKMapView的性能优化-">
  乍一看上去是挺好挺美观的 不同的人会显示不同的头像 可是当人扎堆的时候 问题就来了


  当人多的时候(例如上图所示) 地图滑动起来就能感觉到明显顿卡 那种不流畅感能折磨死人 所以 自然我们要解决这个问题(等等 先不要吐槽为什么不用地图聚合 因为这已经是地图放到最大了 聚合不适合这次的问题讨论)

  分析

  首先看下我是怎么实现这个annotationView的 由于这个annotationsView是异形的(也就是无法通过设置圆角直接得到) 而且里面的图片还因用户而异 所以解决方案就是使用layer.mask来进行遮罩 代码如下

  @implementation MMAnnotationView

  - (instancetype)initWithAnnotation:(id)annotation reuseIdentifier:(NSString *)reuseIdentifier

  {

  self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];

  if ( self )

  {

  self.frame = CGRectMake(0, 0, TRACK_ANNOTATION_SIZE.width, TRACK_ANNOTATION_SIZE.height);

  self.centerOffset = CGPointMake(0, -(TRACK_ANNOTATION_SIZE.height-3)/2);

  self.canShowCallout = NO;

  self.avatarView = [[UIImageView alloc] initWithFrame:self.bounds];

  [self addSubview:self.avatarView];

  self.avatarView.contentMode = UIViewContentModeScaleAspectFill;

  CAShapeLayer *shapelayer = [CAShapeLayer layer];

  shapelayer.frame = self.bounds;

  shapelayer.path = self.framePath.CGPath;

  self.avatarView.layer.mask = shapelayer;

  self.layer.shadowPath = self.framePath.CGPath;

  self.layer.shadowRadius = 1.0f;

  self.layer.shadowColor = [UIColor colorWithHex:0x666666FF].CGColor;

  self.layer.shadowOpacity = 1.0f;

  self.layer.shadowOffset = CGSizeMake(0, 0);

  self.layer.masksToBounds = NO;

  }

  return self;

  }

  //mask路径

  - (UIBezierPath *)framePath

  {

  if ( !_framePath )

  {

  CGFloat arrowWidth = 14;

  CGMutablePathRef path = CGPathCreateMutable();

  CGRect rectangle = CGRectInset(CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetWidth(self.bounds)), 3,3);

  CGPoint p[3] = {

  {CGRectGetMidX(self.bounds)-arrowWidth/2, CGRectGetWidth(self.bounds)-6},

  {CGRectGetMidX(self.bounds)+arrowWidth/2, CGRectGetWidth(self.bounds)-6},

  {CGRectGetMidX(self.bounds), CGRectGetHeight(self.bounds)-4}

  };

  CGPathAddRoundedRect(path, NULL, rectangle, 5, 5);

  CGPathAddLines(path, NULL, p, 3);

  CGPathCloseSubpath(path);

  _framePath = [UIBezierPath bezierPathWithCGPath:path];

  CGPathRelease(path);

  }

  return _framePath;

  }

  我用代码生成了形状路径 并以此生成了layer的mask和shadowPath

  使用时 只要直接用SDWebImage设置头像就行了

  1

  [annotationView.avatarView sd_setImageWithURL:[NSURL URLWithString:avatarURL] placeholderImage:placeHolderImage];

  接下来用工具分析一下问题出来哪 分析性能当然是选择Instrments(用法在这里就不做介绍了) 打开Core Animation 然后运行程序 滑动地图 可以看到性能分析如下


  原来平均帧数只有不到30帧 这离我们的目标60帧差得实在太远

  再使用Debug Option来深入分析一下


  由于MKMapView的原因 这里我们主要关心这几个选项

  Color Blended Layers

  Color Misaligned Images

  Color Offscreen-Rendered Yellow

  分别打开这几个选项 结果如下


  可以看到

  Color Blended Layers没有问题 不过这也是正常的 由于使用了mask 没有透明的地方

  Color Misaligned Images除了默认头像外全中 这是因为服务器上的图片大小跟显示的大小不一致 导致缩放 而默认头像则是一致的 所以没问题

  Color Offscreen-Rendered Yellow全中 由于使用了mask 导致大量的离屏渲染 这也是性能下降的主要原因

  解决

  问题的原因找到了 那么接下来该如何解决呢?

  首先mask是肯定不能用了

  其次下载下来的图片我们要预处理成实际大小

  那么 直接把下载下来的图片合成为我们要显示的最终结果不就ok了吗? 试试看

  - (void)loadAnnotationImageWithURL:(NSString*)url imageView:(UIImageView*)imageView

  {

  //将合成后的图片缓存起来

  NSString *annoImageURL = url;

  NSString *annoImageCacheURL = [annoImageURL stringByAppendingString:@"cache"];

  UIImage *cacheImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:annoImageCacheURL];

  if ( cacheImage )

  {

  //LLLog(@"hit cache");

  imageView.image = cacheImage;

  }

  else

  {

  //LLLog(@"no cache");

  [imageView sd_setImageWithURL:[NSURL URLWithString:annoImageURL]

  placeholderImage:placeHolderImage

  completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {

  if (!error)

  {

  UIImage *annoImage = [image annotationImage];

  imageView.image = annoImage;

  [[SDImageCache sharedImageCache] storeImage:annoImage forKey:annoImageCacheURL];

  }

  }];

  }

  }

  @implementation UIImage (LJC)

  - (UIImage*) annotationImage

  {

  static UIView *snapshotView = nil;

  static UIImageView *imageView = nil;

  if ( !snapshotView )

  {

  snapshotView = [UIView new];

  snapshotView.frame = CGRectMake(0, 0, TRACK_ANNOTATION_SIZE.width, TRACK_ANNOTATION_SIZE.height);

  imageView = [UIImageView new];

  [snapshotView addSubview:imageView];

  imageView.clipsToBounds = YES;

  imageView.frame = snapshotView.bounds;

  imageView.contentMode = UIViewContentModeScaleAspectFill;

  CGFloat arrowWidth = 14;

  CGMutablePathRef path = CGPathCreateMutable();

  CGRect rectangle = CGRectInset(CGRectMake(0, 0, CGRectGetWidth(imageView.bounds), CGRectGetWidth(imageView.bounds)), 3,3);

  CGPoint p[3] = {

  {CGRectGetMidX(imageView.bounds)-arrowWidth/2, CGRectGetWidth(imageView.bounds)-6},

  {CGRectGetMidX(imageView.bounds)+arrowWidth/2, CGRectGetWidth(imageView.bounds)-6},

  {CGRectGetMidX(imageView.bounds), CGRectGetHeight(imageView.bounds)-4}

  };

  CGPathAddRoundedRect(path, NULL, rectangle, 5, 5);

  CGPathAddLines(path, NULL, p, 3);

  CGPathCloseSubpath(path);

  CAShapeLayer *shapelayer = [CAShapeLayer layer];

  shapelayer.frame = imageView.bounds;

  shapelayer.path = path;

  imageView.layer.mask = shapelayer;

  snapshotView.layer.shadowPath = path;

  snapshotView.layer.shadowRadius = 1.0f;

  snapshotView.layer.shadowColor = [UIColor colorWithHex:0x666666FF].CGColor;

  snapshotView.layer.shadowOpacity = 1.0f;

  snapshotView.layer.shadowOffset = CGSizeMake(0, 0);

  CGPathRelease(path);

  }

  imageView.image = self;

  UIGraphicsBeginImageContextWithOptions(TRACK_ANNOTATION_SIZE, NO, 0);

  [snapshotView.layer renderInContext:UIGraphicsGetCurrentContext()];

  UIImage *copied = UIGraphicsGetImageFromCurrentImageContext();

  UIGraphicsEndImageContext();

  return copied;

  }

  @end

  然后使用的时候 只要简单的如下调用就OK了

  [self loadAnnotationImageWithURL:avatarURL imageView:annotationView.avatarView];

  看看修改之后的Instruments表现如何


  Color Blended Layers全中 这也是无可避免的 因为显示的就是一张带透明度的图 但是由于地图的特殊性(头像的位置变化间隔较长 所以不会经常引发合成 也没有动画) 所以这里也不是问题

  Color Misaligned Images没问题了 因为头像已被缩放成了相同大小

  Color Offscreen-Rendered Yellow没问题了 因为只是简单的显示了一张图片 而并没有需要离屏渲染的东西了

  再来看下帧数情况


  Oh-Yeah~ 不光帧数达到了我们的目标60帧(由于还有业务逻辑线程在后台跑 所以没有那么的稳定) 就连平均运行耗时都下降了不少 就算地图上再多显示几十个人 也不成问题了

  小结

  不光是MKMapView 其实包括UITableView在内的很多地方都可以用文中所说的方法去优化 其核心点就是 合成+缓存 当然 由于合成还是会耗费一部分资源的 所以比较适合头像这种小的资源

  关于图形性能优化 可以看下这篇好文(有对文中提到的Debug Option不太明白的 这里有详细的解释)

时间: 2024-11-16 01:58:21

iOS开发:对MKMapView的性能优化的相关文章

iOS开发入门:关于性能优化–选择nib还是故事板的讨论

故事板是苹果在iOS5之后推出的技术,本意是集成多个nib文件于一个故事板文件,管理起来方便,故事板还能反应控制器之间的导航关系,很多导航是需要连连线就可以了,不需写代码,使用起来很方便.但是我告诫读者,从内存占用角度看故事板不是一个好的技术. 为了比较我们使用Xcode中的Master-Detail模板分别创建,基于故事板的应用StoryboardDemo和基于nib的应用 NibDemo.然后通过Instruments工具的Allocations模板分析ViewController视图控制器

iOS开发那些事-关于性能优化–选择nib还是故事板的讨论

故事板是苹果在iOS5之后推出的技术,本意是集成多个nib文件于一个故事板文件,管理起来方便,故事板还能反应控制器之间的导航关系,很多导航是需要连连线就可以了,不需写代码,使用起来很方便.但是我告诫读者,从内存占用角度看故事板不是一个好的技术. 为了比较我们使用Xcode中的Master-Detail模板分别创建,基于故事板的应用StoryboardDemo和基于nib的应用NibDemo.然后通过Instruments工具的Allocations模板分析ViewController视图控制器加

IOS详解TableView:性能优化及手工绘制UITableViewCell

提高表视图的性能 UITableView作为应用中最常用的视图,它的性能优化问题几乎是经常提及 .下面对在非网络访问情况下的表视图性能优化进行了主要的几点说明: 1.自定义类或XIB文件 时 在系统提供的样式不能满足我们的时候,我们经常会创建自定义类或者XIB文件来自定义单元 格样式. 在之前,我们通常通过loadNib的方式或者在代理方法中继续使用老的方法来设置重用 ,管理缓存池.在IOS6以后,我们可以通过注册的方式在注册单元格甚至表头视图,让系统来更高效的 进行管理. 2.Interfac

Go语言项目(kingshard)性能优化实例剖析

kingshard性能优化网络篇 最近kingshard的功能开发节奏慢了许多.一方面是工作确实比较忙,另一方面是我觉得kingshard的功能已经比较完善了,下一步的开发重点应该是性能优化.毕竟作为一个MySQL proxy,如果转发SQL的性能很差,再多的功能都无济于事.所以这个周末一直宅在家里优化kingshard的转发性能.经过两天的探索发现,将kingshard的转发SQL性能提升了18%左右,在这个过程中学到了一下知识.借此机会分享一下,同时也是督促一下自己写博客的积极性.:) 1.

iOS开发UI篇—UITableviewcell的性能优化和缓存机制

iOS开发UI篇-UITableviewcell的性能问题 一.UITableviewcell的一些介绍 UITableView的每一行都是一个UITableViewCell,通过dataSource的 tableView:cellForRowAtIndexPath:方法来初始化每⼀行 UITableViewCell内部有个默认的子视图:contentView,contentView是UITableViewCell所显示内容的父视图,可显示一些辅助指示视图 辅助指示视图的作⽤是显示一个表示动作的

iOS开发入门:性能优化–查找和解决僵尸对象

内存泄漏是当一个对象或变量在使用完成后没有释放掉,那么如果我们走了另外一个极端情况会什么样呢?这就导致过渡释放(over  release)问题,从而使对象"僵尸化",对象称为僵尸(zombies)对象.一个对象已经被释放过了,或者调用者没有这个对象的所有权而释放它, 都会造成过渡释放,产生僵尸对象. 僵尸对象或许对很多人听起来很恐怖.也很陌生,如果要说起EXEC_BAD_ACCESS异常,可能大家并不陌生.试图调用僵尸对象方法应用会崩溃(应用直接跳出),并抛出异常EXEC_BAD_A

ios应用相关的性能优化及参考文档

ios app programming guide:  https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/Introduction/Introduction.html http://www.cocoachina.com/newbie/tutorial/2013/0412/5980.html   中级 http://www.cocoachina.com/n

Android 游戏开发性能优化经验总结

http://blog.csdn.net/lz201234/article/details/45640505 优化概论 说起游戏的优化,在游戏开发中经常分为这几步: 首先要确定游戏中经常会出现哪些问题 – Profile 然后确定在哪些方向进行性能优化 – Analyze 最后再尽可能将问题逐个解决 – Solve 游戏开发中一定是先做工具,进行Profile,再进行优化,所以,说优化就不得不再扯一下Profile 常见的工具有一些是引擎和IDE自带的,比如Unity自带的Profiler,就包

Oracle数据库设计开发阶段性能优化策略

引言 Oracle是目前使用最为广泛的大型数据库管理系统,提高Oracle数据库系统的运行效率,是整个计算机信息系统高效运转的前提和保证.影响Oracle数据库应用系统性能的因素很多,既有软件方面的因素,也包括数据运行的硬件环境.网络环境.数据库管理和维护方面的因素等.数据库系统设计开发阶段是Oracle应用优化的最佳阶段,也是主动优化阶段,能达到以最小成本获得最大性能增益的目的.通过对其逻辑存储结构和物理存储结构设计进行优化,使之在满足需求条件下,时空开销性能最佳,可以解决数据库系统运行过程中