合成存取器中copy和retain的区别

前言:

最近在学OC内存管理,想到了一个问题:如果使用@property来声明的copy和retain都可以实现引用计数管理,那么两者有什么区别?
然后上网找了找,看了一些说法,可是没有什么比自己动手解决来的透彻,自己写了个小demo,然后通过这个小demo记录一下二者的区别,我讨论的只是我懂的,如有谬误可以发邮件给我>>>>@975215414@qq.com
要从OC中的深拷贝和浅拷贝说起,浅拷贝是简单的使得当前引用指向被赋值对象,深拷贝是重新分配内存产生一个和被赋值对象相同类型的新对象,当然两者地址也是不同的,然后将该对象的属性赋给新对象.但是在OC中想要实现深拷贝需要被赋值对象的类型实现NSCopying协议,重写copyWithZone方法。使用深拷贝时候要调用被赋值对象的copy方法,回调的就是copyWithZone方法。如果只言片语大家理解不了的话,可以自行搜索OC中的深浅拷贝。这不是本文讨论重点。
那么,@property中的copy属性的意思是,该属性自动生成的setter方法的形式是这样的:
-(void)setMyDog:(Dog*)dog{
           if(_myDog!=dog){//并非重复赋同一个值
              [_myDog release];//旧值引用计数-1
              _myDog=[dog copy];//copy一个新对象,并且新对象的retainCount值为1
           }  
    }

而@property的retain属性的意思是,该属性自动生成的setter方法的形式是这样的:

-(void)setMyDog:(Dog*)dog{
          if(_myDog!=dog){//同上
              [_myDog release];//旧的值引用-1
              _myDog=[dog retain];//新的值引用+1,表示所有权
          }
}

所以区别就在于最后一个调用copy来实现深拷贝,一个调用retain简单实现计数+1.
好了,是不是感觉很简单,容易理解呢?
先别急,这样还远远不够!如果你写一个简单的demo之后就会发现,对于不可变的对象类型,copy和ratain是没有差别的,并不会实现深拷贝,原因在于:不可变的类型比如NSString,一旦赋值过后就不可以再改变内容,所以使用深拷贝的意义就不大了,因为深拷贝就是为了防止一处改变内容导致另一处的改变。只有对于可变的类型,如NSMutableString,copy才会实现深拷贝,和retain才有区别。对于自定义的类型,如我们这里的Dog对象,如果在Dog类里面实现了NSCopying协议或者NSMutableCopying协议的话,copy会实现深拷贝.否则程序会运行崩溃,并且提示你没有实现copy方法。
所以,综上,当满足两个条件时候,copy实现深拷贝,从而和retain有所区分:
1)属性类型是可变的
2)属性类型是自定义的,并且该属性类型实现了NSCopying协议或者NSMutableCopying协议。

Demo:

Dog.h:
#import <Foundation/Foundation.h>

@interface Dog : NSObject<NSCopying>
@property(nonatomic,copy)NSString *dogName_copy;//默认是atomic,为了执行效率的提升,使用property时候要显式声明为nonatomic,先用copy试试这样默认产生的setter是否会产生一个新的地址
@property(nonatomic,retain)NSString *dogName_retain;//默认是atomic,同样我们如果不是在多线程的程序中存取该属性,就要在@property中将该属性显式声明为nonatomic,再用retain试试这样默认产生的setter是否会产生一个新的地址,还有引用计数是怎样的
@end
Dog.m:
#import "Dog.h"

@implementation Dog
//实现NSCopying协议中的方法
-(id)copyWithZone:(NSZone *)zone{
    Dog *dog=[[Dog alloc] init];
    dog.dogName_copy=_dogName_copy;
    dog.dogName_retain=_dogName_retain;
    return dog;
}
//析构
-(void)dealloc{
    NSLog(@"dog 被dealloc...");
    [super dealloc];
}
@end
Person.h
#import <Foundation/Foundation.h>
#import "Dog.h"
@interface Person : NSObject
@property(nonatomic,copy)Dog *dogCopy;//使用copy属性的dog成员
@property(nonatomic,retain)Dog *dogRetain;//使用retain属性的dog成员
@end
Person.m
#import "Person.h"
@implementation Person
//析构
-(void)dealloc{
    NSLog(@"person 被dealloc...");
    [_dogCopy release];//释放dogCopy
    [_dogRetain release];//释放dogRetain
    [super dealloc];
}
@end
main.m
//
//  main.m
//  OC-copy和retain区别
//
//  Created by mac on 15/10/15.
//  Copyright (c) 2015年 macb. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Dog.h"
#import "Person.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
//        创建dog
        Dog *dog=[[Dog alloc] init];//1
        NSMutableString *str=[[NSMutableString alloc] initWithString:@"苏联红"];
        dog.dogName_copy=str;
        dog.dogName_retain=str;
        NSLog(@"str:%p,copy_dog:%p,retain_dog:%p",str,dog.dogName_copy,dog.dogName_retain);
//        创建person
        Person *person=[[Person alloc] init];//1
//        赋值
        person.dogCopy=dog;
        person.dogRetain=dog;
//
        [dog release];
//        打印地址
        NSLog(@"dog:%p",dog);
        NSLog(@"person.copyDog:%p",person.dogCopy);
        NSLog(@"person.retainDog:%p",person.dogRetain);
//        打印计数
        NSLog(@"retainCount-dog:%ld",dog.retainCount);
        NSLog(@"retainCount-copyDog:%ld",person.dogCopy.retainCount);
        NSLog(@"retainCount-retainDog:%ld",person.dogRetain.retainCount);
        [person release];
    }
    return 0;
}

运行结果:

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值