2011-07-30 67 views
0

我是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

我會指出,'-directoryContentsAtPath:'已過時,用' - (NSArray的*)contentsOfDirectoryAtPath:(的NSString *)路徑誤差:(NSError **)error'代替。 –

回答

3

您的屬性上有retain關鍵字,這很好。但是,這並不重要,因爲你實際上並沒有使用它們。您正在直接訪問ivars,繞過了Objective-c編譯器爲您生成的getter方法。

要和伊娃與屬性之間的對比,看到這樣的代碼:

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 ... 
} 

要使用從你的類你會伊娃只需參考它的名稱,在你的例子中,你是這樣做的:

fileManager = [NSFileManager defaultManager]; 

由於您所調用的方法返回的自動釋放實例未被保留,因此稍後在程序中會收到EXEC_BAD_ACCESS異常。要使用你需要與所屬的對象引用前言它的屬性:

self.fileManager = [NSFileManager defaultManager]; 

這可以確保您的伊娃被設置保留。


編輯

現在,真正體會一個實例變量和屬性之間的區別,你可以宣佈你的接口:

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

@property (retain) NSFileManager * fileManager; 

而且在您的m你將有:

@synthesize fileManager = _fileManager。

現在,當你試圖做fileManager = [NSFileManager defaultManager];你的代碼不會編譯,因爲你的班級中沒有叫做fileManager的ivar。避免常見錯誤是一種有用的編碼風格。

+0

實例變量的首選方法是將下劃線作爲後綴添加,而不是作爲前綴。原因是,如果你繼承蘋果的類碰上踩着這可能會導致不可預測的結果超強的私有實例變量的潛在問題: '的NSFileManager * fileManager_;' –

+0

確定。因此,如果我創建實例變量爲「name_」,我的屬性爲「name」,那麼何時通過實例變量訪問該屬性? – user472292

0

您未分配到屬性,您正在分配給實例變量。如果您要使用這些屬性,則需要在self.前加上名稱,如self.fileManager

效果是,對象不被保留,並可能在稍後被釋放。當您稍後嘗試訪問屬性(或其實例變量)時,對象已經消失。有時在他們的位置有一個不同的對象,有時候是垃圾。那就是當你發生崩潰的時候。

1

@Perception已經解釋了爲什麼這是行不通的。這是如何糾正這一點。由於您沒有使用將發送保留消息的合成設置器,所以在直接訪問實例變量時,您必須自己處理它。還請注意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; 
} 
0

我不想踩腳趾的任何 - @Perception提供了詳盡的解釋,然後@馬克給了實際的解決方案。

這裏的快速版本:

@property (nonatomic, copy) NSString* appFolderPath; 

和匹配

@synthesize appFolderPath = _appFolderPath; 

意味着編譯器生成以下方法

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

這些甲基根據您選擇的選項retain/copy/assign,ods將負責保留/複製/引用分配對象的內存管理。這個內存管理只會生效如果使用

[self setAppFolderPath:@"My Folder Path"]; 

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

現在,這是一個好你的class好用於一般用途,但有時它是建議不要使用getter和setter以防他們造成副作用。兩個你通常想要避開吸氣劑和吸附劑的地方在init方法和dealloc

因此,當你在init方法中,你應該直接訪問伊娃而不使用getters/setters。這意味着你將需要自己執行內存管理。例如

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

dealloc方法,你能直接再次訪問伊娃這樣

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

正如@馬克指出你一般copy字符串,使他們不能,你下進行更改。

有關示例的其他註釋。

當我打電話給@synthesize appFolderPath = _appFolderPath;它使用名稱appFolderPath創建獲取者和設置者,但是這些方法生效的ivar實際上被稱爲_appFolderPath。這是一個很好的做法,因爲當您直接訪問ivar時或者通過getter/setter訪問ivar時,您將始終確信,因爲在代碼中引用appFolderPath不會編譯。

相關問題