“敞开成长之旅!这是我参加「日新计划 2 月更文应战」的第 19 天,点击查看活动概况”

前语:

内存办理是程序在运行时分配内存、运用内存,并在程序完结时开释内存的过程。在Objective-C中,也被看作是在很多数据和代码之间分配有限内存资源的所有权(Ownership)的一种办法。

内存办理关怀的是整理或回收不必的内存,以便内存能够再次运用。 假如一个目标不再运用,就需求开释目标占用的内存。Objective-C提供了两种内存办理的办法:手动办理内存计数(MRR)和主动引证计数(ARC)。

这两种办法都采用了一种称为“引证计数”的模型来实现,该模型由Foundation结构的NSObject类和运行时环境(Runtime Environment)一起提供。

I、内存办理


但凡函数名中带有create、copy、new、retain等字眼的,都应该在不需求这个数据的时分进行release。

GCD的数据类型在ARC环境下不需求进行release;而CF的数据类型在ARC、MRC环境下都需求做release的

1.1 ARC下内存办理的规矩小结

  • 需求开释的资源:imageCache、queue、operations、view、告诉监听者的移除。毁掉soundID。

  • 开释的办法:dealloc 、applicationDidReceiveMemoryWarning、didReceiveMemoryWarning

  • 内存办理的弥补

/* 内存办理的弥补:
     1、foundation结构 OC言语
     2、core foundation 结构
     1)、C言语,例如通讯录便是基于这个结构。
     2)、core foundation 结构 在ARC、非ARC编译环境下都要办理内存目标。即core foundation 结构中手动创立的数据类型,都需求手动开释
     foundation 和 core foundation 的数据类型能够彼此转化
     */
    NSString *str = @"123";
    CFStringRef  str2 = CFBridgingRetain(str);//Casts an Objective-C pointer to a Core Foundation pointer and also transfers ownership to the caller.
    CFStringRef  str3 = (__bridge CFStringRef)(str);//桥接,跨结构的数据类型转化
    NSString *str4 = CFBridgingRelease(str3);//Moves a non-Objective-C pointer to Objective-C and also transfers ownership to ARC.
    NSString *str5 = (__bridge NSString *)(str3);//不改动具有者
    NSLog(@"%@,%@,%@,%@,%@",str,str2,str3,str4,str5);
    //开释core foundation数据类型
//    CFArrayRef array = CFArrayCreate(NULL, NULL, 10, NULL);
//    CFRelease(array);

1.2 桥接

  • Foundation 和 Core Foundation 彼此转化(bridge相关的关键字介绍)

I.3 dealloc


移除告诉中心的Observer 中止网络监控Notifier //关闭网络检测

- (void)dealloc{
[self.netReachability stopNotifier];  
[[NSNotificationCenter defaultCenter] removeObserver:self name:kReachabilityChangedNotification object:nil];
}

1.3.1 ASI结构的安全内存回收建议

// Clears all delegates and blocks, then cancels the request
- (void)clearDelegatesAndCancel;
//request并没有retain你的delegate,所以在没有请求完的时分开释了此delegate,需求在dealloc办法里先撤销所有请求,再开释请求实例

能够调用ASIHTTPRequest 目标的dealloc办法

1.4、敞开僵尸目标(Zombie Objects)来定位内存问题

1.5、didReceiveMemoryWarning

运用didReceiveMemoryWarning进行内存办理的一些比如如下:

-(void)didReceiveMemoryWarning
    {
            [super didReceiveMemoryWarning];//即使没有显示在window上,也不会主动的将self.view开释。
            // Dispose of any resources that can be recreated.
            // 此处做兼容处理需求加上ios6.0的宏开关,确保是在6.0下运用的,6.0以前屏蔽以下代码,不然会在下面运用self.view时主动加载viewDidUnLoad
            if ([[UIDevice currentDevice].systemVersion floatValue] >= 6.0) {
             //需求留意的是self.isViewLoaded是必不可少的,其他办法访问视图会导致它加载 ,在WWDC视频也忽视这一点。
             if (self.isViewLoaded && !self.view.window)// 是否是正在运用的视图
             {
                   //code
                   self.view = nil;// 意图是再次进入时能够从头加载调用viewDidLoad函数。
             }
           }
    }
  • 移除目标及撤销一些操作
/**内存处理*/
- (void)didReceiveMemoryWarning{
    [super didReceiveMemoryWarning];
    [self.images removeAllObjects];
    [self.operations removeAllObjects];
    [self.queue cancelAllOperations];
}
  • 毁掉soundID

/** 毁掉soundID*/
//通过AudioServicesDisposeSystemSoundID 毁掉soundID。并从字典数组移除。
- (void)didReceiveMemoryWarning{
    [DKAudioTool audioServicesDisposeWithFileName:@"buyao.wav"];//音频的毁掉
}

1.6.applicationDidReceiveMemoryWarning

/**
内存办理
*/
@implementation AppDelegate
- (void) applicationDidReceiveMemoryWarning:(UIApplication *)application{
    SDWebImageManager *imageMgr = [SDWebImageManager sharedManager];
    [imageMgr cancelAll];
    [imageMgr.imageCache clearMemory];
}

II、引证计数

引证计数(Reference Count)是一个简单而有用的办理目标生命周期的办法,

当创立一个新的目标时,初始的引证计数为1。为确保目标的存在,每当创立一个引证到该目标时,通过给目标发送retain音讯,为引证计数加1;当不再需求目标时,通过给目标发送release音讯,为引证计数减1;当目标的引证计数为0时,体系就知道这个目标不再运用了,通过给目标发送dealloc音讯,毁掉目标并回收内存。一般在retain办法之后,引证计数一般也被称为保存计数(retain count)。

iOS小技能:内存管理(引用计数、 ARC下内存管理的规则、桥接)

2.1 手动办理内存 MRR(manual retain-release)

是基于引证计数来实现的,通过自己跟踪目标来明确办理内存。它与ARC之间的唯一区别是:在MRR中,目标的保存和开释都是由我们手动处理,而在ARC中是主动处理的。

2.1.1 手动办理内存的规矩

  • 以alloc、new、copy或mutableCopy最初的办法创立的目标,我们具有该目标,运用完结后需求调用release或autorelease开释。
  • 在init办法中为了获取目标的所有权,或许在某些情况下防止目标被移除,能够运用retain保存目标。在运用完目标后,需求运用release进行开释。
  • 对运用了retain、copy、mutableCopy、alloc或new办法的任何目标,以及具有retain和copy特性的特点进行开释,需求重写dealloc办法,使得在目标被开释的时分能够开释这些实例变量。
  • 给目标发送release音讯并不一定当即毁掉这个目标,只有当目标的引证计数减至0时,目标才会被毁掉,然后体系会发送dealloc音讯给这个目标用于开释它的内存。
  • 假如在办法中不再需求用到某个目标,但需求将其返回,能够给该目标发送autorelease音讯用以标记推迟开释,目标的引证计数会在当时主动开释池的末尾减1。
  • 当应用程序终止时,内存中的所有目标都会被开释,不论它们是否在主动开释池中。
  • 当不再需求一个目标时,有必要放弃所具有的该目标的所有权。
  • 不能放弃一个你所不具有的目标的所有权。

III、遇到的问题


3.1、加载图片的问题

1、App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app’s Info.plist file. developer.apple.com/library/ios… 在info.plist中添加

处理在iOS9 beta1中,苹果将原http协议改成了https协议,运用 TLS1.2 SSL加密请求数据。

<key>NSAppTransportSecurity</key>
    <dict><key>NSAllowsArbitraryLoads</key><true/>
    </dict>

3.2、加载XIB问题

1、xib文件上的目标顺序问题

ps: xib 最好只放一个目标

代码

- (IBAction)tapToolBar:(UITapGestureRecognizer *)sender {
    NSLog(@"%s",__func__);
}
+ (instancetype)toolBar{
    return [[[NSBundle mainBundle]loadNibNamed:@"HLtoolBar" owner:nil options:nil]firstObject];
}
  1. 问题剖析:
 [UITapGestureRecognizer superview]: unrecognized selector sent to instance 0x7a20fa60
 1)superview 办法归于UIView
 2)HLtoolBar *toolBar =[HLtoolBar toolBar];
 3) [self.view addSubview:toolBar];

po toolBar 即可看出问题所在:将UITapGestureRecognizer 当作UIView运用

 po toolBar
 <UITapGestureRecognizer: 0x79695ef0; state = Possible; view = <HLtoolBar 0x796962b0>; target= <(action=tapToolBar:, target=<HLtoolBar 0x796962b0>)>>

回到加载xib类办法进行剖析:

 return [[[NSBundle mainBundle]loadNibNamed:@"HLtoolBar" owner:nil options:nil]lastObject];

运用 po 即可看出: lastObject 是UITapGestureRecognizer firstObject 才是HLtoolBar

 po [[NSBundle mainBundle]loadNibNamed:@"HLtoolBar" owner:nil options:nil]
 <__NSArrayM 0x7bfd4a20>(
 <HLtoolBar: 0x7bfd5570; frame = (0 0; 200 100); autoresize = W+H; gestureRecognizers = <NSArray: 0x7bfd9810>; layer = <CALayer: 0x7bfd4a70>>,
 <UITapGestureRecognizer: 0x7bfd51b0; state = Possible; view = <HLtoolBar 0x7bfd5570>; target= <(action=tapToolBar:, target=<HLtoolBar 0x7bfd5570>)>>
 )
  1. VC的View的运用xib界说的时分,留意事项 File‘s owner 修改为对应的VC,并设置VC的View的连线。

留意View的xib的文件称号,是否与其它的VC称号一样

[[HLViewController alloc]init];执行过程剖析:

  1. 去掉Controlle之后,同名的xib–HLView.xib
  2. 找彻底同名的xib–HLViewController.xib

IV、知识弥补


ARC的简介

敞开僵尸目标调试形式

4.1 block自身是像目标一样能够retain,和release


  1. block在创立的时分,它的内存是分配在栈(stack)上,而不是在堆(heap)上。

他自身的作于域是归于创立时分的效果域,一旦在创立时分的效果域外面调用block将导致程序崩溃

  1. 处理这个问题的办法便是在创立完block的时分需求调用copy的办法。copy会把block从栈上移动到堆上,那么就能够在其他当地运用这个block了。

4.2 宏的界说语法


参数拼接:

#define HSSingletonH(classname) +(instancetype)share##classname

界说weakself,方便在block中运用self

#define WS(weakSelf) __weak __typeof(&*self)weakSelf = self;