美文网首页
iOS-1 内存管理 属性关键字、内存分区总结

iOS-1 内存管理 属性关键字、内存分区总结

作者: leesen | 来源:发表于2019-08-04 22:12 被阅读0次

    一、属性关键字分3类,原子性,引用计数,读写权限

    1.原子性:

    ·atomic:线程安全,开销大,影响性能,一般不用;

    ·nonatomic:常用,可以用代码,保证线程的安全;

    2.引用计数:

    ·assign 修饰非指针变量,一般用于基础类型对象和C数据类型,这些都不是对象,由系统栈进行内存管理; (id类型的delegate属性到底是用assign还是weak?答案是weak,据说是weak比assign多了一个,当所指对象销毁时,自动置为nil,这样再给weak修饰的属性发送消息就不会crash) ;首先assign修饰的是基本数据类型,简单赋值不改变引用计数,weak只可以修饰oc中的对象。其次arc环境下weak修饰的对象被释放后指向对象的指针会被自动置为nil,而assign修饰的变量可能不会被置为nil,造成野指针会导致程序crash。

    ·weak对对象的弱引用,不增加对象的引用计数,也不持有该对象,当对象消失了,指针自动置为nil,从而避免野指针。

    ·strong对对象的强引用,增加对象的引用计数,持有该对象,如果指向的对象为空,会造成野指针;某些情况下,使用strong,也会造成循环引用,造成内存泄露;但是我们最常用的还是strong。

    retain

    retain用来释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为1

    对其他NSObject和其子类,对参数进行release旧值,再retain新值。    

    retain的语法为:

    - (void)setName:(NSString *)newName {

      if(name != newName) {

          [name release];

          name = [newName retain];

        //注意: 把对象添加到数组中时,引用计数将增加对象的引用次数+1。

          // name’s retain count has been bumped up by 1   

     }

     声明属性时用strong或者retain效果是一样的(貌似更多开发者更倾向于用strong)。不过在声明Block时,使用strong和retain会有截然不同的效果。strong会等于copy,而retain竟然等于assign!

      当然定义Block还是应该用copy(还有其他需要注意的地方,可以参考这篇文章:iOS: ARC和非ARC下使用Block属性的问题),因为非ARC下不copy的Block会在栈中,ARC中的Block都会在堆上的。

      可以这样复现问题。在非ARC环境下,定义一个简单类型,定义一个Block属性,先用正确的copy:

    参考:retain及区别

    iOS的属性声明:retain和strong的区别 - 黑暗森林的歌者 - 博客园

    ·copy会创建一个引用计数为1的新对象,但并不持有该对象,只是在复制的时候将对象的值复制给该属性,使用copy关键字的对象必须要实现NSCopying协议。修饰的类型一般是NSString,NSArray,NSDictionary。建立一个索引计数为1的对象,然后释放旧对象。

    ·unsafe-unretained跟weak类似,都是弱引用,当所指对象的引用计数为0,即销毁时,指针不会自动置为nil。所以,会导致程序崩溃,现在一般都会使用weak。

    3.读写权限:

    readwrite: 可读可写,默认

    readonly: 只可读,当属性可以被外界看到,但是却不想被外界修改;

    1.1、根据代码分析:weak和strong

     ·声明2个属性

    @property (nonatomic, strong) id strongObj;

     @property (nonatomic, weak) id weakObj;

    ·测试代码

    _strongObj = [NSObject new];

     _weakObj = _strongObj;

    NSLog(@"strongObj=%@, weakObj=%@", _strongObj, _weakObj);

     打印结果 > strongObj=<NSObject: 0x604000005cc0>, weakObj=<NSObject: 0x604000005cc0>

    假如我们给测试代码改为:

    _strongObj = [NSObject new];

     _weakObj = _strongObj;

     _strongObj = nil; // 置为nil 

    NSLog(@"strongObj=%@, weakObj=%@", _strongObj, _weakObj); 

    打印结果: strongObj=(null), weakObj=(null)

    由以上两个打印结果可以看出,weak为弱引用,并没有真正的持有该对象,也没有对该引用计数产生影响,当所指的对象不存在时,weak指针就自动置为nil。

    再次修改测试代码,来分析分析strong

    id obj = [NSObject new];

     _strongObj = obj; 

    _weakObj = _strongObj;

    obj = nil; 

    NSLog(@"strongObj=%@, weakObj=%@, obj=%@", _strongObj, _weakObj,obj); 

    打印结果: strongObj=<NSObject:>, weakObj=<NSObject:>, obj=(null)

    可以看到,虽然给obj置为nil的时候,strong属性和weak修饰的属性都可以打印出结果,由此可说明,strong对对象强引用,并导致引用计数+1,所以,只有再把_strongObj = nil,该对象的引用计数才会变为0, _strongObjc=null,_weakObj=null;

    1.2·根据代码分析:copy,以及在声明block属性的时候为什么一定要用copy

    id obj = [NSObject new]; 

    _copOBJ = obj; 

    NSLog(@"copyObj=%@", _copObj); 

    obj = [NSObject new];

     NSLog(@"copyObj=%@", _copObj); 

    打印结果:

     copyObj=<NSObject:>

    copyObj=<NSObject:>

    由此看出,原对象虽然更改了,但是并未影响copy修饰的属性,即深拷贝。

    block经常使用copy关键字 原因:见下次详解

    注意:NSMutableString、NSMutableArray、NSMutableDictionary,使用copy应注意 原因:添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃.因为copy就是复制一个不可变NSArray的对象;

    内存管理的好处说:iOS内存管理机制的好处就是为了让开发人员方便的管理内存,减少程序中的内存泄漏,在内存管理难度与性能之间找一个最佳的平衡点。

    其他:

    二、关于MRC:

    MRC黄金法则:凡是使用alloc、init、copy、nsmutablecopy、retain进行创建对象的都要使用release或者autorelease进行释放;

    谁创建,谁销毁。谁引用,谁管理。

    三、一些点

    Objective-C 编程:iOS 中的深拷贝与浅拷贝 - 简书

    1 深拷贝 浅拷贝

    1.1. 浅拷贝

    浅拷贝就是对内存地址的复制,让【目标对象指针】和【源对象指针】指向同一块内存空间,当内存被销毁时,指向这块内存的所有指针需要重新定义才可以使用,要不然会成为野指针。

    浅拷贝就是拷贝【指向原来对象的】指针,使原对象的引用计数+1,可以理解为创建了一个指向原对象的新指针,并没有创建一个全新的对象。

    1.2. 深拷贝

    深拷贝是指拷贝对象的具体内容,而内存地址是自主分配的,拷贝结束之后,两个对象虽然存的值是相同的,但是内存地址不一样,两个对象也互不影响,互不干涉。

    深拷贝就是创建【值与原对象相同,但是内存地址不同的】新对象,创建后的对象和原对象没有任何关系。

    总结

    浅拷贝是指针拷贝,深拷贝是内容拷贝。本质区别在于:

    是否影响内存地址的引用计数;

    是否创建新的内存地址。

    2、

    2.1

    弱引用: 不持有对象

    强引用:持有对象

    2.2 

    常量:    不变的量,其值不能被改变,利用const修饰的变量为常量,不可修改; 利用define定义的一般为常量 ,定义不用分号; 利用extern修饰的量只是生命,

    指针变量: 存放地址的变量

    非指针常量: 不带有指针的常量,对于指针,&p传递的是指针的地址; p传递的是指针所指的变量的地址,也就是变量的地址。

    形参: 形式参数,本质上一个名称而已,参与值的传递

    实参:可以是常量 变量 表达式, 有实际意义的值,参与方法执行

    2.3  到底什么时候才需要在ObjC的Block中使用weakSelf/strongSelf - 浮生猎趣

    在 doSomething 中,weakSelf 不会变成 nil,不过在 doSomething 执行完成,调用第二个方法 doOtherThing 的时候,weakSelf 有可能被释放,于是,strongSelf 就派上用场了:

    __strong 确保在 Block 内,strongSelf 不会被释放。

    在 Block 内如果需要访问 self 的方法、变量,建议使用 weakSelf。

    如果在 Block 内需要多次 访问 self,则需要使用 strongSelf。

    相关文章

      网友评论

          本文标题:iOS-1 内存管理 属性关键字、内存分区总结

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