I have looked on the web but can't find anything that might help. My question is can I write the tires[x] array as an NSArray, if so what would be the syntax to both declare and allocate the tire Class instance objects?
#interface CarClass : NSObject {
EngineClass *engine;
TireClass *tires[4];
}
.
#implementation CarClass
- (id) init {
[super init];
NSLog(#"_init: %#", NSStringFromClass([self class]));
engine= [[EngineClass alloc] init];
tires[0]= [[TireClass alloc] init];
tires[1]= [[TireClass alloc] init];
tires[2]= [[TireClass alloc] init];
tires[3]= [[TireClass alloc] init];
return self;
}
EDIT:
this is my dealloc method for the CarClass
- (void) dealloc {
NSLog(#"_deal: %#", NSStringFromClass([self class]));
[engine release];
[tires release];
[super dealloc];
}
Still a bit confused about the retain in the NSArray below, if I add an extra [tires retain] to the CarClass:init then the tires do not get released. However as the code is now the tires release with or without the end retain (i.e.)
tires = [[NSArray arrayWithObjects:
[[[TireClass alloc] init] autorelease],
[[[TireClass alloc] init] autorelease],
[[[TireClass alloc] init] autorelease],
[[[TireClass alloc] init] autorelease],
nil] retain];
tires = [NSArray arrayWithObjects:
[[[TireClass alloc] init] autorelease],
[[[TireClass alloc] init] autorelease],
[[[TireClass alloc] init] autorelease],
[[[TireClass alloc] init] autorelease],
nil];
FINAL EDIT: I also thought that without the autorelease on the individual tires that finally releasing the NSArray in the dealloc would release both the array and the objects it points to, this does not seem to be the case.
cheers -gary-
#interface CarClass : NSObject {
EngineClass *engine;
NSArray *tires;
}
.
#implementation CarClass
- (id) init {
self = [super init];
if (self == nil)
return nil;
NSLog(#"_init: %#", NSStringFromClass([self class]));
engine= [[EngineClass alloc] init];
tires = [[NSArray arrayWithObjects:
[[[TireClass alloc] init] autorelease],
[[[TireClass alloc] init] autorelease],
[[[TireClass alloc] init] autorelease],
[[[TireClass alloc] init] autorelease],
nil] retain];
return self;
}
You don't always need to use an NSArray for collections. How about this way which has a bit more information about each tyre:
#interface CarClass : NSObject {
EngineClass *engine;
NSDictionary *tiresDictionary;
}
#implementation CarClass
- (id) init {
self = [super init];
if (!self) {
return nil;
}
NSLog(#"_init: %#", NSStringFromClass([self class]));
engine= [[EngineClass alloc] init];
tiresDictionary = [[NSDictionary dictionaryWithObjectsAndKeys:
[[[TireClass alloc] init] autorelease], #"LeftFrontTyre",
[[[TireClass alloc] init] autorelease], #"RightFrontTyre",
[[[TireClass alloc] init] autorelease], #"LeftRearTyre",
[[[TireClass alloc] init] autorelease], #"RightRearTyre",
nil] retain];
return self;
}
This way you still have a collection class, but rather than try and remember which index of an array refers to which tyre, you have a dictionary with descriptive keys so you can refer to each tyre by a name.
I would change Nikolai's example a bit. Since I do not want to add autorelease and retain where none is needed, less code is always less bug-prone code.
#interface CarClass : NSObject {
EngineClass *engine;
NSArray *tires;
}
#implementation CarClass
- (id) init {
self = [super init];
if (self) {
NSLog(#"_init: %#", NSStringFromClass([self class]));
engine= [[EngineClass alloc] init];
tires = [[NSArray alloc] initWithObjects:
[TireClass new], [TireClass new], [TireClass new], [TireClass new], nil];
[tires makeObjectsPerormSelector:#selector(release)];
}
return self;
}
The [tires makeObjectsPerormSelector:#selector(release)]; can be scary, but is useful and also very performant. A slightly less performant way, but maybe more clear would be to let the TireClass implement a factory method, and create autoreleased objects from that one. Like so:
+(TireClass)tire;
{
return [[self new] autorelease];
}
Also please stop naming your classes "Something"Class, a kitten dies if you do :)
Related
Can anybody explain me why does the case1 and case2 crashes while the others does not in case of non-ARC?
Case1:
NSString *rr = [[NSString alloc] initWithString:#"AB"];
[rr release];
[rr autorelease];
Case2:
NSString *rrr = [[NSString alloc] initWithString:#"AB"];
[rrr autorelease];
[rrr release];
Case3:
NSMutableString *rr1 = [[NSMutableString alloc] initWithString:#"AB"];
[rr1 release];
[rr1 autorelease];
Case4:
NSMutableString *rrr1 = [[NSMutableString alloc] initWithString:#"AB"];
[rrr1 autorelease];
[rrr1 release];
Case5:
NSArray *rr3 = [[NSArray alloc] initWithObjects:#"jj", nil];
[rr3 release];
[rr3 autorelease];
Case6:
NSArray *rrr3 = [[NSArray alloc] initWithObjects:#"jj", nil];
[rrr3 autorelease];
[rrr3 release];
Case7:
NSMutableArray *rr2 = [[NSMutableArray alloc] initWithObjects:#"jj", nil];
[rr2 release];
[rr2 autorelease];
Case8:
NSMutableArray *rr2 = [[NSMutableArray alloc] initWithObjects:#"jj", nil];
[rr2 autorelease];
[rr2 release];
All are are incorrect because eventually all will be released twice, but some may coincidentally not crash.
The alloc allocates the object with a retain count of 1. release decreases the retain count 1. autorelease eventually decreases the retain count 1. That means that all are over released.
But as #Chuck mentions some instances are constants, are created at compile time and never released so release and autorelease can be called to many tines with no crash.
String constants are one instance of this this where over-releasing will not cause a crash:
NSString *s = #"aa";
Even over-releasing this is OK because the compiler is smart enough:
NSString *s = [NSString stringWithString:#"aa"];
But you will get a warning from the current LLVM compiler that using stringWithString with a literal is redundant.
I've been at this issue for two days, and no matter what I do, I cannot get it to stop leaking strings.
The class is a XML parser (using TouchXML), that is designed to run repeatedly throughout the life time of the application. The first time it runs, there are no leaks, everything cleans up perfectly. On the second run, it begins to leaks, almost always where ever strings are.
Some images from Instruments:
http://www.producerstudio.net/1.png
http://www.producerstudio.net/2.png
.h
#import <Foundation/Foundation.h>
#import "TouchXML.h"
#protocol RSSParsingComplete
-(void)parsingFinished;
#end
#interface RSS : NSObject<NSXMLParserDelegate>{
NSArray *rssURLArray;
NSMutableData *xmlData;
NSMutableArray *articles;
NSMutableArray *arrayOfArticles;
int numberOfFeeds;
NSDateFormatter *inputFormatter;
NSDateFormatter *outputFormatter;
id<RSSParsingComplete> delegate;
}
#property (nonatomic, retain) NSArray *rssURLArray;
#property (nonatomic, retain) NSMutableData *xmlData;
#property (nonatomic, retain) id<RSSParsingComplete> delegate;
#property (nonatomic, retain) NSMutableArray *articles;
#property (nonatomic, retain) NSMutableArray *arrayOfArticles;
#property (nonatomic, retain) NSDateFormatter *inputFormatter;
#property (nonatomic, retain) NSDateFormatter *outputFormatter;
-(id)initWithRSSArray:(NSArray *)inputURLArray;
-(void)connect;
-(NSArray *)feedArticles;
#end
.m
#import "RSS.h"
#implementation RSS
#synthesize xmlData, rssURLArray, articles, arrayOfArticles, delegate, inputFormatter, outputFormatter;
-(void)connect{
self.xmlData = [[[NSMutableData alloc] init] autorelease];
NSURL *rssURL = [[NSURL alloc] initWithString:[self.rssURLArray objectAtIndex:numberOfFeeds-1]];
NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:[NSURLRequest requestWithURL:rssURL] delegate:self];
[urlConnection release];
[rssURL release];
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
[self.xmlData setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[self.xmlData appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
[xmlData release];
[connection release];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
CXMLDocument *xmlDoc = [[[CXMLDocument alloc] initWithData:xmlData options:0 error:nil] autorelease];
self.articles = [[[NSMutableArray alloc] init] autorelease];
self.inputFormatter = [[[NSDateFormatter alloc] init] autorelease];
self.outputFormatter = [[[NSDateFormatter alloc] init] autorelease];
[self.inputFormatter setDateFormat:#"EEE, dd MMM yyyy HH:mm:ss zzz"];
[self.inputFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"] autorelease]];
[self.inputFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"UTC"]];
[self.outputFormatter setDateFormat:#"dd.MM.yyyy HH:mm:ss"];
[self.outputFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"] autorelease]];
[self.outputFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"UTC"]];
NSArray *itemNodes = [xmlDoc nodesForXPath:#"//item" error:nil];
for(CXMLElement *node in itemNodes){
NSMutableDictionary *article = [[NSMutableDictionary alloc] init];
for(int counter = 0; counter < [node childCount]; counter++){
if([[[node childAtIndex:counter] name] isEqualToString:#"title"]){
[article setObject:[[node childAtIndex:counter] stringValue] forKey:#"title"];
}
if([[[node childAtIndex:counter] name] isEqualToString:#"link"]){
[article setObject:[[node childAtIndex:counter] stringValue] forKey:#"url"];
}
if([[[node childAtIndex:counter] name] isEqualToString:#"description"]){
[article setObject:[[node childAtIndex:counter] stringValue] forKey:#"description"];
}
if([[[node childAtIndex:counter] name] isEqualToString:#"pubDate"]){
NSDate *tempDate = [self.inputFormatter dateFromString:[[node childAtIndex:counter] stringValue]];
[article setObject:[self.outputFormatter stringFromDate:tempDate] forKey:#"name"];
}
}
[self.articles addObject:article];
[article release];
}
NSArray *feedTitleNode = [xmlDoc nodesForXPath:#"//title" error:nil];
NSString *feedTitle = [[NSString alloc] initWithString:[[[feedTitleNode objectAtIndex:0] childAtIndex:0] stringValue]];
[self.articles addObject:feedTitle];
[feedTitle release];
[self.arrayOfArticles addObject:[articles copy]];
[self.articles removeAllObjects];
[inputFormatter release];
[outputFormatter release];
numberOfFeeds--;
if(numberOfFeeds > 0){
[self connect];
}else{
[delegate parsingFinished];
}
}
-(NSArray *)feedArticles{
NSLog(#"Array of Articles: %#", self.arrayOfArticles);
return self.arrayOfArticles;
}
-(id)initWithRSSArray:(NSArray *)inputURLArray{
self = [super init];
if (self) {
self.arrayOfArticles = [[[NSMutableArray alloc] init] autorelease];
self.rssURLArray = [[[NSArray alloc] initWithArray:inputURLArray] autorelease];
numberOfFeeds = [self.rssURLArray count];
[self connect];
}
return self;
}
-(void)dealloc{
[rssURLArray release];
[xmlData release];
[articles release];
[arrayOfArticles release];
[super dealloc];
}
- (id)init
{
self = [super init];
return self;
}
#end
I've done everything I can think of to resolve the leaks. I've read the Apple Memory Management guides, as well as the excellent guide on iPhoneDevSDK and that has helped me cut down on 90% of the leaks I originally had (the class doesn't leak so long as you call it once). Maybe i've been staring at this for too long, or maybe i'm missing something obvious.
I appreciate it!
First, should delegate be retained? I'm not sure why you need it as an instance variable at all. But since it's retained (and you don't seem to release it), your RSS object will retain a circular reference to itself and won't ever be released.
Second, do you need to keep the date formatters in an instance variable? It looks like you're allocating them and releasing them in the same method. Note that they're retained in the RSS instance and never released.
Using instruments I found a strange problem regarding memory leaks. My app has a log mechanism which record events through the app and the whole communication with the server(request-response). Each event object that is been wrote has a timestamp. This timestamp is get as follows:
[NSDate descriptionWithLocale:[NSLocale systemLocale]]
Using instruments, I saw that descriptionWithLocale cause leaks.
Below is the code:
-(id)initEvent:(NSString*)eventStr details:(NSString*)detailsStr{
eventString = [[NSMutableString alloc] initWithString:eventStr];
detailsString = [[NSString alloc] initWithString:detailsStr];
date =[[NSMutableString alloc] init];
NSDate* currentDate = [NSDate date];
NSString *dateDescStr = [currentDate descriptionWithLocale:[NSLocale systemLocale]];
[date appendString:dateDescStr];
return [super init];
Thanks,
Alex.
Are you sure it's descriptionWithLocale that's causing your memory leak? You're not using the property accessor/mutator to set your class properties and you're releasing them in a method other than your dealloc method, which is going to cause issues. At the least, you're violating the basic memory management rules by allocating eventString, detailsString, and date in initEvent (which is then the owner, because you don't use a property accessor/mutator) and releasing them in the eventDictionary method, which is not the owner.
I would start by trying the following (assuming all your properties have the retain attribute):
-(id)initEvent:(NSString*)eventStr details:(NSString*)detailsStr{
this.eventString = [[NSMutableString alloc] initWithString:eventStr];
this.detailsString = [[NSString alloc] initWithString:detailsStr];
this.date =[[NSMutableString alloc] init];
NSDate* currentDate = [NSDate date];
NSString *dateDescStr = [currentDate descriptionWithLocale:[NSLocale systemLocale]];
[date appendString:dateDescStr];
[eventString release];
[detailsString release];
[date release];
return [super init];
}
Also remove your release calls from eventDictionary and add them to the dealloc method, as per the standard memory management pattern.
Give that a try and see if it helps. Let me know if I can clarify anything.
Thanks for your solution. It works great. Indeed not descriptionWithLocale was the cause of the leak, but my own memory rule implementation (was 1 year ago :( ). Strange that instruments was pointing out to that method call.... See how my class looks now:
#implementation EventItem
#synthesize eventString, detailsString, date;
-(id)initEvent:(NSString*)eventStr details:(NSString*)detailsStr{
if((self = [super init])){
self.eventString = [[NSMutableString alloc] initWithString:eventStr];
self.detailsString = [[NSString alloc] initWithString:detailsStr];
self.date =[[NSMutableString alloc] init];
NSDate* currentDate = [NSDate date];
NSString *dateDescStr = [currentDate descriptionWithLocale:[NSLocale systemLocale]];
[date appendString:dateDescStr];
[eventString release];
[detailsString release];
[date release];
}
return self;
}
-(NSMutableDictionary*)eventDictionary{
if(!dictionary)
dictionary = [[NSMutableDictionary alloc] init];
else
return dictionary;
[dictionary setObject:self.date forKey:#"Event"];
[dictionary setObject:self.date forKey:#"Date"];
[dictionary setObject:self.detailsString forKey:#"Details"];
[dictionary setObject:self.eventString forKey:#"EventDescription"];
return [dictionary autorelease];
}
-(void)dealloc{
// NSLog(#"dealloc called in EventItem");
[eventString release];
[detailsString release];
[date release];
[super dealloc];
}
#end
Now, another problem that i have is regarding to some image loaded via "loadWithContentsOfFile:"
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.rowHeight = OC_CELL_HEIGHT;
self.tableView.backgroundColor = [UIColor clearColor];
UIImage* backgroundImage = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"TableBg" ofType:#"png"]];
UIImageView* background = [[UIImageView alloc] initWithFrame:self.tableView.frame];
background.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
background.image = backgroundImage;
[background sizeToFit];
[backgroundImage release];
[self.tableView setBackgroundView:background];
[background release];
[self.tableView setAutoresizesSubviews:YES];
selectedOCData = nil;
self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin;
}
The line :
UIImage* backgroundImage = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"TableBg" ofType:#"png"]];
leaks 98% and the haviest backtrace is 240 kb. I attached a screenshot with instruments for this. Is there a problem not with the actual call of initWithContentsOfFile, but with the tableView which is not deallocated correctly?(My class is a tableViewController).
Thanks,
Alex.
-(void) getAccounts {
self.selAccounts = [[NSMutableArray alloc] init];
self.accounts = [[NSMutableArray alloc] init];
NSString *url=[NSString stringWithFormat:#"https://localhost//listaccts"];
self.processor=[[AsynConnectionProcessorController alloc] init];
self.processor.delegate=self;
self.processor.server=self.server;
[processor createRequestfromURL:url];
}
This method is causing memory leak when invoked. Now if I replace this with below
-(void) getAccounts {
[accounts release];
self.selAccounts = [[NSMutableArray alloc] init];
accounts = [[NSMutableArray alloc] init];
NSString *url=[NSString stringWithFormat:#"https://localhost//listaccts"];
self.processor=[[AsynConnectionProcessorController alloc] init];
self.processor.delegate=self;
self.processor.server=self.server;
[processor createRequestfromURL:url];
}
I am getting memory leak if I invoke this method second time as a result of viewcontroller beong popped from stack.
Why does this leak? accounts is an insyance variable with declration like this :
#property (nonatomic, retain) NSMutableArray *accounts;
Can't I assume that there won't be memory leak if I use setter via self.accounts?
This is wrong
self.accounts = [[NSMutableArray alloc] init];
the setter already does a retain, since you specified that in the property
#property (nonatomic, retain) NSMutableArray *accounts;
you should rewrite it like this
NSMutableArray arr = [[NSMutableArray alloc] init];
self.accounts = arr;
[arr release];
or alternatively:
self.accounts = [[[NSMutableArray alloc] init] autorelease];
EDIT: removed 'non preferred' - was subjective.
This is probably a very straight forward application, but I am new to Objective-C (coming from Java) and the whole memory management and "EXC_BAD_ACCESS" errors are breaking my heart.
I have a normal NavigationController iPhone App, with Core Data. in the AppDelegate the NSManagedObjectContext is created and passed to the RootViewController. A view things are looked up directly from the main thread to populate the table, and that seems to work fine.
The App is somekind of RSS-type reader, so as soon as the App starts I fire a thread to fetch new data and update the view:
-(void)updateData:(id)sender {
UIActivityIndicatorView *activityIndicator =
[[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
[activityIndicator startAnimating];
UIBarButtonItem *activityItem =
[[UIBarButtonItem alloc] initWithCustomView:activityIndicator];
[activityIndicator release];
self.navigationItem.leftBarButtonItem = activityItem;
[activityItem release];
// Start thread to update the data
[NSThread detachNewThreadSelector:#selector(doUpdateData) toTarget:self withObject:nil];
}
-(void)doUpdateData{
NSLog(#"Update data Thread (in 5 sec.)");
[NSThread sleepForTimeInterval:5];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
DataManager *data = [[DataManager alloc] initWithContext:managedObjectContext];
[data updateData];
[data release];
data=nil;
[self performSelectorOnMainThread:#selector(finishUpdateData) withObject:nil waitUntilDone:NO];
[pool release];
}
-(void)finishUpdateData{
self.navigationItem.leftBarButtonItem = updateBttn;
DataManager *data = [[DataManager alloc] initWithContext:managedObjectContext];
objects = [data getArticles];
[data release];
data=nil;
NSLog(#"Amount of records after update: %d", [objects count]);
[self.tableView reloadData];
}
The problem is that this doesn't work. In the DataManager, first settings need to be retrieved, and as soon as the NSEntityDescription is created I get the "EXC_BAD_ACCESS":
- (NSFetchedResultsController *)fetchedResultsController {
// Set up the fetched results controller if needed.
if (fetchedResultsController == nil) {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Setting" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"key" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchLimit:1];
.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
}
return fetchedResultsController;
}
I guess the pointer to the ManagedObjectContext is wrong, as a result from running in a different thread and memory-pool. So how do you create such an application if that is the issue), how do I get a reference to the original ManagedObjectContext format he thread?
[EDIT]
I also tried to use
iDomsAppDelegate *appDelegate = (iDomsAppDelegate *)[[UIApplication sharedApplication] delegate];
DataManager *data = [[DataManager alloc] initWithContext:appDelegate.managedObjectContext];
in doUpdateData (as hinted by other posts), but that gives the same result
Managed Object Contexts are not thread safe. Apple's guidelines indicate that you must have a separate instance of NSManagedObjectContext per thread.