About initialization in Objective C iPhoneSDK - objective-c

I have a problem with the initialization of two objects. I have:
banner = [[UIBannerViewController alloc] initWithNibName:#"UIBannerViewController" bundle:nil];
NSLog(#"Banner: %#", banner);
homeBanner = [[UIBannerViewController alloc] initWithBanner:vistine];
NSLog(#"HomeBanner: %#", homeBanner);
Inside the UIBannerViewController I have:
- (void)viewDidLoad {
if ( !isInitialized ) {
bannerViews = [[NSMutableArray alloc] initWithCapacity:0];
images = [[NSMutableArray alloc] initWithCapacity:0];
// DEPRECATED
[NSThread detachNewThreadSelector:#selector(loadDataFromInternet)
toTarget:self
withObject:nil];
} else {
// TODO: Questo non va piĆ¹ bene qua.
// [pageControl setNumberOfPages:[images count]];
[pageControl setNumberOfPages:[bannerViews count]];
[pageControl setCurrentPage:0];
}
No matter what I do: if I uncomment the following code snippet
bannerViews = [[NSMutableArray alloc] initWithCapacity:0];
images = [[NSMutableArray alloc] initWithCapacity:0];
// DEPRECATED
[NSThread detachNewThreadSelector:#selector(loadDataFromInternet)
toTarget:self
withObject:nil];
the App crashes with Exception: * -[NSMutableArray objectAtIndex:]: index 0 beyond bounds for empty array.
What is/can be the problem

Use an autorelease NSMutable array
[NSMutableArray arrayWithCapacity:0]

Related

Objective-c Array not deallocating objects when removing

// AClass.m
// init
enemyBullets = [[NSMutableArray alloc] initWithCapacity:0];
enemy1 = [[Enemy alloc] initWithBullets:enemyBullets];
// At some point
NSMutableArray *bulletsToDelete = [NSMutableArray array];
for(BulletEnemy *thisBullet in enemyBullets)
{
// If I have to delete
[bulletsToDelete addObject: thisBullet];
}
[enemyBullets removeObjectsInArray:bulletsToDelete];
//dealloc method
[enemyBullets release];
[enemy1 release];
Now Inside Enemy some point in time I do the following:
// Enemy.m
- (id)initWithBullets:(NSMutableArray*) _bullets{
// Enemybullets is a var of Enemy
enemyBullets = _bullets;
}
// At some point...
myBullet = [[BulletEnemy alloc] init];
[enemyBullets addObject:myBullet];
[myBullet release];
The problem is when I do the following at Aclass:
[enemyBullets removeObjectsInArray:bulletsToDelete];
The dealloc method inside BulletEnemy doesn't get called because the retain count isn't 0. Why? But If I release ACLass (which releases enemyBullets) then My bullets get deallocated.
Make Enemy own the enemyBullets. And use reverseObjectEnumerator
// AClass.m
// init
enemy1 = [[Enemy alloc] init];
// At some point
for(BulletEnemy *thisBullet in enemy1.enemyBullets.reverseObjectEnumerator)
{
// If I have to delete
[enemy1.enemyBullets removeObject:thisBullet];
}
//dealloc method
[enemy1 release];
//Enemy.h
#property (retain) NSMutableArray* enemyBullets;
// Enemy.m
#synthesize enemyBullets = _enemyBullets;
- (id)init{
// Enemybullets is a var of Enemy
_enemyBullets = [[NSMutableArray alloc] init];
}
// At some point...
myBullet = [[BulletEnemy alloc] init];
[enemyBullets addObject:myBullet];
[myBullet release];
-(void) dealloc{
[_enemyBullets release];
}
Aparently, I was asigning something with retain propery, which made the object +1 thus not to deallocate. Just changed the propery to assign and it worked.

NSMutableArray addObject in for loop - memory leak

i'm putting strings (which are filenames of files in a certain directory) into an NSMutableArray with a for loop:
h-file:
#import <Three20/Three20.h>
#interface AlbumController : TTThumbsViewController {
NSMutableArray *images;
}
#property (nonatomic, retain) NSMutableArray *images;
#end
m-file:
#import "AlbumController.h"
#import "PhotoSource.h"
#import "Photo.h"
#implementation AlbumController
#synthesize images;
-(void)createPhotos {
NSString *bundleRoot = [[NSBundle mainBundle] bundlePath];
NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:bundleRoot error:nil];
NSArray *onlyJPGs = [dirContents filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"self ENDSWITH '.jpg'"]];
NSMutableArray *pics = [[onlyJPGs copy] autorelease];
if(!self.images) {
self.images = [[NSMutableArray alloc] init];
}
for(int i = 0; i < [onlyJPGs count]; i++)
{
//NSLog([pics objectAtIndex:i]);
NSString *ImgURL = [#"bundle://" stringByAppendingString:[pics objectAtIndex:i]];
Photo *photo = [[Photo alloc] initWithURL:ImgURL smallURL:ImgURL size:CGSizeMake(320, 212)];
[images addObject:photo];
[photo release];
}
}
-(void)viewDidLoad{
[self createPhotos]; // method to set up the photos array
self.photoSource = [[PhotoSource alloc]
initWithType:PhotoSourceNormal
title:#"Chili Pflanzen"
photos:images
photos2:nil
];
}
#end
i do not have any problem in the simulator but on my iPod...
Error message:
Data FOrmatters temporarily unavailable, will re-try after a 'continue'. (Unknown error loading shared library "/Developer/usr/lib/libXcodeDebuggerSupport.dylib")
thanks in advance
I think you should use mutableCopy and not copy on your pics array.
so instead of:
NSMutableArray *pics = [[onlyJPGs copy] autorelease];
you should use:
NSMutableArray *pics = [[onlyJPGs mutableCopy] autorelease];
More information about copy/mutablecopy in this question: Copy & mutableCopy?
Looks like the main issue is with
[images addObject:[[Photo alloc] initWithURL:ImgURL smallURL:ImgURL size:CGSizeMake(320, 212)]];
Here you are alloc'ing Photo but not releasing it. When you add an object to an array it increases the retain count for it.
Try changing it to
Photo *photo = [[Photo alloc] initWithURL:ImgURL smallURL:ImgURL size:CGSizeMake(320, 212)];
[images addObject:photo];
[photo release];
In addition ...
I'd change
self.images = [[[NSMutableArray alloc] init] autorelease];
to
if(!self.images) {
self.images = [[NSMutableArray alloc] init];
}
Otherwise there is the potential for a memory leak if it has already been initialized, as well as that you probably do not want it autoreleased;
Your NSMutableArray instance is autoreleased. You are assigning it to the images ivar. The fact that you have declared it as a retained property doesn't matter, because you aren't assigning it to the property. My guess is that you meant to assign to the property, and the crash is caused by the inadvertent deallocation.
Change:
images = [[[NSMutableArray alloc] init] autorelease];
...to:
self.images = [[[NSMutableArray alloc] init] autorelease];
...or:
images = [[NSMutableArray alloc] init];
Also note that your property is declared as NSArray when you are allocating an instance of NSMutableArray.
Also see the Memory Management Programming Guide.

NSMutableArray won't add NSDictionary

I updated to cocos2d from 0.99.4 to 0.99.5. While it was on the older version I had the high score list working and I was able to add the NSDictionary to NSMutableArray with no problems.
Now that I've updated it won't add the NSDictionary variable scoreDetails to my NSMutableArray scoreList. Here's my code:
StatsManager.h
#interface StatsManager : NSObject {
NSMutableArray *scoreList;
NSUserDefaults *saveHighScore;
NSMutableArray *printableScoreList;
//NSMutableArray *scoreListTestOne;
float highScoreHelloWorld;
}
StatsManager.m
-(void)setHighScore:(float)highScore nameStrings:(NSString*)nameString {
NSNumber *newHighScore = [NSNumber numberWithFloat:highScore];
NSLog(#"%# highScore", newHighScore);
NSDictionary *scoreDetails = [NSDictionary dictionaryWithObjectsAndKeys:nameString, #"name", newHighScore, #"score", nil];
NSLog(#"%#", scoreDetails);
//NSMutableArray *testTwo = [[NSMutableArray alloc] init];
[scoreList addObject:scoreDetails];
NSLog(#"scoreList %#", scoreList);
//[scoreListTestOne addObject:scoreDetails];
//NSLog(#"scoreListTestOne %#", scoreListTestOne);
//sort
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"score" ascending:NO];
[scoreList sortUsingDescriptors:[NSArray arrayWithObject:sort]];
printableScoreList = scoreList;
NSLog(#"printableScoreList %#", printableScoreList);
//NSLog(#"scoreListTestOne %#", scoreListTestOne);
}
The line in question is
[scoreList addObject:scoreDetails];
I created a local NSMutableArray variable in the setHighScore function and tried adding the scoreDetails to that and it worked. but why doesn't it work like I've coded it above anymore?
I alloc init my scoreList here:
#implementation StatsManager
static StatsManager *_sharedStatsManager = nil;
-(id)init {
scoreList = [[NSMutableArray alloc] init];
//playerNames = [[NSMutableArray alloc] init];
//playerScores = [[NSMutableArray alloc] init];
printableScoreList = [[NSMutableArray alloc] init];
//listOfScoresTest = [[NSMutableDictionary alloc] initWithCapacity:5];
/*if ([scoreList count] == 0) {
for (int i = 0; i < 5; i++) {
[scoreList addObject:[NSNumber numberWithFloat:0.00]];
}
}*/
return [super init];
}
I should also mention that I created a new projectB and transferred my files/images from my old projectA to the new one because the old one wouldn't compile anymore because of some duplicate error. But i "cleaned all targets" again and it worked but that also has the same problem as my new projectB
Do you initialize scoreList ivar in init or so forth?
- (id)init
{
/* snip */
scoreList = [[NSMutableArray alloc] init];
return self;
}
- (void)dealloc
{
[scoreList release];
[super dealloc];
}
Ok Georg I reconsidered your suggestion about overwriting it later.
and it had something to do with my NSUserdefaults. I commented them out and now it add's the objects to my NSMutableArray. I'm pretty new to NSUserdefaults so I don't know exactly how to use it atm lol
-(void)save
{
//make another array to save the scores.
saveHighScore = [NSUserDefaults standardUserDefaults];
//[saveHighScore setObject:scoreListNew forKey:#"DodgerAppBeta"];
[saveHighScore synchronize];
//[[NSUserDefaults standardUserDefaults] setObject:scoreListTestOne forKey:#"DodgerBeta"];
//[[NSUserDefaults standardUserDefaults] synchronize];
}
-(void)load
{
saveHighScore = [NSUserDefaults standardUserDefaults];
//scoreListNew = [[saveHighScore objectForKey:#"DodgerAppBeta"] mutableCopy];
printableScoreList = [[saveHighScore objectForKey:#"DodgerAppBeta"] mutableCopy];
//NSLog(#"scoreListTestOne %#", scoreListTestOne);
//[printableScoreList addObject:[[NSUserDefaults standardUserDefaults] objectForKey:#"Dodger"]];
NSLog(#"PSL %#", printableScoreList);
}

Initialization of an NSDictionary in Objective C (for iOS)

I am relatively new to Objective-C and now I have a problem in my iPhone app that I don't fully understand.
I try to use a NSMutableDictionary, this does not seem to work as i expect for some reason. When I run the debugger and do po numberToCallerMap to see the dictionary, I get an exception. I have read the documentation for NSMutableDictionary on how to initialize it, but I can not see what I am doing wrong. Help and advice are appreciated. The variable causing me problem is numberToCallerMap, here is the relevant function:
- (void)setData:(NSString*)value{
[list release];
list = [[NSMutableArray alloc] init];
SBJSON *json = [[[SBJSON alloc] init] autorelease];
NSMutableDictionary* numberToCallerMap;
CallerInfo* caller;
NSDictionary* callerInfo;
#try {
NSArray *array = (NSArray*)[json objectWithString:value];
// reading all the items in the array one by one
numberToCallerMap = [NSMutableDictionary dictionary];
for (id *item in array) {
// if the item is NSDictionary (in this case ... different json file will probably have a different class)
NSDictionary *dict2 = (NSDictionary *) item;
CallInfo *data = [CallInfo alloc];
[data initFromDictionary:dict2];
callerInfo = (NSDictionary*)[dict2 valueForKey:#"caller"] ;
//Here, we want the phonenumber to be part of the CallerInfo object instead.
// It is sent from the server as part of the Call-object
NSString* number = (NSString*)[dict2 valueForKey:#"phoneNumber"];
[callerInfo setValue:number forKey:#"phoneNumber"];
caller = (CallerInfo*)[numberToCallerMap valueForKey:number];
if(caller == nil || [caller isKindOfClass:[NSNull class]]){
caller = [CallerInfo alloc];
[caller initFromDictionary:callerInfo];
[numberToCallerMap setValue:caller forKey:number];
[list insertObject:caller atIndex:0];
}
[caller addRecentCall:data];
}
}
#catch (NSException * e) {
[list release];
list = [[NSMutableArray alloc] init];
}
#finally {
[numberToCallerMap release];
}
}
This is probably not the only problem, but you are not alloc-ing your numberToCallerMap dictionary, you are getting it from a convenience class method -- [NSMutableDictionary dictionary] -- that returns it autoreleased. So you should not call release on it yourself.

nested NSAutoreleasePool

In one of my applications for iPad I am building up the db remotely using a json string then converted to NSArray to be insert in core data and then I donwload around 600Mb of images on the ipad. All this is created in a background thread causing from the beginning some memory issue.
I get hold of the problem nesting 3 different NSAutoreleasePool in the operation and releasing each of them at a convenient point. I got no error, nor leak, nor warning. I was just wondering if it is a good way of doing it or I just miss something.
Here a schematic example (the real code is quite long):
- (void)main{
#try {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // first pool
[managedOC lock];
NSArray *results = [self loadEntitiesWithGroupName:#"Project" andName:#"projects"];
NSAutoreleasePool *prjPool; // second pool
for (NSDictionary *thisResult in results) {
prjPool = [[NSAutoreleasePool alloc] init];
Project *prj = [[Project alloc] initWithEntity:[NSEntityDescription entityForName:#"Project" inManagedObjectContext:self.managedOC] insertIntoManagedObjectContext:self.managedOC];
prj.name = [thisResult objectForKey:#"name"];
[prj saveThumbnail:[thisResult objectForKey:#"thumbnail"]];
//Slides. Those are the images that take so mutch space.
NSArray *slides = [thisResult objectForKey:#"slides"];
NSAutoreleasePool *slidePool; // third pool
if(slides != kCFNull){
slidePool = [[NSAutoreleasePool alloc] init];
for(NSDictionary *slide in slides){
Slide *thisSlide = [[Slide alloc] initWithEntity:[NSEntityDescription entityForName:#"Slide" inManagedObjectContext:self.managedOC] insertIntoManagedObjectContext: self.managedOC];
thisSlide.path = prj.path;
[thisSlide saveFile:[slide objectForKey:#"file"]];
[prj addSlidesObject:thisSlide];
[thisSlide release];
[slidePool drain];
}
}
[prj release];
[result release];
[prjPool drain];
}
[self.managedOC unlock];
[totResult release];
[pool drain];
}
#catch (NSException * e) {
}
I'm surprised your code doesn't crash. -drain behaves the same as -release in the reference counted environment, so the following over releases the pool
slidePool = [[NSAutoreleasePool alloc] init]; // this is in the wrong place
for(NSDictionary *slide in slides){
Slide *thisSlide = [[Slide alloc] initWithEntity:[NSEntityDescription entityForName:#"Slide" inManagedObjectContext:self.managedOC] insertIntoManagedObjectContext: self.managedOC];
thisSlide.path = prj.path;
[thisSlide saveFile:[slide objectForKey:#"file"]];
[prj addSlidesObject:thisSlide];
[thisSlide release];
[slidePool drain];
}
Unless there is only one object in the slides collection. You need this:
for(NSDictionary *slide in slides){
slidePool = [[NSAutoreleasePool alloc] init]; // this is in the right place
Slide *thisSlide = [[Slide alloc] initWithEntity:[NSEntityDescription entityForName:#"Slide" inManagedObjectContext:self.managedOC] insertIntoManagedObjectContext: self.managedOC];
thisSlide.path = prj.path;
[thisSlide saveFile:[slide objectForKey:#"file"]];
[prj addSlidesObject:thisSlide];
[thisSlide release];
[slidePool drain];
}
Other than that, you have the right general idea.
You should drain your outermost pool in the finally block of your exception handler so that it is not skipped if an exception is raised i.e. you should do this:
- (void)main{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // first pool
#try {
// Do al your stuff
}
#catch (NSException * e) {
}
#finally
{
[pool drain];
}
}