我是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;
有什么想法吗?
答案 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
将无法编译。