一、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
)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;
}