“开启掘金成长之旅!这是我参与「掘金日新计划 · 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 桥接
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)。
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];
}
- 问题分析:
[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>)>>
)
- VC的View的使用xib定义的时候,注意事项 File‘s owner 修改为对应的VC,并设置VC的View的连线。
注意View的xib的文件名称,是否与其它的VC名称一样
[[HLViewController alloc]init];执行过程分析:
- 去掉Controlle之后,同名的xib--HLView.xib
- 找完全同名的xib--HLViewController.xib
IV、知识补充
4.1 block本身是像对象一样可以retain,和release
- block在创建的时候,它的内存是分配在栈(stack)上,而不是在堆(heap)上。
他本身的作于域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃
- 解决这个问题的方法就是在创建完block的时候需要调用copy的方法。copy会把block从栈上移动到堆上,那么就可以在其他地方使用这个block了。
4.2 宏的定义语法
参数拼接:
#define HSSingletonH(classname) +(instancetype)share##classname
定义weakself,方便在block中使用self
#define WS(weakSelf) __weak __typeof(&*self)weakSelf = self;