iOS内存详解

堆栈

iOS内存条中有一部分是只读的,有一部分是可读可写的。我们操作的是可读可写部分,那么在这块内存当中,我们怎么划分堆和栈呢?我们可以限定死堆栈的内存空间,但是这样显然是不好的,那么可以使用相对弹性的空间,一个从上往下扩展,一个从下往上扩展。假如两个碰在一起,就会造成StackOverFlow堆栈溢出。

 NSString *a =@"12";
 //打印对象的内存地址
 NSLog(@"内存地址1:%p",a);//输出内存地址1:0x102f04068
 //打印指针自己的内存地址
 NSLog(@"指针地址2:%x",&a);//输出指针地址2:eccfab58

我们创建一个NSObject对象

- (void)viewDidLoad {
   [super viewDidLoad];
   NSObject * objc = [[NSObject alloc]init];
}

NSObject对象本身是一个结构体,被创建在堆上面,而objc实际上是指向这个结构体的指针,被创建在栈上面。viewDidLoad方法实际上是一个函数体,当函数调用结束时候,在ARC下会自动释放栈上面的指针所占内存(指针在栈上面的大小为32位是4 64位是8,2^10=1024 2^20=1M 2^30=1G 2^32=4G 32位最大地址为0xffffffff),同时在堆上面的OC对象没有被一个强指针指向,也会被自动释放。这就是一个函数被调用的本质。

在MRC的情况下,如果开发者不手动是否内存,那么函数体执行结束后,仍然会释放掉栈上面的内存,但是所指向的结构体仍然在在堆内存中,程序仍然会认为有人仍然在使用它,而不会被释放掉,从而导致内存泄露。(我们可以使用调用malloc在堆上面开辟内存空间,返回地址。alloc底层也是调用的malloc)

free对应malloc,功能为干掉malloc开辟的堆空间。但是指针并没有被释放掉,此时指针所指向的也许是空内存,也许会指向错误数据,这个指针就叫做野指针。那么怎么释放野指针呢?看下面

 CFRunLoopRef runloop;
 runloop =nil;//NULL表示指向一个空地址;nil表示空指针

没错,直接给指针赋值nil

举个栗子:使用CodeFundation下面CFRunLoopObserverRef observer创建一个对象,那么这个对象所指向的结构体应该如何被释放?free(observer)可以吗?

答案:是不可以,因为observer所指向的堆内存当中很有可能会再次开辟堆空间。如果free(observer)了,那么它所指向的堆内存会被销毁,但是在这个堆内存当中开辟的堆内存会依然存在。
CodeFundation会给我们提供一个NFRelease(observer)方法,来解决这个问题。

MRC手动引用计数的内存释放需要我们能够分析引用计数的内存指向问题

-(instancetype)dealloc{
   NSLog(@"被销毁");
   [_gun dealloc];
   [super dealloc];
}

-(void)setGun:(Gun*)gun{
  if(_gun==gun)return;//如果现在的对象是之前的对象,那就直接返回之前的对象
  [_gun release];     //如果不是之前的对象,则让之前的对象销毁
  [_gun retain];      //此处执行是在if判断相等之后执行的,如果两个对象是同一对象,那么让这个对象的引用计数+1
}

ARC下会在编译时自动加入retain,release,autorelease代码,本质上和MRC无区别。

拓展:

block本质上其实也是指向结构体的指针,被创建在栈上面,在程序运行时生成具体的函数。因此我们要使用copy对其进行修饰,copy到堆上,方便我们对其操作。

block内修改外部定义变量,和手动内存管理一样,ARC如果在block中需要修改block之外定义的变量需要使用__block关键字修饰,比如:

__block NSString *name = @"foggry";

self.expireCostLabel.completionBlock = ^(){

    name = @"wangzz";

};

下面的代码不管在MRC还是ARC下myController对象都是有内存泄露的:

MyViewController * myController =[[MyViewController alloc]init];
myController.cmoletionHandler = ^(NSInteger result){
  [myController dismissViewControllerAnimated:YES completion:nil];
}

内存泄露问题在MRC中可以按如下方式更改:

MyViewController * __block myController = [[MyViewController alloc] init…];
myController.completionHandler =  ^(NSInteger result) {
    [myController dismissViewControllerAnimated:YES completion:nil];
};

我们要使用__block进行修饰。

在ARC中该问题有两种解决方案,第一种:

MyViewController * __block myController = [[MyViewController alloc] init…];
myController.completionHandler =  ^(NSInteger result) {
    [myController dismissViewControllerAnimated:YES completion:nil];
    myController = nil;
};

在myController使用结束后,手动使它指向nil。没有强指针指向myController对象时,会被自动释放掉。

第二种种解决方案,直接使用weak代替block关键字:

MyViewController *myController = [[MyViewController alloc] init…];
MyViewController * __weak weakMyViewController = myController;
myController.completionHandler =  ^(NSInteger result) {
    [weakMyViewController dismissViewControllerAnimated:YES completion:nil];
};

直接避免了block对myController的retain。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值