OC中关键字的介绍及原理分析

关于OC中的关键字,是平时使用离不开的,也是面试官最喜欢问的,不管是出于那种考虑吧,作为一个ios开发的程序员,都必须完全理解透彻。本文旨在罗列分析和对比这些关键字。copy,assign,strong,retain,weak,readonly,nonatomic

首先明确一些概念:

野指针野指针就是指向垃圾内存的指针,这个指针地址不是NULL。指向的对象被释放了,但是这个指针还没有置为nil,再次访问指向的值的时候就会出现不可预期的错误

僵尸指针“僵尸指针”就是野指针的一种情况,即该指针指向的对象已经被释放,但是却没有对当前指针赋值为nil。

僵尸对象:简单的来说,僵尸对象是已经被释放的对象。如果在程序中再度使用该对象,一般会出现报错:unrecognized selector sent to instance

内存泄漏如果程序运行时一直分配内存而不及时释放无用的内存,程序占用的内存越来越大,直到把系统分配给该APP的内存消耗殚尽,程序因无内存可用导致崩溃,这样的情况我们称之为内存泄漏

property的属性默认是:readwrite,assign, atomic

assign修饰基本数据类型,(NSInteger,CGFloat)和C数据类型(int,  float, double, char等),简单赋值,不更改引用计数。

@property (nonatomic, assign) int number;  
@property (nonatomic, assign) id className;//id必须用assign 

weak只是简单的指向某个对象(虚线指向),引用计数不会加一。声明为weak的指针,指针指向的地址一旦被释放,这些指针都将被赋值为nil。这样的好处能有效的防止野指针

retain:在非ARC时代,你需要自己retain一个想要保持的对象(保其命),ARC环境下就不需要了。现在唯一要做的就是用一个指针指向这个对象,只要指针没有被重置为空,对象就会一直在堆上。当指针指向新值的时候,原来的对象就会被release一次

strong:在ARC环境下,只要某一对象被一个strong指针指向,该对象就不会被销毁。如果对象没有被任何strong指针指向,那么就会被销毁。在默认情况下,所有的实例变量和局部变量都是strong类型的。可以说strong类型的指针在行为上跟非ARC下得retain是比较相似的。在ARC环境下,strong代替了retain,所以现在一般都是些strong。

copy:建一个 对象,并且指向他,这个指向的功能和strong一样,强引用。

那么strong和copy的区别是什么?为什么需要copy这个关键字呢?下面娓娓道来

首先理解strong的意义就是直接给指向的对象引用计数加1,而copy不是,他是另外开辟了一块新的内存空间,把赋值过来的内容复制到这个新的空间上,并且强引用他,那么这么做有什么意义呢?那就要从copy的意义说起了,在OC中,引进copy关键字的目的就是在改变备份的时候,不要影响到原始对象的值。仔细理解这句话的意义,就可以知道copy的意义了。

代码示例

还是结合代码来说明这个情况

@interface Person : NSObject
@property (strong, nonatomic) NSArray *bookArray1;
@property (copy, nonatomic) NSArray *bookArray2;
@end

@implementation Person
//省略setter方法
@end

//Person调用
main(){
    NSMutableArray *books = [@[@"book1"] mutableCopy];
    Person *person = [[Person alloc] init];
    person.bookArray1 = books;
    person.bookArray2 = books;
    [books addObject:@"book2"];
    NSLog(@"bookArray1:%@",person.bookArray1);
    NSLog(@"bookArray2:%@",person.bookArray2);
}

我们看到,使用strong修饰的person.bookArray1输出是[book1,book2],而使用copy修饰的person.bookArray2输出是[book1]。这下可以看出来区别了吧。

备注:使用strong,则person.bookArray1与可变数组books指向同一块内存区域,books内容改变,导致person.bookArray1的内容改变,因为两者是同一个东西;而使用copy,person.bookArray2在赋值之前,将books内容复制,创建一个新的内存区域,所以两者不是一回事,books的改变不会导致person.bookArray2的改变。

其实出现上面的不同的根本原因是在c语言中,父类对象可以接受子类对象,例如可以把一个NSMutableArray赋值给一个NSArray类型的指针,(而反过来是不行的,因为加入把父类赋值给一个子类的时候,外部不知道这个子类真是的类型,调用子类的方法的时候,其实父类是不一定有这个方法的,那就会出现经典的crash,unRecognised seclector send to instance)这样的话当

改变NSMutableArray的时候,那么NSArray的指针指向的内容也变了(但是我们希望的是不会改变),因为他们指向的是同一块内存,同一个对象。

本质原因:说到底,其实就是不同的修饰符,对应不同的setter方法,(先release旧值)
1. strong对应的setter方法,是将_property先release(_property release),然后将参数retain(property retain),最后是_property = property。
2. copy对应的setter方法,是将_property先release(_property release),然后拷贝参数内容(property copy),创建一块新的内存地址,最后_property = property。

那么既然差别这么大,那么怎么很多人还是说其实copy和strong可以混用呢?不用关心太多?其实这个就是经常说的深复制和浅复制的区别。

深复制:复制内容,直接新开辟一块内存空间,把值赋值过去。

浅复制:指针复制,直接指向传过来的对象。

为什么会有这个深复制和浅复制呢?就是因为当把一个不可变的对象copy一次时,因为他本身就是不可变的,所以不用担心他的改变会影响原始对象的问题,那么系统为了节约内存空间,就直接来了一次指针赋值,也就是浅复制,这个时候其实就是和strong完全一样的效果,但是如果是把一个可变的对象进行copy的时候,因为是可以改变的,那就必须要重新开辟内存空间来处理了,这就是深复制。所以为了安全起见,类似NSString(NSMutablString),NSArray(NSMutableArray),NSDictionary(NSMutableDictionary)这种就直接全部用copy比较安全,但是如果完全理解其中的内部原理,那就选择适当的关键字来修饰了。

同时,也就不难理解为什么声明一个NSMutableArray时,如果用copy声明,那么后面调用addObject或者removeObject时,就会出现找不到方法的crash,因为当你创建数组时,self.array = [NSMutableArrry array];,这句话其实就是在调用他的set方法,根据上面说copy修饰的setter方法的实现,就会新开辟一个内存空间,把参数copy一份传过去,此时self.array其实是一个NSArray的拷贝,当然就找不到那些子类的方法了,但是如果是strong修饰的,那就是直接指向,也就是可变的数组,相应的方法就可以顺利调用了。(其实这里还是有点懵逼,[NSMutableArrry array]不是应该得到一个可变的数组吗?子类调用应该生成子类的对象啊,为毛copy出来的就是父类的对象呢???

解释上面的懵逼:其实懵逼的原因是因为两个概念没搞清楚,第一:上面一直说的拷贝和副本,不要简单的理解成拷贝文件那样,这个副本是要看得到的结果的。可能这样说不说很明白,下面的代码解析会很清晰。第二:copy这个方法,只要调用就会产生一个不可变的结果,不管是谁调的。先说到这里,直接上代码,后面再总结:其实

@property (nonatomic, copy) NSMutableArray *array;

self.array =  [NSMutableArray array];

的真正的意义是

@property (nonatomic, strong) NSMutableArray *array;

NSMutableArray *b = [NSMutableArray array];

self.array = [b copy];//注意了,问题就出在这里,这里其实等号右边的结果是一个不可变的,赋值给了self.array 

这个问题出现的原因首先就是copy方法没有完全理解透彻,其实创建一个副本赋值给这个属性,这个副本不是就是简单的像拷贝文件一样产生一个一模一样的,他其实是调用的copy方法,这个时候第二个问题就来了,要明白调用copy方法得到的就是不可变的,原则:不管谁调用的,只有调用了copy方法就得到了一个不可变的副本,不管是是谁调用mutableCopy得到的就是一个可变的副本,和谁调用的没有关系,只和调用的方法有关系,注意理解这个副本不是仅仅就像拷贝一个文件一样,一毛一样的,这个要看调用产生的结果的!!!!!!!!!!!!!!!!!!!!!!!!!

至此,上面的懵逼也就烟消云散了,关键问题出在对copy的误解,而且对copy修饰的set方法的误解。这下扯淡透彻了!!!

readonly:表示这个属性是只读的,就是只生成getter方法,不会生成setter方法

readwrite:此标记说明属性会被当成读写的,这也是默认属性。存取器方法都需要在@implementation中实现(已经声明了)。如果使用@synthesize关键字,存取器方法都会被自动创建

nonatomic指出访问器不是原子操作,而默认地,访问器是原子操作。实质上来说,应该考虑线程安全的,但是由于是移动端的开发,考虑到性能开销等,还是声明为nonatomic。因为开发中的很多方法和系统内部的设计,都是线程安全的。

unsafe_unretained:unsafe_unretained 就是ios5版本以下的 assign ,也就是 unsafe_unretained , weak, assign 三个效果都是一个样的。但是他是不安全的,指向的对象销毁后不会自动设置为nil,所以先基本上很少使用,直接使用weak就行了。 

synthesize和dynamic@property有两个对应的词,一个是@synthesize,一个是@dynamic。如果@synthesize和@dynamic都没写,那么默认的就是@syntheszie var = _var;

@synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法

@dynamic诉编译器,属性的settergetter方法由用自己实现,不自生成。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值