我有一个表格视图,当加载时会创建一个人物对象
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 *数据...行,说明泄漏的位置。
为什么会泄漏?
答案 0 :(得分:5)
首先,您需要了解实例变量和属性与getter / setter之间的区别。
使用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
前缀的值不会调用copy
或retain
消息,并且会产生内存问题(不一定是泄漏)。
这可能是仪器所指向的。
(这显然适用于所有属性!)
或者,如果您不想使用访问者,则retain
或copy
检索到的值,例如:
image = [[NSURL URLWithString:[userInfo valueForKey:@"profile_image_url"]] retain];
答案 2 :(得分:0)
你在Person上调用alloc
但是没有释放它。您泄漏了person
个对象。
(在你的手机配置中)