3

我有一个表格视图,在加载时会创建一个人对象

人.h

#import <UIKit/UIKit.h>
#import "TwitterHelper.h"

@interface Person : NSObject {
    NSDictionary *userInfo;
    NSURL *image;
    NSString *userName;
    NSString *displayName;
    NSArray *updates;
}
/*
@property (retain) NSString *userName;
@property (retain) NSString *displayName;
@property (retain) NSDictionary *userInfo;
 */
@property (nonatomic, copy) NSURL *image;
@property (retain) NSArray *updates;

- (id)initWithUserName:userName;

@end

人.m

#import "Person.h"


@implementation Person

/*
@synthesize userName;
@synthesize displayName;
@synthesize userInfo;
 */
@synthesize image;
@synthesize updates;

- (id)initWithUserName:(NSString *)user{

    userName = user;
    userInfo = [TwitterHelper fetchInfoForUsername:user];
    displayName = [userInfo valueForKey:@"name"];
    image = [NSURL URLWithString:[userInfo valueForKey:@"profile_image_url"]];
    updates = [TwitterHelper fetchTimelineForUsername:userName];

    return self;
}

- (void)dealloc
{
    /*
    [userName release];
    [displayName release];
    [updates release];
     [userInfo release];
     [image release];
     */
    [super dealloc];
}

@end

在我的 UITableView 方法 cellAtRowForIndexPath 中,我正在创建每个人对象并像这样分配图像属性......

Person *person = [[Person alloc] initWithUserName:userName];

NSData *data = [[NSData alloc] initWithContentsOfURL:person.image];
[data release];

当我在 Instruments 中运行它时,它会突出显示 NSData *data... 行,说明这是泄漏的位置。

为什么会漏在那里?

4

3 回答 3

5

首先,您需要了解实例变量和属性与 getter/setter 之间的区别。

  • 实例变量 (ivars) 是存储在对象中的变量。您只需通过命名(例如“userName”)从方法中访问 ivar。
  • 属性定义对象的接口,允许读取和/或写入对象的信息。
  • getter/setter 实现该接口并可以使用 ivar 作为后备存储

您可以通过使用 getter/setter 来访问属性,或者显式地(例如 [self userName])或(等效地)使用点语法 self.userName。请注意,这两个符号完全相同。您在对象的接口中使用 @property 声明一个属性(即,您声明对象的接口),例如:

@property (copy) NSString* userName;

这个声明本质上等同于输入:

- (NSString*) userName;
- (void) setUserName: (NSString*) theUserName;

您可以通过使用@synthesize(它只是告诉编译器为您编写getter/setter)或自己实现它(即,您为userName 和setUserName 编写方法实现)来实现一个属性。还有一个很少使用的第三个选项@dynamic,它告诉编译器您将在运行时处理这些方法,本质上只是消除您可能会收到的警告。

接下来,您需要阅读和理解内存管理规则。它只有9个短段,现在去读,我会等。完毕?好的。

此外,您需要知道不应在 init 或 dealloc 例程中使用 getter/setter。

因此,您的 init 例程应如下所示:

- (id)initWithUserName:(NSString *)user{
    userName = [user copy];
    userInfo = [[TwitterHelper fetchInfoForUsername:user] retain];
    displayName = [[userInfo valueForKey:@"name"] copy];
    image = [[NSURL URLWithString:[userInfo valueForKey:@"profile_image_url"]] copy];
    updates = [[TwitterHelper fetchTimelineForUsername:userName] retain];
    return self;
}

请注意,您通过保留或复制获得存储在 ivar 中的每个值的所有权。通常,您使用 NSString 的副本将 NSMutableStrings 转换为您拥有的 NSStrings,而不是使用 retain 保留对可能可变字符串的引用。同样的问题也适用于 NSArray/NSDictionary,但我们假设 TwitterHelper 打算交出获取的数据。

您的 dealloc 将不得不释放各种 ivars:

- (void)dealloc
{
    [userName release];
    [displayName release];
    [updates release];
    [userInfo release];
    [image release];
    [super dealloc];
}

在代码中的其他任何地方,您都可以使用 self.userName 来访问或更改属性,而不是直接访问 ivars。

请注意,您可能会考虑根本不存储 displayName(以及类似的图像),而只需实现一个从 userInfo 检索它的属性 getter。为此,请删除 displayName ivar,将属性更改为:

@property (只读) NSString *displayName;

删除@synthesize displayName,并添加一个手动getter:

- (NSString*) displayName
{
    return [userInfo valueForKey:@"name"];
}

并删除 dealloc 中的释放。

请注意,您不需要保留/释放 displayName 中的值 - 您返回一个接收者不拥有的值,如果他们想保留它,则由他们复制/保留它。

于 2009-08-13T03:10:10.397 回答
1

如果你选择创建一个属性,你应该使用:

self.image = [NSURL URLWithString:[userInfo valueForKey:@"profile_image_url"]];

在你的init信息中而不是

image = [NSURL URLWithString:[userInfo valueForKey:@"profile_image_url"]];

设置不带self前缀的值不会调用copyorretain消息,并且会产生内存问题(不一定是泄漏)。

这可能是 Instruments 向您指出的。

(这显然适用于所有属性!)

或者,如果您不想使用访问器,则retaincopy检索到的值,例如:

image = [[NSURL URLWithString:[userInfo valueForKey:@"profile_image_url"]] retain];
于 2009-08-12T16:30:05.717 回答
0

您正在调用allocPerson 但没有释放它。你泄露了你的person对象。(在您的单元格配置中)

于 2009-08-13T03:25:23.470 回答