iOS开发-内存管理

内存管理

对于那篇呢,其落成在都是ARC方式,正常意况下主题不用我们去手动释放内存,所以一旦不是要面试呀、装逼或者扎实功底的,就先别看了或者打听下即可,因为像面试时,有些面试官想看您的根基时,就多少人会问,现在工作中央不会用到。

 

读书目的

  1. 操纵内存管理的法则

  2. 左右手动内存管理

 

===============================================

1.索要驾驭的学识

1.1内存管理

1.1.1 C的内存管理,以及劳动之处

char *p = (char *)malloc(100*sizeof
(char)); 

那是C的动态内存分配,我们手动跟系统报名了100个字节的内存;或者说系统在堆里开发了100个字节的半空中,并将这些空间的首地址再次来到给指针变量p。

strcpy(p,”Hello World!”);

将字符串拷贝给指针变量p指向的内存空间。

puts(p);

将p指针指向的内存空间里的字符串打印出来。

free(p);

行使完了后,手动跟系统释放内存空间;或者说系统回收空间。

如上就是C里大约的内存管理。

C的内存管理,大家手动申请,手动释放。那样来看,我们只需求留意三个问题就好了:

1,申请内存,使用到位后须要释放,假设不自由会招致内存走漏。

2,不可以反复保释,如若反复获释,则会崩溃。

可是,借使项目比较复杂,必要有几十过多号人一同分工到位,就很简单出现问题。

比方说大家开拓了一块内存空间,里存放了一块很有用的数码。可是,这一个数据不只有自身在这一块代码里用,甚至有几人,在先后的三个地点选择。那样造成的结果就是,即使自己动用形成那块内存,我也不能够去放活他,因为自己不可以确定,外人在其他地点是或不是还索要利用那块内存。内存败露在所难免了。

 

 

 

 

OC的内存管理:

1.1.2 引用计数(retainCount)

对于联合动态申请的内存,有一个人(指针)使用,就给这一个内存的计数器加1,使用完了后,就给这几个计数器减1,当以此内存的引用计数为0了,大家再自由他,那样,下边的题材就缓解了。OC,就是应用引用计数那种艺术来管理内存的。

 

 

 

1.1.3 内存管理的黄金法则

对此引用计数来说,有一套内存管理的黄金法则:

The basic rule to apply is everything that
increases the reference counter with alloc, [mutable]copy[withZone:]
or retain is in charge of the corresponding [auto]release.

要是对一个对象使用了alloc、copy、mutablecopy、retain,new,那么您无法不选用

相应的release或者autorelease。

通俗一点的说法就是哪个人污染哪个人治理。

 

 

 

            

1.1.4 objective-C的内存管理遵循下边这么些容易的方针:

1.您有所你制造的目的,也就是说创立的目标(使用alloc,new,copy或者mutalbeCopy等措施)的启幕引用计数是1。

2.给目的发送retain音讯后,你具备了这些目的 ,retainCount+1

3.当你不须求使用该对象时,发送release或者autorelease音讯舍弃那个目的

4.并非对您不具有的目的发送“甩掉”的新闻

 

 

 

 

 

1.1.4 MRC和ARC

ARC Automatic Reference
Counting,自动引用计数,由xcode,帮大家去管理内存。

MRC Manual  Reference
Counting,手动引用计数,大家手动管理内存。

 

Xcode 5.0  版本之后默许是ARC方式,

 

 

 

 

 

 

 

1.1.5 如何将工程改为MRC

xcode5,工程创立的时候是ARC的,大家只要想要MRC,要求展开如下设置。

选中工程 – target – Bulid Settings
-Automatic Reference Counting改为NO。

 

 

 

1.1.6 ARC执行了新的平整

 

● 开发者无法突显调用dealloc;不能够落到实处和调用retain、release、retainCount和autorelease。

取缔利用@selector(retain),@selector(release)等等。

开发者仍能达成dealloc方法,要是你想治本资源而不是变量。

ARC中自定义的dealloc方法,不须求调用[super
dealloc](其实这么做就会促成编译错误),编译器会强制自动链接到父类。

开发者还是可以对Core
Foundation-style对象,使用CFRetain,CFRelease和任何有关办法。

 


开发者不可以使用NSAutoreleasePool对象。ARC下利用@autoreleasepool,它比NSAtuoreleasePool更有成效。

 

为了合作手动引用计数,ARC的法门命名有限量:


访问器方法无法已new开头,反过来就是:开发者无法声称一个已new起首的性能,除非您给您指定一个getter

 

// 不正确 
@property NSString *newTitle;   

// 正确 
@property (getter=theNewTitle) NSString *newTitle;  

 

 

1.1.7.野指南针错误情势在Xcode中常见表现为:Thread
1:EXC_BAD_ACCESS(code=EXC_I386_GPFLT)
荒唐。因为你拜访了一块已经不属于您的内存。

 

2.内需牢记的学问

 

2.1 alloc与release

成立一个Dog类

@interface Dog : NSObject

  @end

  @implementation Dog

  - (void)dealloc

  {

    NSLog(@"dog dealloc");

    [super dealloc];

  }

  @end

 

 
delloc里的析构函数,当目的销毁的时候,会活动调用这些艺术,大家在此间重写这几个情势。

  在main函数里,写入如下代码:

   

int main(int argc, const char * argv[])

  {

    @autoreleasepool {

        Dog *dog = [[Dog alloc] init];

     }

    NSLog(@"程序即将退出");

    return 0;

  }

 

 
从终端打印音讯来看,程序即将退出那条打印在此以前,已经打印dog
dealloc,也就是说在程序运行截止前,dog对象已经灭绝了。那一个是ARC,由xcode帮我们管理dog对象。

 
将ARC改为MRC,再实践顺序,dog对象并没有灭绝,因为我们现在是手动管理了,我们需要遵从内存管理的金子法则,Dog
*dog = [[Dog alloc] init];
大家须要对dog举办release。将main函数代码改为如下格局:

int main(int argc, const char * argv[])

{

    @autoreleasepool {

        Dog *dog = [[Dog alloc] init];

     [dog release];

     }

    NSLog(@"程序即将退出");

    return 0;

}

 

重复实施顺序,从打印可以看来,dog对象,已经销毁。那就是金子法则,大家对dog举办alloc,就要对dog举行release。

注意,release
并不是绝迹对象,让对象的引用计数减1,当目的的引用计数为0的时候,自动调用dealloc方法,销毁对象。

 

 

 

2.2 retain与retainCount

retain,将目的开展封存操作,也就是使对象的引用计数加1。

retainCount,打印一个对象的引用计数。

 

 

 

 

 

2.3 类的复合中采用

在上头代码中,增加Person类

@interface Person : NSObject {

  // 一个人,养了一条狗(持有一条狗)

    Dog *_dog;

  }

  - (void)setDog:(Dog *)dog;

  - (Dog *)dog;

  @end

  @implementation Person

  /* 版本1 (有问题) 人并没有真正持有狗,如果在main函数里[dog release],让dog的引用计数减1,就变为0,dog就销毁了。

    - (void)setDog:(Dog *)dog

  {

    _dog = dog;

  }

    */



  /* 版本2 (有问题) 如果人再持有别的狗,就会造成第一条狗得不到释放,内存泄露。

  - (void)setDog:(Dog *)dog

  {

    _dog = [dog retain];

  }

    */



  /* 版本3 (有问题) 如果本来持有一条狗,又重新设置这条狗,先进行release,这个时候,很可能dog就销毁了,然后,就没法再次retain了。

  - (void)setDog:(Dog *)dog

  {

    [_dog release];

    _dog = [dog retain];

  }

    */



  // 版本4 OK!,标准写法

  - (void)setDog:(Dog *)dog

  {

    if (_dog != dog) {

         [_dog release];

          _dog = [dog retain];

      }

  }



  - (Dog *)dog

  {

    return _dog;

  }



  - (void)dealloc

  {

    NSLog(@"person dealloc");

  // 人在销毁的时候,一并将持有的dog对象销毁

    [_dog release];

    [super dealloc];

  }

 

 

 

 

//MRC:

黄金法则:

假设采用了alloc/retain/copy/mutableCopy,new, 创造了目的

那么就非得采纳release进行放飞,

———总括一句话就是:哪个人成立,何人负责释放

retain   —  使对象的引用计数+1,
倘使指针须求去持有那么些目的

须要利用retain

retainCount:  重返对象的引用计数值 

release :  — 使对象的引用计数 -1,
而不是假释对象

dealloc:对象销毁的时候(也就是retainCount为0的时候)自动调用这些法子

 

 

 

 

 

MRC:

2.4 @property
retain,assign,copy展开

2.4.1 retain展开

如上代码里,Person的setter和getter方法,也足以用property,写成如下格局

@property (nonatomic, retain) Dog
*dog;

             则会展开如下:

- (void)setDog:(Dog *)dog

{

  if (_dog != dog)

  {

    [_dog release];

    _dog = [dog retain];

  }

}

 

 

 

2.4.2 assign展开

  //不难数据类型 ,OC的内存管理对于简易的数据类型 int\float…, 

   

@property (nonatomic, assign) Dog *dog;,assign是直接赋值,则会展开如下:

- (void)setDog:(QFDog *)dog

{

  _dog = dog;

}

 

 

 

 

 

  2.4.3 copy展开  ,
复制一份原来的目标

 

//copy 多用于字符串

@property (nonatomic, copy)NSString *name;

           展开如下:

- (void)setName:(NSString *)name

{

  if (_name != name)

  {

    [_name release];

    _name = [name copy];

  }

}

 

 

 

 

 

 

 

 

2.4 字符串内存管理

  2.4.1 字符串的内存管理

  // 对于字符串而言,非常不听从黄金法则!
(若是从字符串的引用计数来看,乌烟瘴气!) 那只是一个表象!
其实其中依然依据的!!

        // 我们要做的是,大家依然坚守大家的黄金法则!

       

就此,如果是NSString,我们的property格式写成如下: @property (nonatomic,
copy) NSString *name;

 

2.4.2 copy和mutableCopy

       

 

 

2.5 数组的内存管理

 

 

 

 

结论

 
1)当大家创立数组的时候,数组会对种种对象举行引用计数加1

 
2)当数组销毁的时候,数组会对每个对象开展引用计数减1

 
3)当我们给数组添加对象的时候,会对目的开展引用计数加1

 
4)当大家给数组删除对象的时候,会对目标进行引用计数减1

 
不问可知,何人污染什么人治理,管好自己就可以了(数组内部也听从内存管理)。

 

 

 

 

2.6 autorelease与autoreleasepool

在main函数里写如下代码:

int main(int argc, const char * argv[])

  {

    @autoreleasepool {

           Dog *dog = [[Dog alloc] init];

      //dog并没有马上销毁,而是延迟销毁,将dog对象的拥有权交给了autoreleasepool

      [dog autorelease];

      //这个是可以打印的,因为打印完dog的引用计数后,dog对象才销毁

      NSLog(@"retainCount = %lu",dog.retainCount);

     }

    NSLog(@"程序即将退出");

    return 0;

  }

 

 
autoreleasepool相当于一个数组,假使哪个目的发送autorelease信息,实际将目的的拥有权交给了autoreleasepool;当autoreleasepool销毁的时候,autoreleasepool里有所的目的都发送一个release音讯。

 

ACCESS, 2.7 加方法的内存管理 

 
大家用加方法创立的对象,不用大家release,是因为类内部的贯彻采纳了autorelease,延迟释放

  在Dog类的扬言里增添一个加方法

  + (id)dog;

  在Dog类的落到实处里开展落实

  + (id)dog

  {

小心,那里并非写成release,如若是release,那么刚创制就销毁了,使用autorelease,使得将目的的拥有权交给了活动释放池,只要自动释放池没有灭绝,dog对象也就不会销毁。

return [[[Dog alloc] init]
autorelease];

  }

 

 

2.8  对于电动内存释放不难计算一下:

  1.                1
    autorelease形式不会转移目的的引用计数器,只是将这一个目的放置自动释放池中;
  2. 活动释放池实质是当自动释放池销毁后调用对象的release方法,不肯定就能销毁对象(例如若是一个
                对象的引用计数器>1则此时就不可以销毁);
  3. 鉴于活动释放池最终统一销毁对象,因而即便一个操作比较占用内存(对象比较多如故目的占用资源相比较多),最好不用放到自动释放池或者考虑放到多个电动释放池;
  4. ObjC中类库中的静态方法一般都不须求手动释放,内部已经调用了autorelease方法;

 

 

=====================================

 

ARC方式下的关键字:

__strong/__weak/__unsafe_unretain

 

开发者需求正确修饰变量。使用上面的格式来修饰变量评释。

 

             类名*  修饰  变量名

             例如:

 

       MyClass * __weak myWeakReference;   
       MyClass * __unsafe_unretained myUnsafeReference; 

 

 

对应的@property 参数分别为

strong/weak/unsafe_unretain

 

__strong :
强引用,相当于MRC下的retain,指针对对象拥有决定的占

                           
有,默许景况。

__weak :    
弱引用,指针对对象不享有决定的挤占,相当于MRC下的

     
assign,对象释放后,指针赋值为nil。

__unsafe_unretain:弱引用,指针对对象不具有决定的占据,相当于MRC下的assign,对象释放后,指针为悬垂指针(不会赋值为nil),可以会冒出野指针,不指出使用。

 

@property(nonatomic, strong) xxx

//set 类似于 retain 展开  [name
retain]

@property(nonatomic, weak)  xxx

//类似于 assign

@property(nonatomic, unsafe_unretain)
xxx

//类似于 assign

 

相关文章