为什么类对象是retain的属性属性而不是copy?

时间:2017-02-20 11:02:31

标签: ios objective-c memory-management

我试图将自定义对象传递给下一个视图控制器,我遇到了这个错误-[ClassName copyWithZone:] unrecognized selector sent to instance

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

    if ([segue.identifier isEqualToString:@"attemptDetails"])
    {
        ResultsVC *vc = segue.destinationViewController;
        vc.selectedEntry = selectedEntry;
    }
}

@property (nonatomic, retain) ClassName *selectedEntry; //Why is it retain and not copy?

我仍然对属性属性以及为什么某些类型使用某些属性非常困惑,例如NSString使用(nonatomic, copy)而CLLocationCoordinate2D使用(nonatomic, readonly)

有人可以向我解释或链接每个属性属性的工作原理吗?非常感谢!

3 个答案:

答案 0 :(得分:1)

有很多关于属性属性解释的描述,

参考链接,

Objective-C ARC: strong vs retain and weak vs assign

https://stackoverflow.com/a/4511004/4294543

@property and retain, assign, copy, nonatomic in Objective-C

简短&我理解很简单,

保留:它正在处理创建的对象,它只会增加引用次数。

  • 在你的情况下,你已经有了模型类对象,所以不需要在第二个vc属性中复制,你只需要将它保留到第二个vc属性。

复制:您可以复制分配给属性的值&也用于其他目的(创建对象的浅拷贝,当对象可变时需要,并且需要在完成后释放)。

非原子:线程访问速度更快但您无法同时访问&改变你的财产。

readonly :您无法直接为该属性分配新值。

即使我已经在我的项目中运行了你的案例,

#import "ViewController.h"
#import "TestViewController.h"
#import "CustomClass.h"
@interface ViewController (){

    CustomClass *classT;
}

@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    classT = [[CustomClass alloc]init];
    classT.test = YES;

}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
- (IBAction)btn:(id)sender {
    TestViewController * vc = [self.storyboard instantiateViewControllerWithIdentifier:@"TestViewController"];
    vc.className = classT;
    [self presentViewController:vc animated:YES completion:nil];
}
@end




#import <UIKit/UIKit.h>
#import "CustomClass.h"

@interface TestViewController : UIViewController


@property (nonatomic,retain) CustomClass *className; // Work as i said

//@property (nonatomic,copy) CustomClass *className; // Makes a copy of an object, and returns it with retain count of 1. If you copy an object, you own the copy. This applies to any method that contains the word copy where “copy” refers to the object being returned thats why here you will get crash


@end

答案 1 :(得分:1)

我已经阅读了几篇关于内存管理的好文章。根据{{​​3}}

保留属性:retain属性是strong的手动保留发布版本,它具有完全相同的效果:声明所分配值的所有权。您不应在自动参考计数环境中使用它。

复制属性:复制属性是strong的替代选项。它不是取得现有对象的所有权,而是创建您分配给该属性的任何内容的副本,然后获取该属性的所有权。只有符合NSCopying协议的对象才能使用此属性。

即使我也经历了一些很好的stackoverflow链接。 rypress为保留与复制提供了很好的解释。

保留与复制 - 声明的属性默认使用retain(因此您可以完全省略它)并自动管理对象的引用计数,无论是将另一个对象分配给属性还是将其设置为零;使用copy自动发送新分配的对象a -copy消息(它将创建传递的对象的副本并将该副本分配给属性 - 在某些情况下有用(甚至是必需的),在这种情况下,被分配的对象可能被修改后设置为某个其他对象的属性(这意味着修改/变异也将适用于该属性)。

还找到了很好的例子Joshua Nozzi's answer

代码:

NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:@"First",@"Second", nil];
    NSMutableArray *copiedArray = [array mutableCopy];
    NSMutableArray *retainedArray = [array retain];

    [retainedArray addObject:@"Retained Third"];
    [copiedArray addObject:@"Copied Third"];

    NSLog(@"array = %@",array);
    NSLog(@"Retained Array = %@",retainedArray);
    NSLog(@"Copied Array = %@",copiedArray);

输出:

array = (
    First,
    Second,
    "Retained Third"
)
2013-12-19 17:15:49.380 RetainVsCopy[2876:c07] Retained Array = (
    First,
    Second,
    "Retained Third"
)
2013-12-19 17:15:49.381 RetainVsCopy[2876:c07] Copied Array = (
    First,
    Second,
    "Copied Third"
)

请参阅,阵列和保留阵列都具有相同的内容。这是因为两者都指向相同的内存/实例/对象。复制数组的内容不同。这是因为copy创建了一个单独的实例。

答案 2 :(得分:0)

在Objective C中,您会发现每个类实际上都有一个结构。属性是快捷方式,可以在结构,getter和setter中创建值。例如:

@interface MyClass

@property id myValue;

@end

将创建:

@interface MyClass {
   id _myValue;
}

@property id myValue;

@end

@implementation 

- (id)myValue {
    return _myValue;
}
- (void)setMyValue:(id)myValue {
    _myValue = myValue;
}

@end

现在,retaincopy这些标志为setter和getter添加了额外的逻辑。使用copy实际上会创建一个setter:

- (void)setMyValue:(id)myValue {
    _myValue = [myValue copy];
}

这意味着该值必须实现copy方法。由于你的对象没有崩溃。

为什么使用副本是为了安全。这对于作为字符串的东西来说很少重要,但它对于类似数组的东西很重要。因此,例如,您创建了一个属性@property NSArray *myArray;,它需要一个不可变的数组,但问题是您也可以设置一个可变数组:myClassInstance.myArray = [[NSMutableArray alloc] init];。现在,2个模块可以访问相同的可变数组。因此,如果第一个对象开始修改数组而另一个期望数组始终相同,则可能会发现一些问题。例如MyClass实例可以将它用作表视图的数据源,并且在某些时候数组被突变但是没有添加/删除单元格,并且表视图将导致崩溃。

老实说,您可以将所有这些保留为默认值,并仅在您确实需要时进行修改。无论如何,上述情况极不可能。