为Objective-C数据对象分配内存的问题

时间:2011-04-15 00:33:46

标签: iphone objective-c cocoa

我已经编程了几个月的Objective-C并且到目前为止做得很好而不必发布任何问题。这将是我的第一次。问题是我在其中一个方法中从数据对象收到内存泄漏警告。我可以看到问题是我在不释放它的情况下向它发送一个alloc,但是我不知道如何让它在内存中保留对象。如果我取出分配,程序崩溃。如果我留下它,它会泄漏记忆。这是有问题的方法:

+ (id) featureWithID:(int)fID name:(NSString*)fName secure:(int)fSecure {
Feature *newFeature = [[self alloc] init];
newFeature.featureID = fID;
newFeature.featureName = fName;
newFeature.featureSecure = fSecure;

return [newFeature autorelease];

}

此方法由我的视图控制器中的另一个方法调用。这种方法如下:

+ (NSMutableArray*) createFeatureArray {

NSString *sqlString = @"select id, name, secure from features";
NSString *file = [[NSBundle mainBundle] pathForResource:@"productname" ofType:@"db"];
sqlite3 *database = NULL;
NSMutableArray *returnArray = [NSMutableArray array];

if(sqlite3_open([file UTF8String], &database) == SQLITE_OK) {

    const char *sqlStatement = [sqlString UTF8String];
    sqlite3_stmt *compiledStatement;

    if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {

        while(sqlite3_step(compiledStatement) == SQLITE_ROW) {

            Feature *myFeature = [Feature featureWithID:sqlite3_column_int(compiledStatement,0) 
                                                   name:[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)]
                                                 secure:sqlite3_column_int(compiledStatement,2)];

            [returnArray addObject:myFeature];

        }
    }
    // Release the compiled statement from memory
    sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
return returnArray;

}

我尝试了几个方法,比如创建一个featureWithFeature类方法,这个方法允许我在调用方法中为init分配功能,但这也会使程序崩溃。

如果您需要任何澄清或代码的任何其他部分,请告知我们。提前感谢您的帮助。

更新:2011年4月14日

在阅读前两个回复后,我实施了该建议,发现该程序现在崩溃了。关于如何追查罪魁祸首我完全不知所措。希望这有帮助,我也从视图控制器发布调用方法:

- (void)setUpNavigationButtons {
// get array of features from feature data controller object
NSArray *featureArray = [FeatureController createFeatureArray];
int i = 0;


for (i = 0; i < [featureArray count]; i++) {
    Feature *myFeature = [featureArray objectAtIndex:i];
    CGRect buttonRect = [self makeFeatureButtonFrame:[featureArray count] withMember:i];

    UIButton *aButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [aButton setFrame:buttonRect];

    [aButton addTarget:self action:@selector(buttonTouched:) forControlEvents:UIControlEventTouchUpInside];
    [aButton setTitle:[NSString stringWithFormat:@"%@",myFeature.featureName] forState:UIControlStateNormal];
    aButton.tag = myFeature.featureID;

    [self.view addSubview:aButton];

}

}

注意:这些方法的发布方式与它们被调用的顺序相反。最后一个方法调用第二个方法,然后调用第一个方法。

更新:我已经更新了这些函数来显示现在的内容:下面,我将发布该对象的头文件 - 也许这会有所帮助

@interface Feature : NSObject {
    int         featureID;
    int         featureSecure;
    NSString    *featureName;
}

@property (nonatomic, assign) int featureID;
@property (nonatomic, assign) int featureSecure;
@property (nonatomic, retain) NSString *featureName;

- (id) init;

- (void) dealloc;

+ (id) featureWithID:(int)fID name:(NSString*)fName secure:(int)fSecure;



@end

@interface FeatureController : NSObject {

}


- (id) init;

- (void) dealloc;

+ (NSMutableArray*) createFeatureArray;

+ (Feature*) getFeatureWithID:(int)fetchID;

@end

3 个答案:

答案 0 :(得分:1)

便捷方法应遵循返回自动释放对象的惯例。改变这个:

+ (id) featureWithID:(int)fID name:(NSString*)fName secure:(int)fSecure {
Feature *newFeature = [[self alloc] init];
...    
return newFeature;
}

为:

+ (id) featureWithID:(int)fID name:(NSString*)fName secure:(int)fSecure {
Feature *newFeature = [[self alloc] init];
...    
return [newFeature autorelease];
}

答案 1 :(得分:1)

方法的名称 - +featureWithID:name:secure: - 表示它返回调用者不拥有的对象。相反,它返回一个已保留的对象,调用者因此拥有并必须释放。要解决此问题(以及您的泄漏),只需将return newFeature替换为return [newFeature autorelease]

您不需要做任何事情,因为您自己的代码不需要持久的所有权声明,而您添加对象的数组将通过它来管理自己的所有权声明。

答案 2 :(得分:0)

+createFeatureArray中,你过度释放了数组:

+ (NSMutableArray*) createFeatureArray {
    …
    NSMutableArray *returnArray = [[[NSMutableArray alloc] init] autorelease];
    …
    return [returnArray autorelease];
}

在第一行中,您使用了+alloc,因此您拥有该数组。然后你使用了-autorelease,所以你不再拥有这个数组了。这意味着您不应向-release行发送-autoreleasereturn

您可以通过将这些行更改为:

来解决此问题
+ (NSMutableArray*) createFeatureArray {
    …
    NSMutableArray *returnArray = [NSMutableArray array];
    …
    return returnArray;
}

此外,除非调用者认为数组是可变的,否则应该更改该方法以返回NSArray而不是NSMutableArray。您可以按原样保留代码,即返回一个可变数组,即使方法声明声明返回类型为NSArray

<小时/> 至于您的便利构造函数,根据您是要返回自有对象还是非自有对象,基本上有两种选择:

  • 如果要返回拥有的对象,请使用+alloc+new进行分配,并在不自动释放的情况下将其返回。您的方法名称应包含new,例如+newFeatureWithId:…

  • 如果要返回一个不属于调用者的对象,请使用+allocnew进行分配,并在将其返回给调用者之前/之后自动释放它。您的方法名称不应包含newalloccopy

<小时/> 在-setUpNavigationButtons中,您通过+createFeatureArray获取非拥有数组,根据它分配可变数组,并释放可变数组,而无需添加或删除元素。当您需要添加/删除元素时,可变数组是有意义的。如果您没有此需求,可以将方法更改为:

- (void)setUpNavigationButtons {
// get array of features from feature data controller object
NSArray *featureArray = [FeatureController createFeatureArray];
…
// [featureArray release];

您删除了[featureArray release],因为您在该方法中没有featureArray


修改:在-setUpNavigationButtons中,您将保留您创建的按钮,并在发布后立即保留。在那个特定的方法中,那些是幂等操作 - 它们本身并没有错,但根本不是必需的。您可以用

替换该代码
UIButton *aButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
…
[self.view addSubview:aButton];
// [aButton release];

即,不要保留它,也不要释放它。