2009-06-14 109 views
1

我剛剛花了最後3個小時試圖找出這個錯誤。我希望有人向我解釋,所以我不再做。iphone分配實例變量

我分配了一個NSString實例變量而不使用「self」。當類(「自我」)發佈時,我收到了「訪問不良」錯誤。我已經在另一個具有相同變量聲明的類中做了完全相同的事情,並且沒有這個錯誤。以下是我的代碼。我評論了這條線斷了,下面的線修復了它。但我不明白爲什麼......請注意,還有其他實例變量不會導致此問題。我應該在分配實例變量時總是使用「自我」保留字嗎?請告訴我。

聲明

@property (nonatomic, readonly, assign) int IID; 
@property (nonatomic, assign) int ProfileIID; 
@property (nonatomic, retain) NSDate *NoteDate; 
@property (nonatomic, copy) NSString *NoteText; 

代碼段

// the default date format is Year-Month-Day 
NSDateFormatter *df = [[[NSDateFormatter alloc] init] autorelease]; 
[df setDateFormat:kDateFormat]; 

IID = sqlite3_column_int(selectstmt, 0); 
ProfileIID = sqlite3_column_int(selectstmt, 1); 

// notice this does not cause a memory error 
NoteDate = [[df dateFromString: [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 2)]] retain];  

// the following may be NULL. Checking using the sqlite3_column_text method 
const char *columnText = (const char *)sqlite3_column_text(selectstmt, 3); 
if(columnText != NULL) 
{ 
    // this causes a memory error 
    //NoteText = [NSString stringWithUTF8String: columnText ]; 
    // this does not cause memory error 
    self.NoteText = [NSString stringWithUTF8String: columnText ]; 
} 

回答

5

之所以

NoteDate = [[df dateFromString: [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 2)]] retain]; 

是好的,因爲你保留變量。由於您沒有分配字符串,但在NSString上調用stringWithUTF8String,則不會獲得該變量的所有權,因此返回給您的字符串是自動釋放的。但是,由於您保留它,這不會導致問題。

如果變量返回自動釋放,那麼它們在自動釋放池耗盡時釋放,這會在每個事件結束時發生(詳見autorelease pools)。這對於一個實例變量來說並不好,因爲它需要在當前事件之後繼續執行。

當您通過指定變量:

NoteText = [NSString stringWithUTF8String: columnText]; 

,則不會調用你的setter方法,所以返回的字符串(再次,會被自動釋放)不保留,因此被自動釋放池在發佈事件的結束。

調用它爲:

self.NoteText = [NSString stringWithUTF8String: columnText]; 

確實保留字符串,因爲該行是寫作的另一種方式:它調用你的setter方法並保留變量,防止它

[self setNoteText:[NSString stringWithUTF8String: columnText]]; 

從當前事件結束時被釋放。

4

從一個類的方法中分配NoteText不調用合成setter方法,它直接設置實例變量。這意味着你的字符串沒有被保留(或者在setter的情況下被複制),並且當試圖釋放已經釋放的對象時崩潰。做到這一點:

self.NoteText = [NSString stringWithUTF8String: columnText ]; 

這將召喚你的二傳手,一切都會好的。

編輯: 要說清楚的是,所有ivars都是如此。

myVariable = someValue; // Sets the ivar directly. 
self.myVariable = someValue; // calls [self setMyVariable]. 
+0

該死的,我只是輸入了這個xD Nice的迴應。 – 2009-06-14 19:53:58

0

ivar和setter方法之間的這種混淆是爲什麼我永遠不會將我的setter和我的ivars命名爲相同的。

Apple通常將其ivars以下劃線(_)開頭,例如NoteText。就我而言,我已經採取了ivars的前綴i。例如:

NSString* i_name; 

@property (nonatomic, copy) NSString* name; 

@synthesize name = i_name; 

這樣,你可以很容易地分辨伊娃分配之間的差別:

i_name = [[whatever title] retain]; 

和setter方法調用

self.name = [whatever title]; // Equivalent to [self setName:[whatever title] 

setter方法,因爲它與定義複製(或類似的保留)將取得傳入的變量的所有權,並釋放舊值的所有權。伊娃的任務完全沒有。

還要注意,你的私人名字應該以小寫字母開頭,否則他們不會是KVO compliant

+0

蘋果使用領先的下劃線(用於ivars和私有方法)以避免衝突,例如第三方將類別添加到Apple代碼時。 (事實上​​,蘋果*保留*領先_供自己使用。)但是,這個習慣用法是,你應該*將ivars命名爲與該屬性相同的ivars。實際上,在64位運行時中,您可以聲明一個屬性*,而不需要*匹配的ivar。如果屬性的點語法引起混淆,我建議顯式使用setter方法,而不是重命名ivars。當一個方法被調用時,這更加明顯,它可以幫助解決手頭的問題。 – 2009-06-15 04:21:31