目录
imageNamed:
与imageWithContentsOfFile:
选择- 缓存
NSDateFormatter
的结果- 寻找
(NSDate *)dateFromString:(NSString )string
的替换品。- 不要随意使用 NSLog()
- 获取文件属性
- 内存泄露检测工具
- FPS性能监测工具
一 imageNamed:
与imageWithContentsOfFile:
选择使用
imageNamed:
这种方法会首先在系统缓存
中根据指定的名字寻找图片,如果找到了就返回。如果没有在缓存中找到图片,该方法会从指定的文件中加载图片数据,并将其缓存
起来,然后再把结果返回,下次再使用该名称图片的时候就省去了从硬盘中加载图片的过程。对于相同名称的图片,系统只会把它Cache到内存一次。
适用于会重复加载的小图片,因为系统会自动缓存加载的图片
imageWithContentsOfFile:
只是简单的加载图片,并不会将图片缓存起来,图像会被系统以数据方式加载到程序。
当你不需要重用该图像,或者你需要将图像以数据方式存储到数据库,又或者你要通过网络下载一个很大的图像时,可以使用这种方式。
- 实例代码
- (void)drawImgView {
self.imgView1.center = CGPointMake(kScreenWidth * 0.25, kScreenHeight * 0.5);
self.imgView1.image = [UIImage imageNamed:@"gir2"];
[self.view addSubview:self.imgView1];
self.imgView2.center = CGPointMake(kScreenWidth * 0.75, kScreenHeight * 0.5);
NSString *imgUrl = [[NSBundle mainBundle] pathForResource:@"gir1" ofType:@"jpg"];
self.imgView2.image = [UIImage imageWithContentsOfFile:imgUrl];
[self.view addSubview:self.imgView2];
}
运行结果
image.png二 缓存 NSDateFormatter 的结果
首先,过度的创建NSDateFormatter用于NSDate与NSString之间转换,会导致App卡顿,打开Profile工具查一下性能,你会发现这种操作占CPU比例是非常高的。据官方说法,创建NSDateFormatter代价是比较高的,如果你使用的非常频繁,那么建议你缓存起来,缓存NSDateFormatter一定能提高效率。
Creating a date formatter is not a cheap operation. If you are likely to use a formatter frequently, it is typically more efficient to cache a single instance than to create and dispose of multiple instances. One approach is to use a static variable.
- 实例代码
NSDateFormatter
/// use NSDateFormatter
- (void)convertDateToStringUsingDateFormatter {
old = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < TIMES; i++) {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
self.dateStr = [formatter stringFromDate:[NSDate date]];
}
now = CFAbsoluteTimeGetCurrent();
NSLog(@"convertDateToStringUsingDateFormatter: %f",now - old);
}
struct tm
/// use struct tm
- (void)convertDateToStringUsingCLocaltime {
old = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < TIMES; i++) {
time_t timeInterval = [NSDate date].timeIntervalSince1970;
struct tm *cTime = localtime(&timeInterval);
self.dateStr = [NSString stringWithFormat:@"%d-%02d-%02d %02d:%02d:%02d", cTime->tm_year + 1900, cTime->tm_mon + 1, cTime->tm_mday,cTime->tm_hour, cTime->tm_min, cTime->tm_sec];
}
now = CFAbsoluteTimeGetCurrent();
NSLog(@"convertDateToStringUsingDateFormatter: %f",now - old);
}
运行结果
image.png可知
NSDateFormatter
所花费的时间更长。
三 寻找 (NSDate *)dateFromString:(NSString )string
的替换品。
- (NSDate *)dateFromString:(NSString *)string;
时间戳宏定义
// 2019-04-06 20:11:13
#define kTimeInterval @"1554552673"
#define kTimeInterval2 @"1554552999"
- 实例代码 - 使用
dateFromString:
创建NSDate
对象
// timeInterval - 1554552673
- (NSDate *)dateFromString:(NSString *)timeInterval {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSDate *date = [formatter dateFromString:timeInterval];
return date;
}
- 使用
dateWithTimeIntervalSince1970:
创建NSDate
对象
// timeInterval - 2019-04-06 20:11:13
- (NSDate *)strptimeFromString:(NSString *)timeInterval {
time_t t;
struct tm tm;
strptime([timeInterval cStringUsingEncoding:NSUTF8StringEncoding], "%Y-%m-%dT%H:%M:%S%z", &tm);
tm.tm_isdst = -1;
t = mktime(&tm);
NSDate *date = [NSDate dateWithTimeIntervalSince1970:t + [[NSTimeZone localTimeZone] secondsFromGMT]];
return date;
}
对比两个时间差,比较1024 * 10次,看看所花费的时间
// dateFromString
- (void)compareTime {
old = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < TIMES; i++) {
NSDate* startDate = [self dateFromString:@"1554552673"];
NSDate* endDate = [self dateFromString:@"1554552999"];
NSTimeInterval time = [endDate timeIntervalSinceDate:startDate];
}
now = CFAbsoluteTimeGetCurrent();
NSLog(@"convertDateToStringUsingDateFormatter: %f",now - old);
}
// dateWithTimeIntervalSince1970
- (void)compareTime2 {
old = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < TIMES; i++) {
NSDate* startDate = [self strptimeFromString:@"2019-04-06 20:11:13"];
NSDate* endDate = [self strptimeFromString:@"2019-04-06 20:46:13"];
NSTimeInterval time = [endDate timeIntervalSinceDate:startDate];
}
now = CFAbsoluteTimeGetCurrent();
NSLog(@"convertDateToStringUsingDateFormatter: %f",now - old);
}
运行结果
image.png四 不要随意使用 NSLog()
五 获取文件属性
当试图获取磁盘中一个文件的属性信息时,使用[NSFileManager attributesOfItemAtPath:error:]
会浪费大量时间读取可能根本不需要的附加属性。这时可以使用stat
代替NSFileManager
,直接获取文件属性:
- 实例代码 -
NSFileManager
- (void)getFileAttrByFileManager {
NSString *path = [[NSBundle mainBundle] pathForResource:@"address" ofType:@"plist"];
NSError *error;
NSDictionary *attrDict = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error];
if (error) {
NSLog(@"error = %@",error.description);
} else {
NSLog(@"attrDict = %@",attrDict);
}
}
- 实例代码 -
stat
#import <sys/stat.h>
- (void)getFileAttrByStat {
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"address" ofType:@"plist"];
struct stat statbuf;
const char *cpath = [filePath fileSystemRepresentation];
if (cpath && stat(cpath, &statbuf) == 0) {
NSNumber *fileSize = [NSNumber numberWithUnsignedLongLong:statbuf.st_size];
NSDate *creationDate = [NSDate dateWithTimeIntervalSince1970:statbuf.st_ctime];
NSDate *modificationDate = [NSDate dateWithTimeIntervalSince1970:statbuf.st_mtime];
NSLog(@"fileSize = %ld, creationDate = %@, modificationDate = %@",(long)[fileSize integerValue],[self.formatter stringFromDate:creationDate],[self.formatter stringFromDate:modificationDate]);
}
}
运行结果如下
image.png六 如何预防性能问题
如何在开发测试阶段发现问题解决问题,是预防性能问题的关键。为此,可以通过一些工具,用于发现各种性能问题。
6.1 内存泄露检测工具
MLeakFinder 是团队成员zepo在github开源的一款内存泄露检测工具。
MLeakFinder能在开发阶段,把内存泄露问题暴露无遗,减少了很多潜在的性能问题。
关于MLeakFinder
原理详解请看我的另一篇文章 iOS-MLeaksFinder详解 。
6.2 FPS性能监测工具条
FPS监测的原理并不复杂,虽然并不百分百准确,但非常实用,因为可以随时查看FPS低于某个阈值时的堆栈信息,再结合当时的使用场景,开发人员使用起来非常便利,可以很快定位到引起卡顿的场景和原因。
关于FPS监测原理及使用请看我的另一篇文章 iOS-卡顿监测-FPS监测(附详细代码及原理讲解)。
本文会持续更新,更多内容敬请期待!
文本参考
微信读书 iOS 性能优化总结
网友评论