iphone分配实例变量

时间:2009-06-14 19:44:55

标签: iphone objective-c cocoa

我花了最后3个小时试图找出这个错误。我希望有人向我解释,所以我不再这样做了。

我在不使用“self”的情况下分配了一个NSString实例变量。当类(“self”)发布时,我收到了“错误访问”错误。我在另一个具有相同变量声明的类中完成了同样的事情并且没有此错误。以下是我的代码。我注释掉了破坏的线,下面的线修复了它。但我不明白为什么......请注意,还有其他实例变量不会导致此问题。在分配实例变量时,我是否应始终使用“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 ];
}

3 个答案:

答案 0 :(得分:5)

原因

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

很好,因为你保留变量。由于您没有分配字符串,而是在stringWithUTF8String上调用NSString,因此您不会获取变量的所有权,因此返回给您的字符串是自动释放的。但是,由于你保留它,这不会导致问题。

如果变量返回自动释放,则在自动释放池耗尽时释放它们,这发生在每个事件结束时(请参阅autorelease pools上的更多信息)。这对于实例变量来说并不好,因为它需要在当前事件之后保持不变。

通过以下方式分配变量时:

NoteText = [NSString stringWithUTF8String: columnText];

您的setter方法未被调用,因此返回的字符串(同样是自动释放的)不会被保留,因此在事件结束时由自动释放池释放。

将其称为:

self.NoteText = [NSString stringWithUTF8String: columnText];

保留字符串,因为该行是另一种写作方式:

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

调用你的setter方法并保留变量,防止它在当前事件结束时被释放。

答案 1 :(得分:4)

从类方法中分配NoteText不会调用合成的setter方法,它会直接设置实例变量。这意味着您的字符串不会被保留(或者在您的setter的情况下被复制),并且崩溃是在尝试释放已经释放的对象时。这样做:

self.NoteText = [NSString stringWithUTF8String: columnText ];

这会打电话给你的二传手,一切都会好的。

修改: 需要明确的是,所有伊娃都是如此。

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

答案 2 :(得分:0)

ivar和setter方法之间的这种混淆是为什么我永远不会将我的定位器和我的ivars命名为同样的原因。

Apple通常将其ivars命名为以下划线(_)开头,例如 NoteText。在我的情况下,我已经为ivars设置了i 的前缀。例如:

NSString* i_name;

@property (nonatomic, copy) NSString* name;

@synthesize name = i_name;

通过这种方式,您可以轻松区分ivar分配:

i_name = [[whatever title] retain];

和setter方法调用

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

setter,因为它是用copy(或类似的retain)定义的,它将获取传入的变量的所有权并释放旧值的所有权。 ivar的任务没有做到这一点。

另请注意,您的名字应以小写字母开头,或者不是KVO compliant