@property的内存管理策略&copy属性修饰符解惑

@property不仅涉及实例变量和存取方法,还包括内存管理策略。本文详细解析了MRC和ARC下的内存管理,如retain、assign、copy等。在NSString和block对象上使用copy修饰符的原因是为了确保数据的稳定性和避免生命周期问题。对于NSString,copy能防止NSMutableString的修改影响到NSString。而对于block,copy能将栈上的_block转化为堆上的_NSConcreteMallocBlock,延长其生命周期,确保在对象存活期间可以正常使用。

壹:@property的内存管理策略

@property 的本质:

@property = ivar + getter + setter;

“属性” (property)有两大概念:ivar(实例变量)、存取方法(access method = getter + setter)。

我们都知道, @property修饰符, 系统会给类声明的属性声明默认 (最简单的) 的setter和getter方法.

一般它有如下四个作用:

1.控制set方法的内存管理

①MRC下, @property的内存管理策略:

  • retain : release旧值,retain新值(用于OC对象)
  • assign :直接赋值,不做任何内存管理 (默认,用于非OC对象类型)
  • copy : release旧值,copy新值(一般用于NSString *和block)

②ARC下, @property的内存管理策略 (重点掌握) :

  • copy : NSString *和block对象
  • strong : 除NSString\block以外的OC对象, 强指针引用过, 防止对象被销毁
  • weak : 当两个对象互相引用时, 一端用strong, 一端用weak, 防止循环引用
  • assign : 基本数据类型

2.控制需不需要生成set方法

  • readwrite :同时生成set方法和get方法(默认)
  • readonly :只会生成get方法

3.多线程管理

  • atomic :线程安全,性能低,并不能完全保证安全(默认)
  • nonatomic :线程不安全,性能高

4.控制set方法和get方法的名称

  • setter : 设置set方法的名称,一定有个冒号:
  • getter : 设置get方法的名称

贰:Copy属性修饰符解惑

困惑1: 为什么我们要用copy修饰NSString对象?

如果我们查阅资料, 会发现很多答案, (copy方法和深浅复制的使用在我之前的一篇文章讲到了, 如果还不太理解可以点击)

那么其实在@property中使用copy修饰符就是为了防止NSMutableString赋值给NSString(也就是我们的成员属性)时, 前者的修改引起了后者的值变化.

copy方法底层, 会在setString:这个方法内做判断:
如果赋值来源是NSString时, copy的作用和strong作用相同, 只是起了强引用的作用(因为NSString对象本身是不可被修改的);
如果赋值来源是NSMutableString时, copy会对其做深拷贝, 生成一个新的对象, 那么我们修改赋值来源的对象时,我们的成员属性就不会因为来源的值改变而改变了!

困惑2: 为什么我们要用copy修饰block对象?

简单来说,block就像一个函数指针,指向我们要使用的函数。

就和函数调用一样的,不管你在哪里写了这个block,只要你把它放在了内存中(通过调用存在这个block的方法或者是函数),不管放在栈中、堆中,还是在静态区,只要该block没有被销毁,你都可以用“blockName()”调用他。

说到在类中声明一个block为什么要用copy修饰的话,那就要先说block的三种类型。

1._NSConcreteGlobalBlock,全局的静态block,不会访问外部的变量。
就是说如果你的block没有调用其他的外部变量,那你的block类型就是这种。
例如:你仅仅在你的block里面写一个NSLog(“hello world”);

2._NSConcreteStackBlock,保存在栈中的 block,当函数返回时会被销毁。
这个block就是你声明的时候不用copy修饰,并且你的block访问了外部变量。

3._NSConcreteMallocBlock,保存在堆中的 block,当引用计数为 0 时会被销毁。
好了,这个就是今天的主角 ,用copy修饰的block。

我们知道,函数的声明周期是随着函数调用的结束就终止了。我们的block是写在函数中的。

如果是全局静态block的话,他直到程序结束的时候,才会被被释放。但是我们实际操作中基本上不会使用到不访问外部变量的block。

如果是保存在栈中的block,他会随着函数调用结束被销毁。从而导致我们在执行一个包含block的函数之后,就无法再访问这个block。因为(函数结束,函数栈就销毁了,存在函数里面的block也就没有了),我们再使用block时,就会产生空指针异常。

如果是堆中的block,也就是copy修饰的block。他的生命周期就是随着对象的销毁而结束的。只要对象不销毁,我们就可以调用的到在堆中的block。

这就是为什么我们要用copy来修饰block。因为不用copy修饰的访问外部变量的block,只在他所在的函数被调用的那一瞬间可以使用。之后就消失了。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值