我在这里有一个问题:Confusing double free error message/memory leak in iPhone app我认为需要一个新问题来回答它。
我感兴趣的代码就是那个问题,但我会在这里发布
#import <UIKit/UIKit.h>
#import "MyManager.h"
@interface ListOfCarShares : UITableViewController <NSXMLParserDelegate>
{
NSURLConnection *connection;
NSMutableData *carsharexml;
NSMutableArray *ldestination;
NSMutableArray *ldeparts_from;
NSMutableArray *lcs_id;
NSMutableArray *ltime;
NSMutableString *currentElement;
NSMutableString *tdest;
NSMutableString *tfrom;
NSMutableString *ttime;
NSMutableString *tid;
}
-(void)fetchcarshares;
@property (nonatomic, assign) IBOutlet UITableViewCell *maincell;
@end
#import "ListOfCarShares.h"
@implementation ListOfCarShares
@synthesize maincell;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict
{
currentElement = [[elementName copy] autorelease];
if ([elementName isEqualToString:@"destination"])
{
//NSLog(@"found current conditions tag it reads %@",currentElement);
tdest = [[NSMutableString alloc] init];
}
if ([elementName isEqualToString:@"departs_from"])
{
tfrom = [[NSMutableString alloc] init];
}
if ([elementName isEqualToString:@"time"])
{
ttime = [[NSMutableString alloc] init];
}
if ([elementName isEqualToString:@"cs_id"])
{
tid = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if ([currentElement isEqualToString:@"destination"])
{
[tdest appendString:string];
}
if ([currentElement isEqualToString:@"departs_from"])
{
[tfrom appendString:string];
}
if ([currentElement isEqualToString:@"time"])
{
[ttime appendString:string];
}
if ([currentElement isEqualToString:@"cs_id"])
{
[tid appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([currentElement isEqualToString:@"destination"])
{
[ldestination addObject:tdest];
[tdest release];
}
if ([currentElement isEqualToString:@"departs_from"])
{
[ldeparts_from addObject:tfrom];
[tfrom release];
}
if ([currentElement isEqualToString:@"time"])
{
[ltime addObject:ttime];
[ttime release];
}
if ([currentElement isEqualToString:@"cs_id"])
{
[lcs_id addObject:tid];
[tid release];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
ldestination = [[NSMutableArray alloc] init];
ldeparts_from = [[NSMutableArray alloc] init];
ltime = [[NSMutableArray alloc] init];
lcs_id = [[NSMutableArray alloc] init];
carsharexml = [[NSMutableData alloc] init];
[self fetchcarshares];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[connection release];
[ldestination release];
[ldeparts_from release];
[ltime release];
[lcs_id release]; ///
[carsharexml release];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [ltime count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
if (cell == nil)
{
[[NSBundle mainBundle] loadNibNamed:@"carsharecell" owner:self options:nil];
}
// Configure the cell...
cell=maincell;
UILabel *from;
UILabel *dest;
UILabel *time;
from = (UILabel *)[cell viewWithTag:4];
dest = (UILabel *)[cell viewWithTag:5];
time = (UILabel *)[cell viewWithTag:6];
from.text=[ldeparts_from objectAtIndex:indexPath.row];
dest.text=[ldestination objectAtIndex:indexPath.row];
time.text=[ltime objectAtIndex:indexPath.row];
return cell;
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
-(void)fetchcarshares
{
MyManager *sharedManager = [MyManager sharedManager];
NSString *urlString = [NSString stringWithFormat:@"http://url/get.php?username=%@&password=%@",sharedManager.user,sharedManager.passw];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
connection = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:YES];
}
-(void) connection:(NSURLConnection *)conn didReceiveData:(NSData *)data
{
[carsharexml appendData:data];
}
-(void) connectionDidFinishLoading:(NSURLConnection *)conn
{
NSString *xmlcheck = [[NSString alloc] initWithData:carsharexml encoding:NSUTF8StringEncoding];
NSLog(@"%@",xmlcheck);
[xmlcheck release];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData: carsharexml];
[parser setDelegate:self];
[parser parse];
[parser release];
[[self tableView] reloadData];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 102;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
-(void)dealloc
{
[super dealloc];
}
@end
我只在.h文件中定义了一个属性。回答这个问题的人似乎认为我有双重免费错误的原因是因为我的变量没有@property
。
我有很多代码几乎与此相同,我没有问题。
我的问题是
由于
答案 0 :(得分:21)
从技术上讲,您只需要为可以从其他类访问的值使用属性,但是许多人发现所有指针类型实例变量都更容易使用(保留)属性,因此保留更加自动化。 (然后使用self.propertyName = xxx;
表示法进行设置,self.propertyName = nil;
表示在dealloc
中发布。)
是的,你可以“手动”进行保留和释放,但这样做很麻烦,而且当你进行“快速编辑”时,你往往会把事情弄糟。但是,您必须注意的一件事是将保留(不是简单的自动恢复)值(例如alloc/init
值)分配给self.xxx
属性。如果你不以某种方式缓解它,这将导致双重保留。
如果你不使用属性,另一件事就是nil
之后总是release
一个指针值。这可以防止您意外使用释放的值,并且可以防止您执行双release
。
(请注意,使用像我上面描述的“懒惰”技术绝不是“糟糕的编程”,而是“完美地”搞清楚所有内容。大约98%的编程是调试,以及你可以采取的任何措施来防止错误或让他们更容易找到是善良。)
(我还要注意,上述代码中的问题似乎主要是在发布它们之后你没有nil
tdest
等指针。而且你的if
测试应该在使用之前检查指针是否已经被填满。)
已添加:请注意,上述内容适用于ARC之前的程序。随着ARC,“规则”发生了重大变化。
答案 1 :(得分:6)
属性做了很多事情。在最肤浅的层面,它们允许您以点线形式访问您的成员变量。充其量,它们可以是出色的内存管理工具(等等)。
假设你有一个变量:
NSNumber * myNumber;
稍后在代码中,您将其访问为:
myNumber = [NSNumber numberWithInt: 5];
问题是您可能会丢失对myNumber中先前存储的值的引用。可能的内存泄漏!! 此时,你没有对myNumber进行保留,并且在你使用它之前它可能会被释放。
属性如何帮助?假设你在它周围定义了一个属性并使用了synthesize:
在界面定义中:
NSNumber * myNumber;
...
@property (retain, nonatomic) NSNumber * myNumber;
和
在实施文件中:
@synthesize myNumber;
这将创建一个getter和setter。意思是......每当你将myNumber分配给某个东西时,如:
self.myNumber = newNumber;
调用以下setter方法(由synthesize指令创建):
- (NSNumber *) setMyNumber: (NSNumber *) newNumber {
[myNumber release];
myNumber = newNumber;
[myNumber retain];
return newNumber;
}
此处,myNumber会自动获得保留。每次手工操作都非常繁琐......正如您所看到的,使用属性要容易得多。
但这仍然不是一个完美的解决方案!为什么?如果在实现中使用以下语句,该怎么办:
myNumber = newNumber;
请记住,只有在使用点分表示法(self.myNumber
)时才会调用属性的getter和setter。所以在这里,使用属性对我们没有任何作用,因为我们忘了使用它们!
这是非常常见的,可能会失效并且理解为令人沮丧。
那么,最好的方法是什么?这是我推荐的(和无数其他人一样):
在接口类中:
NSNumber * _myNumber;
...
@property (retain, nonatomic) NSNumber * myNumber;
在实施文件中:
@synthesize myNumber = _myNumber;
现在,您可以访问您的号码:
self.myNumber = whateverNewNumber;
但是,如果你这样做了:
myNumber = whateverNewNumber;
你会收到错误...因为myNumber变量不存在...强迫你每次都使用self.myNumber
!
此外,如果你选择走这条路线,不要忘记dealloc:
- (void) dealloc {
[_myNumber release];
_myNumber = nil;
}
或更简洁:
- (void) dealloc {
self.myNumber = nil;
}