2009-06-14 112 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