Process -
NSObject Class used to generate a card with certain properties. This is added to a MutableArray and used accordingly. However, after the function to determine the hand outcome in another class, the MutableArray loses all it's values.
Now I know a MutableArray simply points to the objects as opposed to holding them, so for it to lose all it's values I'm assuming the objects are being swept up by ARC.
-(void)rankHand {
NSString *echo = [Hand returnHandRank:_hand withString:false]; // 7 values in _hand
// 0 values in _hand.
NSLog(@"%@", echo);
}
After breakpointing to see the issue, the issue arises after returnHandRank: withString:
@interface Cards : NSObject
@property (nonatomic, strong) NSString *face;
@property (nonatomic, strong) NSString *suit;
@property (nonatomic, strong) NSString *symbol;
@property (nonatomic) int prime;
@property (nonatomic) int rankByInt;
+(NSMutableArray*)createDeck:(id)sender {
[sender removeAllObjects];
NSArray *faces = [[NSArray alloc] initWithObjects:@"A",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10",@"J",@"Q",@"K", nil];
NSArray *suits = [[NSArray alloc] initWithObjects:@"h",@"d",@"c",@"s", nil];
NSArray *primes = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:41],[NSNumber numberWithInt:2],[NSNumber numberWithInt:3],[NSNumber numberWithInt:5],[NSNumber numberWithInt:7],[NSNumber numberWithInt:11],[NSNumber numberWithInt:13],[NSNumber numberWithInt:17],[NSNumber numberWithInt:19],[NSNumber numberWithInt:23],[NSNumber numberWithInt:29],[NSNumber numberWithInt:31],[NSNumber numberWithInt:37], nil];
for (int i = 0; i < 52; i++) {
Cards *card = [[Cards alloc]init];
card.face = [NSString stringWithFormat:@"%@", faces[i % 13]];
card.suit = [NSString stringWithFormat:@"%@", suits[i / 13]];
card.rankByInt = i % 13;
card.symbol = [Cards symbolForSuit:card.suit];
card.prime = [[primes objectAtIndex:(i % 13)] intValue];
[sender addObject:card];
}
[sender shuffle];
return sender;
}
Creates the _deck
then _hand
is filled by
[_hand addObject:[_deck objectAtIndex:0]];
[_hand addObject:[_deck objectAtIndex:1]];
[_hand addObject:[_deck objectAtIndex:3]];
[_hand addObject:[_deck objectAtIndex:4]];
[_hand addObject:[_deck objectAtIndex:5]];
[_hand addObject:[_deck objectAtIndex:7]];
[_hand addObject:[_deck objectAtIndex:9]];
returnHandRank: withString:
is a very long function in the Hand
class. So that is why I'm assuming they're not being retained.
Can anyone elaborate? I see it pointless to add the cards again from the _deck
, it will it be the best solution?
EDIT: Added returnHandRank: withString:
+(NSString *)returnHandRank:(id)cards withString:(BOOL)returnString {
NSArray *combinations = [self returnCombinations];
cards = [self organizeCardsRankOrder:cards];
__block int maxRank = 0;
__block int maxValue = 0;
for (int i = 0; i < [combinations count]; i++) {
NSArray *splitString = [combinations[i] componentsSeparatedByString:@" "]; // splits the combination string.
NSArray *pointerArray = [[NSArray alloc] initWithObjects:
[NSNumber numberWithInt:[splitString[0] intValue]],
[NSNumber numberWithInt:[splitString[1] intValue]],
[NSNumber numberWithInt:[splitString[2] intValue]],
[NSNumber numberWithInt:[splitString[3] intValue]],
[NSNumber numberWithInt:[splitString[4] intValue]],
nil]; // turns the combinations into int values in an array.
NSMutableArray *fiveCardHand = [[NSMutableArray alloc] initWithObjects:
[cards objectAtIndex:[[pointerArray objectAtIndex:0] intValue]],
[cards objectAtIndex:[[pointerArray objectAtIndex:1] intValue]],
[cards objectAtIndex:[[pointerArray objectAtIndex:2] intValue]],
[cards objectAtIndex:[[pointerArray objectAtIndex:3] intValue]],
[cards objectAtIndex:[[pointerArray objectAtIndex:4] intValue]],
nil]; // Create the 5 card hand for the combination loop we are in, we'll now check this to see what rank it returns.
//Check for hand rank.
fiveCardHand = [self organizeCardsRankOrder:fiveCardHand];
NSArray *fiveCardHandOrganized = fiveCardHand;
int strength = [self handRankWithFiveCards:fiveCardHandOrganized];
if (strength > maxRank) {
maxRank = strength;
maxValue = 0;
}
int value = [self associateStrengthToHand:fiveCardHandOrganized andHand:strength];
if (value > maxValue) {
maxValue = value;
}
}
if (returnString) {
return [self handForStrengthWithStrength:maxRank];
} else {
return [NSString stringWithFormat:@"%i", maxValue];
}
}
There have been a few recent question involving combinations, so unless you are creating accounts we suspect there is homework afoot... No problem, let's see if we can point you in the right direction. However we cannot answer the question, not because it might be homework but because there is not sufficient information to do so.
Now I know a MutableArray simply points to the objects as opposed to holding them,
Correct so far...
so for it to lose all it's values I'm assuming the objects are being swept up by ARC.
but now completely wrong :-( You are misunderstanding how automatic memory management in Objective-C works. First forget "retain", modern ARC-based management is about ownership - whether a variable storing a reference asserts ownership over the object the reference references. When it does assert ownership the variable has the attribute strong, when it stores a reference but does not assert ownership then it has the attribute weak (there are some other ownership attributes you will come across later, they can be ignored for the moment). Object reference variables by default have the attribute strong.
Let's try an analogy:
An unannotated variable defaults to strong, so when a reference is stored in it the variable asserts ownership of the referenced object and it will not be cleared away by ARC. An instance variable of a class, or a standard (strong) property, all assert ownership. All the standard collections (arrays, dictionaries, sets) assert ownership over the objects referenced by the references stored in the collection.
Therefore, if you store a reference in an NSMutableArray
the referenced object will not be cleared away by ARC as long as the reference remains in the array. If you mutate the array and remove a reference then the object referenced by it will be recycled (returned to the available memory pool) by ARC if and only if there are no other references to it stored in strong variables.
The array itself will stay around as long as a reference to it is stored in a strong variable. When there is no strong reference remaining to the array the array itself will be recycled by ARC, in the process all references stored in the array will be removed and if those references are the last strong ones to the referenced objects they too will be recycled.
Hope that helps and understanding how this works will help you find out where you are either emptying your array, or losing all strong references to the array itself; e.g. by assigning a new reference (or nil
) to the variable(s) referencing the array.
Now let's look at some of your code:
NSArray *suits = [[NSArray alloc] initWithObjects:@"h",@"d",@"c",@"s", nil];
This is old style syntax, you can more easily create an NSArray
using an array literal, @[ ... ]
:
NSArray *suits = @[@"h", @"d", @"c", @"s"];
There are no NSMutableArray
literals so you use an NSArray
one an make a mutable copy: [@[ ... ] mutableCopy]
or the shorter @[ ... ].mutableCopy
(opinions differ on the use of the latter). There is also a literal for NSNumber
objects, your code:
[NSNumber numberWithInt:41]
can simply be replaced by @41
.
Using the above literals will make your code shorter and easier to read.
Now your statement:
card.face = [NSString stringWithFormat:@"%@", faces[i % 13]];
suggests a misunderstanding of how references and immutable objects work. An NSString
object is immutable, once created its value will never change. The method stringWithFormat:
constructs an NSString
according to its format and arguments, which in this case is a single string, so you are just copying the string equivalent to:
card.face = [faces[i % 13] copy];
However a copy of an immutable object is just the original object. You know faces
contains only immutable strings as you create it using string literals, so the above is equivalent to:
card.face = faces[i % 13];
Important: You can use a mutable, NSMutableString
, reference as an NSString
one by sub-classing, so the last step here dropping the copy
is only valid if you know the reference is to an NSString
object and not to an NSMutableString
one.
Having used direct indexing on faces
and suits
you switch to long form:
card.prime = [[primes objectAtIndex:(i % 13)] intValue];
and in a few other places. All of them can be replaced by [...]
, e.g.:
card.prime = [[primes[i % 13] intValue];
While you uses of division (i / 13
) and remainder (i % 13
) are all correct you might want to consider using two nested loops to avoid them, e.g. something like:
for(int suitRank = 0; suitRank < 4; suitRank++)
{ for(int cardRank = 0; cardRank < 13; cardRank++)
{ // now use suitRank for i / 13 and cardRank for i % 13
The above is all just tidying up to make your code shorter, more readable, and less error prone. Now a more serious issue:
+(NSMutableArray*)createDeck:(id)sender {
[sender removeAllObjects];
Never do this! While id
has it uses it reduces the compilers ability to check your code is correct and can result in your code going wrong when it is run for simple errors the compiler would have caught. Here sender
is clearly meant to be a reference to a mutable array, declare it as such:
+ (NSMutableArray *)createDeck:(NSMutableArray *)sender
{
[sender removeAllObjects];
Later (after applying the above use of literals) you have:
NSMutableArray *fiveCardHand = @[ cards[[pointerArray[0] intValue]],
...
].mutableCopy;
//Check for hand rank.
fiveCardHand = [self organizeCardsRankOrder:fiveCardHand];
Here you:
fiveCardHand
fiveCardHand
with the result of organizeCardsRankOrder:
So here you appear not to have mutated the array referenced by fiveCardHand
but instead changed the variable to reference a different array. You don't need to use mutable arrays to do that, you are mutating the variable holding the reference not the referenced array. Now "appear" was used here as you have not supplied the code of organizeCardsRankOrder:
, maybe that method does mutate the array passed to it, if that is the case it does not need to also return it and there is no need for the assignment to the variable. So look at your code carefully here and decide whether you are mutating arrays or just variables and change it accordingly.
Finally you do not provide any declarations in the question for _deck
and _hand
. By naming convention you might be directly accessing the backing variable of a property (doing this is often best avoided), or accessing an instance variable, both of some unspecified class. Therefore we cannot provide any real help with these, just check that if they are connected to an instance that you are using the same instance everywhere you expect to - a common early error is to set an instance variable in one instance, try to read it from another instance, and then wonder why the value is different...
HTH, happy debugging!