长路漫漫,唯剑作伴--内存优化

一、iOS 内存优化那些事

1、ios release版本中去掉NSLog:NSLog是比较消耗内存的,特别是一些字符串拼接的打印。解决方法是可以再PCH文件中定义一个宏,在DEBUG版本中使用系统的NSLog,在RELEASE版本中使用自己定义的。如下:

#ifdef DEBUG          //如果是调试状态
#define HITLog(...)   NSLog(__VA_ARGS__)       //把NSLog换成自己写的HITLog
#else                 //如果是发布状态
#define HITLog(...)   //把自己写的HITLog定义成空
#endif</span></span>

2、不要让主线程承担大量的数据处理工作,这样容易造成程序假死:例如我们可以把耗时的操作放到子线程中处理,之后将刷新UI的工作交给主线程。苹果提供了三种开辟子线程的方式--NSThread、NSOperation、GCD。

3、使用SDWebImage加载图片:SD会保证相同的图片只加载一次,而且永远不必担心阻塞主线程;

4、UITableview的cell重用机制:用户交互时,将看不见的cell放入缓存池中。创建新的cell是,先判断缓存池有没有相同的identifier可以使用,如果没有的话,再创建新的cell;如下:

//每当有一个cell进入视野范围的时候会调用此方法
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *ID = @"cellID";
    //从缓存池中取出可以循环利用的cell
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    
    if (cell == nil) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewStylePlain reuseIdentifier:ID];
    }
    
    return cell;
    
}

5、如果创建了大量的临时对象,最好加入autoreleasepool中,确保对象的及时释放。如下:

 for (int i = 0; i < 100000; i ++) {
         @autoreleasepool {
             NSString * log  = [NSString stringWithFormat:@"%d", i];
             NSLog(@"%@", log);
         }
     }

6、使用Analyse和Instruments工具解决内存泄露问题:Analyse是静态分析工具,Instruments是动态分析工具;

7、优化UITableview:UITableview是使用很频繁的控件,可以使用以下方法保证UITableview的平滑滚动;

  ①、正确的使用reuseIdentifier重用cell;

  ②、使用不透明的视图,包括cell本身;

  ③、缓存行高,做法如下:

    1️⃣、首先为UITableview添加一个category,并在分类中创建一个CellHeightCache类,用于缓存行高;

    2️⃣、在声明三个方法和一个用于缓存行高的可变字典。如下:

#import "UITableView+CellHeightCache.h"
#import <objc/runtime.h>

@interface CellHeightCache ()

@property(nonatomic,strong) NSMutableDictionary<id<NSCopying>, NSNumber *> *mutableCellHeightCaches;

@end

@implementation CellHeightCache

- (instancetype)init
{
    self = [super init];
    
    if (self) {
        _mutableCellHeightCaches = [NSMutableDictionary dictionary];
    }
    
    return self;
}
 // 是否已经缓存
- (BOOL)existsHeightForKey:(id<NSCopying>)key {
    NSNumber *number = self.mutableCellHeightCaches[key];
    return number && ![number isEqualToNumber:@-1];
}
 // 缓存行高
- (void)cacheHeight:(CGFloat)height byKey:(id<NSCopying>)key {
    self.mutableCellHeightCaches[key] = @(height);
}
// 获得行高
- (CGFloat)heightForKey:(id<NSCopying>)key {
#if CGFLOAT_IS_DOUBLE
    return [self.mutableCellHeightCaches[key] doubleValue];
#else
    return [self.mutableCellHeightCaches[key] floatValue];
#endif
}


@end

    3️⃣、在UITableview的分类中关联cellHeigCache属性,并声明一个缓存行高和一个获取行高的方法,具体如下:

@implementation UITableView (CellHeightCache)

// 关联CellHeightCache。如果缓存存在则返回,不则在则设置缓存
- (CellHeightCache *)cellHeightCache
{
    /**
     *  获得关联对象
     *
     *  @param object    添加过关联的对象
     *  @param key       添加的唯一标识符
     */
    CellHeightCache *cache = objc_getAssociatedObject(self, _cmd);
    if (!cache) {
        cache = [CellHeightCache new];
        /**
         *  设置关联对象
         *
         *  @param object  需要添加关联的对象
         *  @param key     添加的唯一标识符
         *  @param value   关联的对象
         *  @param policy  关联的策略,是个枚举
         */
        objc_setAssociatedObject(self, _cmd, cache, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return cache;
}

// 获取行高
- (CGFloat)getCellHeightCacheWithCacheKey:(NSString *)cacheKey
{
    if (!cacheKey) {
        return 0;
    }
    
    //如果已经存在cell height 则返回
    if ([self.cellHeightCache existsHeightForKey:cacheKey]) {
        CGFloat cachedHeight = [self.cellHeightCache heightForKey:cacheKey];
        return cachedHeight;
    } else {
        return 0;
    }
}

//缓存cell的高度
- (void)setCellHeightCacheWithCellHeight:(CGFloat)cellHeight CacheKey:(NSString *)cacheKey
{
    [self.cellHeightCache cacheHeight:cellHeight byKey:cacheKey];
}


@end

    可以看到获取cell高度及缓存cell高度局调用了- (CellHeightCache *)cellHeightCache方法;

    4️⃣、接下来,利用ViewModel计算好cell高度,及一个与之关联的id,做法如下:

#import <Foundation/Foundation.h>

@class WeiBoData;

@interface WeiBoFrame : NSObject

@property (nonatomic, assign)CGRect iconF;
@property (nonatomic, assign)CGRect nameF;
@property (nonatomic, assign)CGRect vipF;
@property (nonatomic, assign)CGRect contentF;
@property (nonatomic, assign)CGFloat cellH;


@property (nonatomic, strong)WeiBoData *weiBoData;
//id
@property(nonatomic,copy,readonly) NSString *identifier;

@end
#import "WeiBoFrame.h"
#import "WeiBoData.h"

#define NameFontSize [UIFont boldSystemFontOfSize:15]
#define ContentFontSize [UIFont boldSystemFontOfSize:16]

@implementation WeiBoFrame

- (instancetype)init
{
    self = [super init];
    if (self) {
        _identifier = [self setIdentifier];
    }
    return self;
}

- (NSString *)setIdentifier
{
    static NSInteger counter = 0;
    NSLog(@"%@",[NSString stringWithFormat:@"statusFrame_id_%@",@(counter++)]);
    return [NSString stringWithFormat:@"statusFrame_id_%@",@(counter++)];
}

    5️⃣、最后只需要在方法里这样调用就可以了

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    WeiBoFrame *frame = self.dataSource[indexPath.row];
//    NSLog(@"%f",frame.cellH);
//    return frame.cellH;
    CGFloat cellHeight = [tableView getCellHeightCacheWithCacheKey:frame.identifier];
    NSLog(@"从缓存取出来的-----%f",cellHeight);
    
    if(!cellHeight){
        cellHeight = frame.cellH;
        [tableView setCellHeightCacheWithCellHeight:cellHeight CacheKey:frame.identifier];
    }
    
    return cellHeight;
}

 

posted @ 2017-01-12 16:23  来事啊  阅读(207)  评论(0编辑  收藏  举报