我怎么泄漏记忆?

时间:2009-08-12 16:18:31

标签: iphone objective-c cocoa memory-leaks cs193p

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

Person.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

Person.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];

当我在仪器中运行它时,它会突出显示NSData *数据...行,说明泄漏的位置。

为什么会泄漏?

3 个答案:

答案 0 :(得分:5)

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

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

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

@property (copy) NSString* userName;

此声明基本上等同于输入:

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

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

接下来,您需要阅读并理解memory management rules。它只有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,而不是保留哪个会使您保留对可能可变字符串的引用。同样的问题适用于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(readonly)NSString * displayName;

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

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

并删除dealloc中的版本。

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

答案 1 :(得分:1)

如果您选择创建属性,则应使用:

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

init消息中,而不是

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

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

这可能是仪器所指向的。

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

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

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

答案 2 :(得分:0)

你在Person上调用alloc但是没有释放它。您泄漏了person个对象。 (在你的手机配置中)