在 iOS 中可以有很多方法做出漂亮的按钮。你可以用整幅的图片,可调大小的图片,uozhe 可以用 CALayer, CoreGraphics 甚至 OpenGL 来画它们。
当然每个不同的解决方法都有不同的复杂程度和相应的性能。有一篇 Apple UIKit team 中的一员 Andy Matuschak 推荐过的很棒的关于 graphic 性能的帖子很值得一读。
一旦系统内存过低,iOS 会通知所有运行中 app。在官方文档中是这样记述:
如果你的 app 收到了内存警告,它就需要尽可能释放更多的内存。最佳方式是移除对缓存,图片 object 和其他一些可以重创建的 objects 的 strong references.
幸运的是,UIKit 提供了几种收集低内存警告的方法:
的方法
- applicationDidReceiveMemoryWarning:
- didReceiveMemoryWarning
一旦收到这类通知,你就需要释放任何不必要的内存使用。
例如,UIViewController 的默认行为是移除一些不可见的 view, 它的一些子类则可以补充这个方法,删掉一些额外的数据结构。一个有图片缓存的 app 可以移除不在屏幕上显示的图片。
这样对内存警报的处理是很必要的,若不重视,你的 app 就可能被系统杀掉。
然而,当你一定要确认你所选择的 object 是可以被重现创建的来释放内存。一定要在开发中用模拟器中的内存提醒模拟去测试一下。
一些 objects 的初始化很慢,比如 NSDateFormatter 和 NSCalendar。然而,你又不可避免地需要使用它们,比如从 JSON 或者 XML 中解析数据。
想要避免使用这个对象的瓶颈你就需要重用他们,可以通过添加属性到你的 class 里或者创建静态变量来实现。
注意如果你要选择第二种方法,对象会在你的 app 运行时一直存在于内存中,和单例 (singleton) 很相似。
下面的代码说明了使用一个属性来延迟加载一个 date formatter. 第一次调用时它会创建一个新的实例,以后的调用则将返回已经创建的实例:
- // in your .h or inside a class extension
- @property (nonatomic, strong) NSDateFormatter *formatter;
- // inside the implementation (.m)
- // When you need, just use self.formatter
- - (NSDateFormatter *)formatter {
- if(! _formatter) {
- _formatter = [[NSDateFormatter alloc] init];
- _formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy";// twitter date format
- }
- return_formatter;
- }
还需要注意的是,其实设置一个 NSDateFormatter 的速度差不多是和创建新的一样慢的!所以如果你的 app 需要经常进行日期格式处理的话,你会从这个方法中得到不小的性能提升。
你是一个游戏开发者吗,那么 Sprite sheets 一定是一个你的最好的朋友了。Sprite sheet 可以让渲染速度加快,甚至比标准的屏幕渲染方法节省内存。
我们有两个很好的关于 Sprite 的教程:
第二个教程涵盖了可能在很大程度上影响你游戏性能的 pixel 格式的细节。
如果你对于 spirte
sheet 还不是很熟悉,可以看下这两个 (youtube) 视频 SpriteSheets – The Movie, Part 1 和 Part
2。视频的作者是创建 Sprite sheet 很流行的工具之一 Texture Packer 的作者 Andreas L?w。
除了使用 Sprite sheets,其它写在这里的建议当然也可以用于游戏开发中。比如你需要很多的 Sprite sheets,像敌人,导弹之类的动作类必备元素,你可以重用这些 sprites 而不用每次都要重新创建。
许多应用需要从服务器加载功能所需的常为 JSON 或者 XML 格式的数据。在服务器端和客户端使用相同的数据结构很重要。在内存中操作数据使它们满足你的数据结构是开销很大的。
比如你需要数据来展示一个 table view, 最好直接从服务器取 array 结构的数据以避免额外的中间数据结构改变。
类似的,如果需要从特定 key 中取数据,那么就使用键值对的 dictionary。
从 app 和网络服务间传输数据有很多方案,最常见的就是 JSON 和 XML。你需要选择对你的 app 来说最合适的一个。
在 View 里放背景图片就像很多其它 iOS 编程一样有很多方法:
如果你使用全画幅的背景图,你就必须使用 UIImageView 因为 UIColor 的 colorWithPatternImage 是用来创建小的重复的图片作为背景的。这种情形下使用 UIImageView 可以节约不少的内存:
- // You could also achieve the same result in Interface Builder
- UIImageView *backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background"]];
- [self.view addSubview:backgroundView];
如果你用小图平铺来创建背景,你就需要用 UIColor 的 colorWithPatternImage 来做了,它会更快地渲染也不会花费很多内存:
- self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background"]];
UIWebView 很有用,用它来展示网页内容或者创建 UIKit 很难做到的动画效果是很简单的一件事。
但是你可能有注意到 UIWebView 并不像驱动 Safari 的那么快。这是由于以 JIT compilation 为特色的 Webkit 的 Nitro Engine 的限制。
所以想要更高的性能你就要调整下你的 html 了。第一件要做的事就是尽可能移除不必要的 javascript,避免使用过大的框架。能只用原生 js 就更好了。
另外,尽可能异步加载例如用户行为统计 script 这种不影响页面表达的 javascript。
最后,永远要注意你使用的图片,保证图片的符合你使用的大小。使用 Sprite sheet 提高加载速度和节约内存。
更多相关信息可以看下 WWDC 2012 session #601 – Optimizing Web Content in UIWebViews and Websites on iOS
如何在一个 View 或者一个 layer 上加一个 shadow 呢,QuartzCore 框架是很多开发者的选择:
- #import
- // Somewhere later ...
- UIView *view = [[UIView alloc] init];
- // Setup the shadow ...
- view.layer.shadowOffset = CGSizeMake(-1.0f, 1.0f);
- view.layer.shadowRadius = 5.0f;
- view.layer.shadowOpacity = 0.6;
看起来很简单,对吧。
可是,坏消息是使用这个方法也有它的问题… Core Animation 不得不先在后台得出你的图形并加好阴影然后才渲染,这开销是很大的。
使用 shadowPath 的话就避免了这个问题:
view.layer.shadowPath = [[UIBezierPath bezierPathWithRect:view.bounds] CGPath];
使用 shadow path 的话 iOS 就不必每次都计算如何渲染,它使用一个预先计算好的路径。但问题是自己计算 path 的话可能在某些 View 中比较困难,且每当 view 的 frame 变化的时候你都需要去 update shadow path.
想了解更多可以看看 Mark Pospesel 的这篇。
Table view 需要有很好的滚动性能,不然用户会在滚动过程中发现动画的瑕疵。
为了保证 table view 平滑滚动,确保你采取了以下的措施:
来重用 cells
- reuseIdentifier
来画阴影
- shadowPath
,如果你需要用到它,只用一次然后缓存结果
- cellForRowAtIndexPath:
,
- rowHeight
和
- sectionFooterHeight
来设定固定的高,不要请求 delegate
- sectionHeaderHeight
当做本地数据存储时你会怎么做?
你有很多选择,比如:
- NSUerDefaults
NSUserDefaults 的问题是什么?虽然它很 nice 也很便捷,但是它只适用于小数据,比如一些简单的布尔型的设置选项,再大点你就要考虑其它方式了
XML 这种结构化档案呢?总体来说,你需要读取整个文件到内存里去解析,这样是很不经济的。使用 SAX 又是一个很麻烦的事情。
NSCoding?不幸的是,它也需要读写文件,所以也有以上问题。
当存储大块数据时, 以上的方法都不适用. 在这种应用场景下,使用 SQLite 或者 Core Data 比较好。使用这些技术你用特定的查询语句就能只加载你需要的对象。
在性能层面来讲,SQLite 和 Core
Data 是很相似的。他们的不同在于具体使用方法。Core Data 代表一个对象的 graph
model,但 SQLite 就是一个 DBMS。Apple 在一般情况下建议使用 Core
Data,但是如果你有理由不使用它,那么就去使用更加底层的 SQLite 吧。
快速打开 app 是很重要的,特别是用户第一次打开它时,对 app 来讲,第一印象太太太重要了。
负责释放 block 中的 autoreleased objects。一般情况下它会自动被 UIKit 调用。但是有些状况下你也需要手动去创建它。假如你创建很多临时对象,你会发现内存一直在减少直到这些对象被 release 的时候。这是因为只有当 UIKit 用光了 autorelease pool 的时候 memory 才会被释放。��的对象来避免这个行为:
- NSAutoreleasePool
- NSArray *urls = <# An array of file URLs #>;
- for(NSURL *url in urls) {
- @autoreleasepool {
- NSError *error;
- NSString *fileContents = [NSString stringWithContentsOfURL:url
- encoding:NSUTF8StringEncoding error:&error];
- /* Process the string, creating and autoreleasing more objects. */
- }
- }
这段代码在每次遍历后释放所有 autorelease 对象
更多关于 NSAutoreleasePool 请参考官方文档。
常见的从 bundle 中加载图片的方式有两种,一个是用
,二是用
- imageNamed
,第一种比较常见一点。
- imageWithContentsOfFile
既然有两种类似的方法来实现相同的目的,那么他们之间的差别是什么呢?
的优点是当加载时会缓存图片。
- imageNamed
的文档中这么说: 这个方法用一个指定的名字在系统缓存中查找并返回一个图片对象如果它存在的话。如果缓存中没有找到相应的图片,这个方法从指定的文档中加载然后缓存并返回这个对象。相反的,
- imageNamed
仅加载图片。下面的代码说明了这两种方法的用法:
- imageWithContentsOfFile
- UIImage * img = [UIImage imageNamed: @"myImage"]; // caching
- // or
- UIImage * img = [UIImage imageWithContentsOfFile: @"myImage"]; // no caching
那么我们应该如何选择呢?
如果你要加载一个大图片而且是一次性使用,那么就没必要缓存这个图片,用
足矣,这样不会浪费内存来缓存它。然而,在图片反复重用的情况下
- imageWithContentsOfFile
是一个好得多的选择。
- imageNamed
如果你要用
来处理很多日期格式,应该小心以待。就像先前提到的,任何时候重用
- NSDateFormatter
都是一个好的实践。然而,如果你需要更多速度,那么直接用 C 是一个好的方案。Sam Soffes 有一个不错的帖子 (
- NSDateFormatters
- - (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp {
- return[NSDate dateWithTimeIntervalSince1970:timestamp];
- }
这样会比用 C 来解析日期字符串还快!
需要注意的是,许多 web API 会以微秒的形式返回时间戳,因为这种格式在 javascript 中更方便使用。记住用
之前除以 1000 就好了。
- dateFromUnixTimestamp
来源: http://www.bubuko.com/infodetail-1992741.html