美文网首页将来跳槽用
iOS-性能优化总结(附实例)

iOS-性能优化总结(附实例)

作者: 路飞_Luck | 来源:发表于2019-04-06 21:19 被阅读49次
目录
  • 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 性能优化总结


项目连接地址 - PerformanceOptimizeDemo

相关文章

  • iOS-性能优化总结(附实例)

    目录 imageNamed:与imageWithContentsOfFile:选择缓存 NSDateFormatt...

  • iOS-性能优化深入探究

    iOS-性能优化深入探究 iOS-性能优化深入探究

  • iOS 性能优化总结

    iOS 性能优化总结 iOS 性能优化总结

  • IOS的性能优化包括哪几点

    iOS性能优化总结 iOS性能优化总结。关于 iOS 性能优化梳理: 基本工具、业务优化、内存优化、卡顿优化、布局...

  • App瘦身、性能优化总结

    App瘦身、性能优化总结 App瘦身、性能优化总结

  • Awesome Extra

    性能优化 性能优化模式 常见性能优化策略的总结 Spark 性能优化指南——基础篇 Spark 性能优化指南——高...

  • iOS-性能优化深入探究

    iOS-性能优化深入探究 上图是几种时间复杂度的关系,性能优化一定程度上是为了降低程序执行效率减低时间复杂度。如下...

  • iOS 性能优化

    iOS App 启动性能优化iOS离屏渲染优化(附DEMO) iOS Objective-C 数组遍历的性能及原理...

  • iOS必读 - 收藏集 - 掘金

    iOS 性能优化总结 - iOS - 掘金关于iOS 性能优化梳理: 基本工具、业务优化、内存优化、卡顿优化、布局...

  • Redis 持久化

    目录: RDB AOF 持久化恢复 问题排查和性能优化fork 操作子进程开销AOF 追加阻塞单机多实例部署 总结...

网友评论

    本文标题:iOS-性能优化总结(附实例)

    本文链接:https://www.haomeiwen.com/subject/mdgriqtx.html