目录
属性的含义
属性的本质是:实例变量+set+get方法
属性关键字都是对两种方法进行的修饰
给现有的成员变量生成一对setter/getter方法。
如果没有声明成员变量,自动声明一个 _属性名的私有变量(默认成员变量是受保护的)
@property
@property关键字可以自动生成某个成员变量的setter和getter方法的声明
@synthesize关键字会指定一个以下划线 ( _ ) 为前缀,加上属性名的成员变量。
并且由编译器自动进行该属性setter和getter方法的实现
@dynamic关键字,编译器就不会为上面这个类自动合成存取方法或实例变量
xcode4.5之后@property已经会自动为我们提供@synthesize方法
所以一般情况下无需对属性添加 @synthesize ,但一些特殊情形仍然需要,例如protocol中声明的属性。
我们在协议中使用@property声明一个属性,在某个类中遵循这个协议,这时就会报警告,我们必须使用@synthesize来获取这个属性的成员变量,并且得到其set/get的实现函数
引用计数
在看属性关键字之前我们先来看看什么是引用计数
IOS中手动管理内存方式是通过引用计数来实现内存的管理,当对象调用alloc方法,引用计数置为一,以后每一次retain,引用计数加一,
对应的,release方法,会将对象的引用计数减一,当对象的引用计数为0时,系统将回收对象的内存。
其中值得注意的是调用add方法时也会retain
OC 语言使用引用计数来管理内存每个对象都有个可以递增或递减的计数器,如果想某个对象继续存活,那就递增其引用计数,用完之后,就递减其计数,计数变为0,就销毁。
当使用alloc new copy等方法的时候引用计数设置为1
之后每一次使用retain(比如add方法,持有对象)引用计数就会加1,这代表多了一个对象在使用这个变量,其引用计数就会增加。
使用release方法,其引用计数就会减1,当引用计数减少为0的时候就代表没人用他了,这时候系统就会调用dealloc方法来销毁其对象
非自己生成的对象,自己也能持有
//取得非自己生成并持有的对象
id obj = [NSMutableArray array];
//NSMutableArray类变量被赋值给obj,但是obj自己并不持有该对象。
[obj retain];
//现在obj持有该对象
//通过retain方法,非自己生成的对象跟用alloc方法生成并持有的对象一样。
无法释放非自己持有的对象
对于用alloc\ new\ copy \ mutableCopy方法生成并持有的对象,或是用retain方法持有的对象,由于持有者是自己,所以在不需要该对象时需要将其释放,而由此之外所得到的对象绝对不能释放。若在应用程序中释放了非自己所持有的对象就会造成崩溃。
- (id)object {
id obj = [[NSObejct alloc] init];
//自己持有对象
[obj autorelease];
//取得对象存在,但自己不持有对象
return obj;
}
//通过autorelease方法,可以使取得的对象存在,但自己不持有对象
id obj1 = [obj0 object];
//取得对象存在,但自己不持有对象
[obj1 release];
//释放了非自己持有的对象!应用程序会崩溃
release 与autorelease
autorelease
autorelease即“自动释放”,是OC的一种内存自动回收机制,可以将一些临时变量通过自动释放池来回收统一释放。自动释放池销毁的时候,池子里面所有的对象都会做一次release操作
那么,autorelease释放与简单的release释放有什么区别呢?
调用 autorelease 方法,就会把该对象放到离自己最近的自动释放池中,即:使对象的持有权转移给了自动释放池,调用方拿到了对象,但这个对象还不被调用方所持有。当自动释放池销毁时,其中的所有的对象都会调用一次release操作。
本质上,区别在于autorelease 方法不会改变调用者的引用计数,它只是改变了对象释放时机,不再让程序员负责释放这个对象,而是交给自动释放池去处理 。
autorelease 方法相当于把调用者注册到 autoreleasepool 中,ARC环境下不能显式地调用 autorelease 方法和显式地创建 NSAutoreleasePool 对象,但可以使用@autoreleasepool { }块代替(并不代表块中所有内容都被注册到了自动释放池中)。
对于所有调用过autorelease实例方法的对象,在废弃NSAutoreleasePool对象时,都将调用release实例方法。
自动释放池使用场景
- 循环中创建了许多临时对象,在循环里使用自动释放池,用来减少高内存占用。
MRC和ARC
我们想要彻底理解属性关键字,那首先我们先来简单了解一下,MRC和ARC
MRC
MRC:Manul Reference Counting(手动引用计数)
需要手动管理内存,即手动添加release/retain等内存管理代码,否则,会造成内存泄露
涉及方法
- alloc/new/copy/mutableCopy:生成对象并自己持有,引用计数+1(从0变为1)
- retain :持有对象,使对象的引用计数加1
- release : 释放对象,使对象的引用计数减1
- retainCount : 获取当前对象的引用计数值
- autorelease : 当前对象会在autoreleasePool结束的时候,调用这个对象的release操作,进行引用计数减1
- dealloc : 在MRC中若调用dealloc,需要显示的调用[super dealloc],来释放父类的相关成员变量
关于retainCount
我们在MRC中,有时可能会想要打印引用计数,但retainCount方法并不是很有用,由于对象可能会处于自动释放池中,这会导致打印的引用计数并不精准,而且其他程序库也很有可能自行保留或释放对象,这都会扰乱引用计数的具体值。
查阅了一下官方的文档,第一句就是“Do not use this method.”,后面给出了说明,因为Autorelease pool的存在,对于内存的管理会相当复杂,retainCount就不能用作调试内存时的依据了
ARC
ARC:Automatical Reference Counting(自动引用计数)
iOS中的垃圾回收机制跟其它语言不同,iOS的内存管理机制是编译器(Xcode)帮我们添加内存管理的代码,不用像MRC那样,手动添加retain/release代码
- ARC进行自动内存管理的原则是:判断是否还有强指针指向当前对象
在非MRC模式下,不能调用release,retain,retainCount等方法,也无法在dealloc中调用[super dealloc]方法。
其次:在ARC下不要手动的为@autoreleasepool代码块内部对象添加autorelease,ARC会自动把@autoreleasepool代码块中创建的对象加入自动释放池中
修饰符
当ARC有效时,id类型和对象类型必须附加所有权修饰符,一共有如下四种。
- __strong
- __weak
- __unsafe_unretained(查阅资料的时候,听说这个好像不咋用了,不用考虑)
- __autoreleasing
strong
__strong修饰符是id类型和对象类型默认的所有权修饰符。
id obj = [[NSObject alloc] init];
//在没有明确指定所有权修饰符时,默认为__strong
id __strong obj = [[NSObject alloc] init];
不论调用哪种方法,强引用修饰的变量会持有该对象,如果已经持有则引用计数不会增加。
__strong修饰符表示对对象的强引用。持有强引用的变量在超出其作用域时被废弃,随着强引用的失效,引用的对象会随之释放。
__weak修饰符
弱引用表示并不持有对象,当所引用的对象销毁了,这个变量就自动设为nil。
MRC手动内存管理就是每一次对retain,alloc或者new的调用,就需要对其release或者autorelease.而ARC所做的无非就是在合适的时机帮我们release或者autorelease,无需程序员手动把这些代码敲上去,极大的节省了时间,并且避免因忘记释放造成的内存泄露。
在ARC中,所有实例变量和局部变量默认情况下都是strong类型,类似于MRC中的retain.只要某个对象被任一strong指针指向,那么它将不会被销毁,相反的,如果对象没有被任何strong类型的指针指向,那么就将被销毁. 与strong相对是weak,weak类型的指针可以指向对象,但是不会持有该对象,当对象被销毁,weak会自动指向nil。
关键字的分类
了解了引用计数之后再来看看这些属性关键字都有什么作用
属性关键字 分为三类
- 读写权限类型
- 原子类型
- 引用计数类型
读写权限类型readonly , readwrite
读写权限就是对成员是否只进行读或者读写
- readwrite 是可读可写特性;会自动生成getter方法和setter方法
- readonly 是只读特性 只会生成getter方法 ,不会生成setter方法
readwrite是系统默认的类型
对于readonly属性的变量进行修改操作时会报错,你可以访问,但是不能修改。
原子类型atomic , nonatomic
关于原子类型
原子性是指一个事物的操作是不可分割的,要么都发生,要么都不发生。
atomic- 默认线程安全
atomic(默认属性)原子属性,为setter方法加锁,线程安全的,效率相对低。
atomic描述的是属性赋值,属性赋值中还包含着很多其他操作,如访问对象,赋值等等,natomic是保证这个赋值的整个过程的完整性,并且不受其他线程的干扰,要么成功要么失败- 原子操作。
atomic仅仅保证了属性赋值的过程是安全并不保证对属性的读写操作是安全的
atomic对一个数组,进行赋值或获取,是可以保证线程安全的。但是如果进行数组进行操作,比如给数据加对象或移除对象,是不在atomic的保证范围。
nonatomic- 高效
nonatomic是非原子属性,线程不安全的,不会为属性的Setter方法加锁,效率高 -推荐使用
关于安不安全的方面现在还考虑不到,以后会加深学习的。
引用计数类型
- retain
retain只有在MRC的环境下使用
retain引起对象的引用计数加1, release引起引用计数减1,当引用计数为0时,dealloc函数被调用,内存被回收。 - Strong And Weak
Strong 表示了指向并且拥有该对象,其修饰的对象引用计数会加1.该对象只要引用计数不为0则不会被销毁。当然强制将其置为nil也可以销毁它。
关于这个关键字我的学习中还出了问题,由于没有用strong修饰,其被引用之后会被销毁,导致后续的函数无法识别该属性。
Weak表示了指向但不拥有该对象,其修饰的对象引用计数不变并且在程序结束的时候自行销毁,属性也会清空
copy:对象的属性声明,直接拷贝对象作为一个新的副本,而被拷贝的对象的引用计数不会加1,即两个对象分别指向不同的内存,只是内存中的数据相同。例如常用来修饰NSString 类型。
- assgin
assgin:修饰OC里基础的数据类型,int,BOOL,被修饰的对象引用计数不会发生改变,但是当assgin修饰的对象被销毁的时候该对象指向的指针指向之前的地址,成为悬垂指针,可能导致内存泄露和系统崩溃