获取在构造函数中设置的属性

时间:2011-07-30 20:24:36

标签: iphone objective-c ios

我是Objective-C的新手。我试图在构造函数中获取属性集,但是我收到了EXC_BAD_ACCESS错误。这是我的构造函数:

- (id) init {
    self = [super init];
if (self != nil) {
    appFolderPath = [[NSBundle mainBundle] resourcePath];
    fileManager = [NSFileManager defaultManager]; 
    mediaArray = [fileManager directoryContentsAtPath: [appFolderPath stringByAppendingString:@"/Media/Silly"]];    
    mediaIndex = 0;
}
return self;

}

以下是我的属性:

@property (retain) NSFileManager* fileManager;
@property (retain) NSString* appFolderPath;
@property int mediaIndex;
@property (retain) NSArray* mediaArray;

有什么想法吗?

4 个答案:

答案 0 :(得分:4)

您的属性上有retain关键字,这很好。但是,它并不重要,因为你实际上并没有使用它们。您正在直接访问 ivars ,绕过Objective-c编译器为您生成的getter方法。

要比较和ivar和属性,请参阅以下代码:

MyInterface.h

@interface MyInterface : NSObject {
@private
    NSFileManager * fileManager; // This is an instance variable, or 'ivar'
}

@property (retain) NSFileManager * fileManager; // This is the declaration of a property

MyInterface.m

@implementation MyInterface {

@synthesize fileManager;

// Calling this causes the Objective-C compiler to generate the following
// methods for you: 
//  -(NSFileManager *) getFileManager ...
// and - (void) setFileManager: (NSFileManager *) val ...
}

要使用您班级中的ivar,您只需引用其名称,在您的示例中,您使用该行:

fileManager = [NSFileManager defaultManager];

由于您调用的方法返回的自动释放实例未被保留,因此稍后会在程序中收到EXEC_BAD_ACCESS异常。要使用该属性,您需要在其前面加上拥有对象引用:

self.fileManager = [NSFileManager defaultManager];

这可确保您的ivar设置为


修改

现在,要真正了解实例变量和属性之间的区别,您可以将接口声明为:

@interface MyInterface : NSObject {
@private
    NSFileManager * _fileManager; 
}

@property (retain) NSFileManager * fileManager;

在你的.m中你会有:

@synthesize fileManager = _fileManager。

现在,当您尝试执行fileManager = [NSFileManager defaultManager];时,您的代码将无法编译,因为您的班级中没有名为fileManager的ivar。这是避免常见错误的有用编码风格。

答案 1 :(得分:1)

@Perception已经解释了为什么这不起作用的细节。以下是如何纠正这一点。由于您没有使用会发送保留消息的合成setter,因此在直接访问实例变量时您将被迫自己处理它。另请注意,应复制NSString类型的对象而不是保留,因此appFolderPath的声明实际上应为@property (nonatomic, copy) NSString *appFolderPath;。考虑到所有这些因素,您的-init应该看起来像这样。

- (id)init
{
    self = [super init];

    if (self != nil)
    {
        appFolderPath = [[NSBundle mainBundle] resourcePath] copy];
        fileManager = [NSFileManager defaultManager] retain]; 
        mediaArray = [fileManager directoryContentsAtPath:[appFolderPath stringByAppendingString:@"/Media/Silly"]] retain];    
        mediaIndex = 0;
    }

    return self;
}

答案 2 :(得分:0)

您没有分配属性,而是分配给实例变量。如果您想使用这些属性,则需要在self.之后使用self.fileManager作为名称的前缀。

效果是不保留对象,可能会在以后取消分配。然后,当您稍后尝试访问属性(或其实例变量)时,对象已经消失。有时在他们的位置有一个不同的对象,有时它是垃圾。当你遇到崩溃时就是这样。

答案 3 :(得分:0)

我不想踩任何脚趾 - @Perception提供了彻底的解释,然后@Mark提供了实际的解决方案。

以下是快速版:

@property (nonatomic, copy) NSString* appFolderPath;

和匹配

@synthesize appFolderPath = _appFolderPath;

表示编译器生成以下方法

- (void)setAppFolderPath(NSString *)appFolderPath;
- (NSString *)appFolderPath;

这些方法将根据您选择的retain/copy/assign选项,负责保留/复制/引用分配对象的内存管理。只有使用

,此内存管理才会生效
[self setAppFolderPath:@"My Folder Path"];

self.appFolderPath = @"My Folder Path"; // which compiles to the previous call

现在这对于class中的一般用途来说一切都很好,但有时建议不要使用getter和setter以防它们引起副作用。您通常希望避免使用getter和setter的两个地方位于init方法和dealloc

因此,当您使用init方法时,您应该直接访问ivar而不使用getter / setter。这意味着您需要自己执行内存管理。 e.g。

- (id)init
{
    self = [super init];
    if (self) {
      _appFolderPath = [[[NSBundle mainBundle] resourcePath] copy];
    }
    return self;
}

dealloc方法中,您可以像这样直接再次访问ivar

- (void)dealloc
{
    [_appFolderPath release];
    [super dealloc];
}

由于@Mark通常会指出copy个字符串,因此无法在您下面更改它们。

关于例子的补充说明。

当我调用@synthesize appFolderPath = _appFolderPath;时,它使用名称appFolderPath创建getter和setter,但是ivar这些方法效果实际上称为_appFolderPath。这是一种很好的做法,因为当您直接或通过getter / setter访问ivar时,您总是可以确定,因为只是在代码中引用appFolderPath将无法编译。