美文网首页
内存优化

内存优化

作者: heart_领 | 来源:发表于2018-09-28 17:24 被阅读5次

    一、自动释放池

    1. 自动释放池的主要结构体和类是:__AtAutoreleasePool、AutoreleasePoolPage
    2. 调用了autorelease的对象最终都是通过AutoreleasePoolPage对象来管理的
    3. AutoreleasePoolPage的大小都是4096个字节


      结构.png
      tag22.png

    5.自动释放池是由AutoreleasePoolPage以双向链表的方式实现的
    6.当对象调用autorelease方法是, 会将延迟释放的对象加入AutoreleasePoolPage中(入栈)
    7.调用pop方法时(出栈), 会向栈中的对象发送release消息,一直pop到哨兵处
    注意:可以通过这个函数来查看自动释放池的情况
    extern void _objc_autoreleasePoolPrint(void);


    实例.png

    二、引起内存泄漏的原因
    1.循环引用
    2.强引用
    3.非OC对象,没有手动释放
    NSTimer

    //循环引用原因:RunLoop -> timer --> self
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];
    

    解决SNTimter循环引用

    调用
        self.timer = [[TimerWrapper alloc] initWithTimerInterval:1.0 target:self selector:@selector(fire)];
    
    @interface TimerWrapper : NSObject
    - (id) initWithTimerInterval:(NSTimeInterval)interval target:(id)target selector:(SEL) sel;
    - (void) stop;
    @end
    
    #import "TimerWrapper.h"
    @interface TimerWrapper()
    @property (nonatomic, weak) id target;//弱引用
    @property (nonatomic, assign) SEL sel;
    @property (nonatomic, strong) NSTimer* timer;
    @end
    @implementation TimerWrapper
    - (id) initWithTimerInterval:(NSTimeInterval)interval target:(id)target selector:(SEL) sel {
        if (self = [super init]) {
            self.target = target;
            self.sel = sel;
            self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];
        }
        return self;
    }
    - (void) fire {
        if ([self.target respondsToSelector:self.sel]) {
            [self.target performSelector:self.sel];
        }
    }
    - (void) stop {
        [self.timer invalidate];
        self.timer = nil;
    }
    @end
    第二种方式
    /// RunLoop -> timer -> target <--- self
        self.target = [NSObject new];
        class_addMethod([self.target class], @selector(fire), (IMP)fire, "v@:");
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self.target selector:@selector(fire) userInfo:nil repeats:YES];
    
    - (void)dealloc
    {
        [self.timer invalidate];
        self.timer = nil;
        NSLog(@"%s", __func__);
    }
    第三种方式
    
    self.proxy = [TZWeakProxy alloc];
        self.proxy.target = self;
        
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self.proxy selector:@selector(fire) userInfo:nil repeats:YES];
    
    #import <Foundation/Foundation.h>
    NS_ASSUME_NONNULL_BEGIN
    @interface TZWeakProxy : NSProxy
    @property (nonatomic, weak) id target;
    @end
    NS_ASSUME_NONNULL_END
    #import "TZWeakProxy.h"
    @implementation TZWeakProxy
    //消息转发:转发到那个对象,就用那个对象签名方法,然后用该对象调用签名的方法(消息转发到A类,就用A类对象签名A类中的方法,然后用A类的对象调用签名的方法)
    - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
        return [self.target methodSignatureForSelector:sel];//对象签名对象里的方法,转发到那个对象,就用那个对象签名方法
    }
    - (void) forwardInvocation:(NSInvocation *)invocation {
        [invocation invokeWithTarget:self.target];//指定用那个对象调用方法
    }
    @end
    

    三、内存泄漏检测方法
    1.静态检测方法(手动(product)、自动(build setting中搜索analyze))
    2.动态监测方法(instruments 第三方工具检测)
    3.dealloc
    检查对象是否释放

    //在使用dispatch_get_main_queue()的情况下,下一次runloop才会执行以下代码,如果对象在上次runloop中释放了则weakSelf为nil,若没有释放则对象存在,就会执行assertNotDealloc方法(每次runloop的对象会在本次runloop结束,下次runloop开始之前释放掉。dispatch_after中代码块在下次runloop中执行。如果想要查看对象是否释放,就在下一次runloop中查看上一次runloop中是否把对象释放了。或者使用dispatch_get_global_queue(0, 0)的情况下延迟执行block中的代码块也可以达到相同效果,比如延迟1秒,延迟执行的代码不会在本次runloop中执行,根据延迟的时间不同会在以后的runloop中执行,有可能在下一次,也有可能在下下次,下下下次...)
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    //        __strong id strongSelf = weakSelf;
            [weakSelf assertNotDealloc];
        });
    

    四、启动相关知识和优化建议
    1.启动分为2种: 冷启动和热启动
    2.启动的时间分为两部分:main函数执行之前、main函数至应用启动完成

    启动优化建议
    main函数之前
    1.减少动态库、合并一些动态库
    2.减少Objc类、分类的数量、减少Selector数量
    3.main函数至应用启动完成,耗时操作,不要放在finishLaunching方法中
    4.动态库对启动时间的影响测试,http://www.cocoachina.com/ios/20161125/18179.html
    //DYLD_PRINT_STATISTICS

    main之前加载.png

    界面相关知识
    1.卡顿的原理
    2.界面流畅度的评测

    界面优化建议
    1.耗时操作,不要放在主线程
    2.合理使用CPU与GPU
    CPU: 计算显示内容, 比如视图的创建、布局计算、图片解码、文本绘制
    GPU :会把CPU计算好的数据进行渲染
    五、网络优化建议
    1)资源优化基本就是尽可能的缩小传输数据的大小

    2)可以使用ProtocolBuffer代替Json进行数据传输
    Protocolbuffer(简称Protobuf或PB)是由Google推出的一种数据交换格式,它独立于语言,独立于平台。

    ProtocolBuffer代替Json进行数据传输,因为ProtocolBuffer数据比Json更小,也是跨平台的,序列号与反序列化也很简单。在实际项目中,当数据变小的时候会显著提高传输速度。
    六、如何获得Link Map文件?
    1.在XCode中开启编译选项Write Link Map File \n
    XCode -> Project -> Build Settings -> 把Write Link Map File选项设为yes,并指定好linkMap的存储位置

    2.工程编译完成后,在编译目录里找到Link Map文件(txt类型) 默认的文件地址:
    ~/Library/Developer/Xcode/DerivedData/XXX-xxxxxx/Build/Intermediates/XXX.build/Debug-iphoneos/XXX.build/
    七、能耗优化

    1. CPU:CPU是电能消耗大户,高CPU使用量会迅速消耗掉用户的电池电量。app做的每件事几乎都需要用CPU,所以使用CPU要精打细算,真正有需要时通过分批、定时、有序地执行任务。

    2.设备唤醒:iOS设备通过睡眠来节能。只要设备被唤醒,屏幕和其他的硬件资源就必须通电,会产生很高的间接功耗。如非必须,app要尽量保持闲置,不要推送消息或用其他方式唤醒设备,特别是app在后台的时候。

    3.网络操作:大多数app都需要网络操作。网络通信时,蜂窝数据和Wi-Fi等元器件开始工作就会消耗电能。分批传输、减少传输、压缩数据、恰当地处理错误,你的app省电效果会很显著。

    4.图像、动画、视频:app内容每次更新到屏幕上都需要消耗电能处理像素信息。动画和视频格外耗电。不经意的或者不必要的内容更新同样会消耗电能,所以UI不可见时,应该避免更新其内容。

    1. 位置:很多app为了记录用户的活动或者提供基于位置的服务会进行定位。定位精度越高,定位时间越长,消耗电量也就越多。所以app应该尽量降低定位精度、缩短定位时间。不需要位置信息之后立即停止定位。

    6.动作传感器:长时间用不上加速度计、陀螺仪、磁力计等设备的动作数据时,应该停止更新数据,不然也会浪费电能。应按需获取,用完即停。

    7.蓝牙:蓝牙活动频度太高会消耗电能,应该尽量分批、减少数据轮询等操作
    八、安装包瘦身
    1.资源优化
    1.1.无损压缩(图片、音频、视频):格式工厂、图片压缩https://tinypng.com/
    1.2.删除无用资源:(https://github.com/tinymind/LSUnusedResources
    2.可执行文件瘦身
    2.1.删除无用代码 app code
    2.2.静态库瘦身:
    2.2.1. 查看静态库支持的CPU架构:lipo -info libname.a (或libname.framework/libname)
    2.2.2. 合并静态库:lipo -create libname-armv7.a libname-armv7s.a libname-i386.a -output libname.a
    2.2.3. 静态库拆分:lipo libname.a -thin armv7 -output libname-armv7.a
    2.3.编译器优化:
    2.3.1. Strip Linked Product、Make Strings Read-Only、Symbols Hidden by Default设置为YES
    2.3.2. 去掉异常支持,Enable C++ Exceptions、Enable Objective-C Exceptions设置为NO, Other C Flags添加-fno-exceptions

    相关文章

      网友评论

          本文标题:内存优化

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