我怎么泄漏记忆?

Jas*_*son 3 iphone cocoa memory-leaks objective-c 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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

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

NSData *data = [[NSData alloc] initWithContentsOfURL:person.image];
[data release];
Run Code Online (Sandbox Code Playgroud)

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

它为什么泄漏?

Pet*_*wis 5

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

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

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

@property (copy) NSString* userName;
Run Code Online (Sandbox Code Playgroud)

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

- (NSString*) userName;
- (void) setUserName: (NSString*) theUserName;
Run Code Online (Sandbox Code Playgroud)

您可以通过使用@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;
}
Run Code Online (Sandbox Code Playgroud)

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

你的dealloc必须释放各种ivars:

- (void)dealloc
{
    [userName release];
    [displayName release];
    [updates release];
    [userInfo release];
    [image release];
    [super dealloc];
}
Run Code Online (Sandbox Code Playgroud)

您可以使用self.userName访问或更改属性,而不是直接访问ivars.

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

@property(readonly)NSString*displayName;

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

- (NSString*) displayName
{
    return [userInfo valueForKey:@"name"];
}
Run Code Online (Sandbox Code Playgroud)

并删除dealloc中的版本.

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