NSArray problems

Discussion in 'Mac Programming' started by theopavlakou, Jul 20, 2011.

  1. theopavlakou, Jul 20, 2011
    Last edited by a moderator: Jul 20, 2011

    theopavlakou macrumors newbie

    Joined:
    Jul 20, 2011
    #1
    Hey,
    I have made a program that basically takes 2 parents blood groups (genotypes) and finds all the combinations that the baby's genotype could be, stores it in an NSArray and then selects a random one for the baby.

    i.e. if the genotypes were OA for the father and BO for the mother, the baby's possible genotypes would be: OB, OO, AB, AO.

    The problem however is that the array initially seems to store all the right values, but when it saves it returns the array later it seems to save an array of only AO (it saves this 4 times) and so the baby no matter what seems to have AO always.

    Here is the code:

    Human.h

    Code:
    #import <Cocoa/Cocoa.h>
    #import "ArrayCategory.h"
    #import "BloodType.h"
    
    @class BloodType;
    
    
    @interface Human : NSObject
    {
    	BloodType * blood;
    	
    }
    
    -(Human *) makeBabyWith: (Human *) other;
    -(id) init;
    -(void) dealloc;
    
    
    @property (nonatomic, retain) BloodType * blood;
    
    
    
    @end
    

    Human.m

    Code:
    #import "Human.h"
    
    @implementation Human
    
    -(Human *) makeBabyWith: (Human *) other
    {
    	Human * baby = [[Human alloc]init];
    	
    	NSArray * arr = [NSArray arrayWithArray:[self.blood getAllCombosWith: other.blood]];
    	
    	baby.blood = [arr tpRandomObject];
    	[arr release];
    	
    	//baby.blood = [[self.blood getAllCombosWith: other.blood] tpRandomObject];
    	
    	return baby;
    }
    
    -(id) init
    {
    	if (self =[super init])
    	{
    	
    		
    		blood = [[BloodType alloc] init ];
    	
    		nos = 3;
    		
    	}
    	return (self);
    
    }
    
    -(void) dealloc
    {
    	[blood release];
    	[super dealloc];
    }
    
    -(NSString *) description
    {
    	return [self.blood description];
    }
    
    
    @synthesize blood, nos;
    
    @end
    
    BloodType.h
    Code:
    #import <Cocoa/Cocoa.h>
    #import "ArrayCategory.h"
    
    
    typedef enum {O = 1, A, B} bloodType;
    
    @interface BloodType : NSObject 
    {
    	bloodType allele1;
    	bloodType allele2;
    	int number;
    
    }
    
    
    -(NSString *) alleleAsString;
    -(NSMutableArray *) getAllCombosWith: (BloodType *) otherBlood;
    
    @property bloodType allele1;
    @property  bloodType allele2;
    @property int number;
    @end
    
    BloodType.m

    Code:
    #import "BloodType.h"
    
    
    @implementation BloodType
    
    -(NSArray *) getAllCombosWith: (BloodType *) otherBlood
    {
    	NSMutableArray * array = [NSMutableArray arrayWithCapacity:4];
    	BloodType * tempBlood = [[BloodType alloc]init];
    	tempBlood.allele1 = self.allele1;
    	tempBlood.allele2 = otherBlood.allele1; //OB
    	[array addObject:tempBlood];
    	
    	tempBlood.allele1 = self.allele1;
    	tempBlood.allele2 = otherBlood.allele2;
    	[array addObject:tempBlood]; //OO
    
    	tempBlood.allele1 = self.allele2;
    	tempBlood.allele2 = otherBlood.allele1;
    	[array addObject:tempBlood]; //AB
    
    	tempBlood.allele1 = self.allele2;
    	tempBlood.allele2 = otherBlood.allele2;
    	[array addObject:tempBlood]; //AO
    	
    	[tempBlood release];
    	
    	return array;
    }
    
    -(NSString *) alleleAsString
    {
    	//NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt: 0],@"O", [NSNumber numberWithInt: 1], @"A",[NSNumber numberWithInt: 2], @"B", nil];
    	
    	NSMutableString * a1 = [[NSMutableString alloc]init];
    	NSMutableString * a2 = [[NSMutableString alloc]init];
    
    	
    	switch (self.allele1) {
    		case O:
    			[a1 appendString: @"O"];
    			break;
    		case A:
    			[a1 appendString: @"A"];
    			break;
    		case B:
    			[a1 appendString: @"B"];
    			break;
    		default:[a1 appendString: @"***There was an error!***"];
    			break;
    	}
    	
    	//NSLog(@"this is %@", a1);
    	
    	switch (self.allele2) {
    		case O:
    			[a2 appendString: @"O"];
    			break;
    		case A:
    			[a2 appendString: @"A"];
    			break;
    		case B:
    			[a2 appendString: @"B"];
    			break;
    		default:[a2 appendString: @"***There was an error!***"];
    			break;
    	}
    	
    	//NSLog(@"this is %@", a2);
    
    	
    	[a1 autorelease];
    	[a2 autorelease];
    	
    	NSString * retString = [NSString stringWithFormat: @"blood genotype is %@ %@", a1  , a2];
    	[retString autorelease];
    	
    	return (retString);
    	
    
    	
    	
    	//return (@"My blood genotype is %@ %@", [dict objectForKey: [NSNumber numberWithInt: self.allele1]], 
    	//		[dict objectForKey: [NSNumber numberWithInt: self.allele2]]); //Check how to fix
    	
    	
    }
    
    ArrayCategory.h
    Code:
    #import <Cocoa/Cocoa.h>
    
    
    @interface NSArray (ArrayCategory)
    
    -(id) tpRandomObject;
    
    @end
    
    ArrayCategory.m
    Code:
    #import "ArrayCategory.h"
    
    
    @implementation NSArray (ArrayCategory)
    
    -(id) tpRandomObject
    {
    	
    	long int x = arc4random();
    
    	int y = x % 4;
    	
    	return [self objectAtIndex: y];
    }
    
    @end
    
    main.m
    Code:
    #import <Foundation/Foundation.h>
    #import "Human.h"
    #import "BloodType.h"
    #import "ArrayCategory.h"
    
    
    int main (int argc, const char * argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    	Human * Joe = [[Human alloc]init];
        Human * Jenny = [[Human alloc]init];
    	Human * baby = [[Human alloc]init];
    	
    	
    	Joe.blood.allele1 = O;
    	Joe.blood.allele2 = A;
    	//NSLog(@"%@", Joe);
    	NSLog(@"Joe's %@ ", [Joe.blood description]);
    
    
    	Jenny.blood.allele1 = B;
    	Jenny.blood.allele2 = O;
    	NSLog(@"Jenny says \"My %@\"", Jenny);
    
    	[Joe.blood setNumber: 5];
    	[Joe setNos:6];
    	
    	baby = [Joe makeBabyWith: Jenny];
    	NSLog(@"The baby's %@", baby);
    	// The baby's blood genotype is A O
    	
    	
    	NSLog(@"%i", [Joe retainCount] ); //1
    	NSLog(@"%i", [Jenny retainCount] ); //1
    	NSLog(@"%i", [baby retainCount] ); //1
    	NSLog(@"%i", [Joe.blood retainCount] ); //1
    
    
    	
    	[Joe release];
    	[Jenny release];
    	[baby release];
        [pool drain];
        return 0;
    }
    
    And that's it, I'm not sure if I have outlined the problem enough, but please if you could help, please do.
     
  2. jiminaus macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #2
    You're problem is in getAllCombosWith:

    Adding an object to an array doesn't add a copy of the object, it just retains the object

    So in getAllCombosWith: you only creating one BloodType object which you keep adding and changing. You need to create and add 4 BloodType objects.
     
  3. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #3
    You could eliminate the NSArray completely.

    Randomly choose between the father's two alleles (50% probability).
    Randomly choose between the mother's two alleles (50% probability).
    Make a new BloodType from the two chosen alleles.

    No NSArray is needed. No category on NSArray is needed. No temporary BloodType objects filling the array are needed.
     
  4. theopavlakou thread starter macrumors newbie

    Joined:
    Jul 20, 2011
    #4
    Thank you

    Hey man thanks a lot, I still haven't got it working but I am starting to understand what you are trying to explain to me. Please have a little more patience with me when I ask this :p Why doesn't this work then? I get an EXC_BAD_ACCESS error with it. Sorry, memory management is something I have to start understanding a little bit more:

    New method:

    -(NSMutableArray *) getAllCombosWith: (BloodType *) otherBlood
    {
    NSMutableArray * array = [NSMutableArray arrayWithCapacity:4];

    BloodType * tempBlood = [[BloodType alloc]init];
    tempBlood.allele1 = self.allele1;
    tempBlood.allele2 = otherBlood.allele1;
    NSLog(@"%@", tempBlood);
    [array addObject:tempBlood];
    [tempBlood release];

    NSLog(@"Retain count of tempBlood is: %i", [tempBlood retainCount]);


    BloodType * tempBlood2 = [[BloodType alloc]init];
    //NSLog(@"Retain count is: %@", [tempBlood retainCount]);

    tempBlood2.allele1 = self.allele2;
    tempBlood2.allele2 = otherBlood.allele2;
    NSLog(@"%@", tempBlood2);
    [array addObject:tempBlood2];
    [tempBlood2 release];


    BloodType * tempBlood3 = [[BloodType alloc]init];
    tempBlood3.allele1 = self.allele1;
    tempBlood3.allele2 = otherBlood.allele2;
    NSLog(@"%@", tempBlood3);
    [array addObject:tempBlood3];
    [tempBlood3 release];

    BloodType * tempBlood4 = [[BloodType alloc]init];
    tempBlood4.allele1 = self.allele2;
    tempBlood4.allele2 = otherBlood.allele1;
    NSLog(@"%@", tempBlood4);
    [array addObject:tempBlood4];
    [tempBlood4 release];

    NSLog(@"Retain count of tempBlood is: %i", [tempBlood retainCount]);

    //NSLog(@"Retain count is: %i", [array retainCount]);

    for (BloodType * blood in array) //Fast enumeration
    {
    NSLog(@"%@", blood);
    }


    [tempBlood release];
    [tempBlood2 release];
    [tempBlood3 release];
    [tempBlood4 release];


    NSArray * arr2 = [NSArray arrayWithArray: array];
    [arr2 writeToFile: @"/Users/theopavlakou/Desktop/blood.txt" atomically: YES];

    return array;
    }
     
  5. theopavlakou thread starter macrumors newbie

    Joined:
    Jul 20, 2011
    #5
    Hey man, yeah I know I can do that too, but the reason I'm doing it like this is because I'm new to Obj C (and to object oriented programming in general) and I want to understand memory management basically. But thanks :)
     
  6. theopavlakou thread starter macrumors newbie

    Joined:
    Jul 20, 2011
    #6
    Okay, i finally got it working, but I am worried about memory management, now, as I have gathered my retain count on all tempBloods is 2, and I haven't released them, this isn't healthy right?


    -(NSMutableArray *) getAllCombosWith: (BloodType *) otherBlood
    {
    NSMutableArray * array = [NSMutableArray arrayWithCapacity:4];

    BloodType * tempBlood = [[BloodType alloc]init];
    tempBlood.allele1 = self.allele1;
    tempBlood.allele2 = otherBlood.allele1;
    NSLog(@"%@", tempBlood);
    [array addObject: tempBlood];


    NSLog(@"Retain count of tempBlood is: %i", [tempBlood retainCount]);


    BloodType * tempBlood2 = [[BloodType alloc]init];
    //NSLog(@"Retain count is: %@", [tempBlood retainCount]);

    tempBlood2.allele1 = self.allele2;
    tempBlood2.allele2 = otherBlood.allele2;
    NSLog(@"%@", tempBlood2);
    [array addObject: tempBlood2];


    BloodType * tempBlood3 = [[BloodType alloc]init];
    tempBlood3.allele1 = self.allele1;
    tempBlood3.allele2 = otherBlood.allele2;
    NSLog(@"%@", tempBlood3);
    [array addObject: tempBlood3];

    BloodType * tempBlood4 = [[BloodType alloc]init];
    tempBlood4.allele1 = self.allele2;
    tempBlood4.allele2 = otherBlood.allele1;
    NSLog(@"%@", tempBlood4);
    [array addObject: tempBlood4];
    NSLog(@"Retain count of tempBlood4 is: %i", [tempBlood4 retainCount]);


    NSLog(@"Retain count of tempBlood4 is: %i", [tempBlood4 retainCount]);




    //NSLog(@"Retain count is: %i", [array retainCount]);

    for (BloodType * blood in array) //Fast enumeration
    {
    NSLog(@"%@", blood);
    }

    /*

    NSArray * array2 = [NSArray arrayWithObjects: @"I", @"am", @"Theo", nil];

    if ([array2 writeToFile: @"/Users/theopavlakou/Desktop/blood.txt" atomically: YES] == NO)
    {NSLog(@"Not Successful");}

    if ([array writeToFile: @"/Users/theopavlakou/Desktop/blood.txt" atomically: YES] == NO)
    {NSLog(@"Not Successful");}//Why not working? Because can't print descriptions, needs NSStrings.
    */

    /* [tempBlood release];
    [tempBlood2 release];
    [tempBlood3 release];
    [tempBlood4 release];
    */

    NSArray * arr2 = [NSArray arrayWithArray: array];
    [arr2 writeToFile: @"/Users/theopavlakou/Desktop/blood.txt" atomically: YES];

    return array;
    }


    Thank you so much by the way :)
     
  7. jiminaus macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #7
    The block of code sending release to tempBlood1, tempBlood2, tempBlood3 and tempBlood4 is still commented out.

    Also you haven't sent release any of the arrays you've created.

    A pattern that you can use is to autorelease at creation. For example, instead of this:
    Code:
    BloodType *tempBlood1 = [[BloodType alloc] init];
    // later in the same method
    [tempBlood1 release];
    Do this instead:
    Code:
    BloodType *tempBlood1 = [[[BloodType alloc] init] autorelease];
     
  8. theopavlakou thread starter macrumors newbie

    Joined:
    Jul 20, 2011
    #8
    Thank you

    Hey man, yeah I know that it is commented out, you see every so often I get an EXC_BAD_ACCESS error, but not every time and I am not sure why, whether or not I release the memory (although I did release the second array), at the end, the array that I return, am I meant to somehow release that too? Also, any ideas why I am getting the error? I think maybe it's due to memory management elsewhere as well.
    Thanks
     
  9. theopavlakou thread starter macrumors newbie

    Joined:
    Jul 20, 2011
    #9
    Also

    Hey also, how does memory management look in here? Thanks man. If I'm being a pain tell me and I will stop :p



    -(NSString *) alleleAsString
    {

    NSMutableString * a1 = [[NSMutableString alloc]init];
    NSMutableString * a2 = [[NSMutableString alloc]init];


    switch (self.allele1) {
    case O:
    [a1 appendString: @"O"];
    break;
    case A:
    [a1 appendString: @"A"];
    break;
    case B:
    [a1 appendString: @"B"];
    break;
    default:[a1 appendString: @"***There was an error!***"];
    break;
    }



    switch (self.allele2) {
    case O:
    [a2 appendString: @"O"];
    break;
    case A:
    [a2 appendString: @"A"];
    break;
    case B:
    [a2 appendString: @"B"];
    break;
    default:[a2 appendString: @"***There was an error!***"];
    break;
    }




    //[a1 autorelease];
    //[a2 autorelease];

    NSString * retString = [NSString stringWithFormat: @"blood genotype is %@ %@", a1 , a2];
    [retString autorelease];


    [a1 release];
    [a2 release];

    return (retString);







    }

    Thanks again :)
     
  10. ChOas macrumors regular

    Joined:
    Nov 24, 2006
    Location:
    The Netherlands
    #10
    No need to autorelease retString. You created it using a convenience method so you don't really 'own' it.
     
  11. theopavlakou thread starter macrumors newbie

    Joined:
    Jul 20, 2011
    #11
    Thanks again

    Hey man, yeah, just realised that, and also the arrays in bloodType.m also don't need to be released either. Thanks a lot man. If we were in the same country I would take you out for a drink :p
     
  12. gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #12
    Now look at the method "makeBabyWith" and at the rules for object ownership. Does the method name start with "copy" or "create"? So should the object that the method returns be autoreleased or not?
     

Share This Page