0

I am encountering a weird issue that I couldn't find the bug of the testing code that I wrote. I am writing a piece of code to generate AttributedString and put it in NSMutableArray, but when I display the added objects from the NSMutableArray using the For loop, the output is showing the same object with same index 3 times, I am trying to debug it for 2 days now and couldn't find the bug, hopefully I can get some help/advice here.

The code to instantiate the NSMutableArray and adding the AttributedString to the array

-(instancetype)init
{
    self = [super init];

    if (self) {
        for (NSMutableAttributedString *attrString in [SetPlayingCard symbolArray]) {
            for (NSString *attrColor in [SetPlayingCard colorsArray]) {
                if ([attrColor isEqualToString:@"redColor"]) {
                    [attrString setAttributes:@{NSStrokeWidthAttributeName : @3,
                                            NSStrokeColorAttributeName : [UIColor redColor]}
                                    range:NSMakeRange(0, [attrString length])];
                    NSLog(@"%@ %@",attrColor, attrString);
                } else if ([attrColor isEqualToString:@"blueColor"]){
                    [attrString setAttributes:@{NSStrokeWidthAttributeName : @3,
                                            NSStrokeColorAttributeName : [UIColor blueColor]}
                                    range:NSMakeRange(0, [attrString length])];
                    NSLog(@"%@ %@",attrColor, attrString);
                } else if ([attrColor isEqualToString:@"purpleColor"]){
                    [attrString setAttributes:@{NSStrokeWidthAttributeName : @3,
                                            NSStrokeColorAttributeName : [UIColor purpleColor]}
                                    range:NSMakeRange(0, [attrString length])];
                    NSLog(@"%@ %@",attrColor, attrString);
                }
                [self addCard:attrString];
            }
        }
    }
    return self;
}

The Class method that is being called

+ (NSArray *)colorsArray
{
    return @[@"redColor",@"blueColor",@"purpleColor"];
}

+ (NSArray *)symbolArray
{
    NSMutableAttributedString *triangle = [[NSMutableAttributedString alloc] initWithString:@"Triangle"];
    NSMutableAttributedString *square = [[NSMutableAttributedString alloc] initWithString:@"Square"];
    NSMutableAttributedString *round = [[NSMutableAttributedString alloc] initWithString:@"Round"];

    return @[triangle,square,round];
}

The method to add and display the output

- (void)addCard:(NSAttributedString *)card
{
    NSLog(@"String to be added to array: %@", card);
    [self.cards addObject:card];
    NSLog(@"Index is %d for %@", [self.cards indexOfObject:card], card);
}


- (NSAttributedString *)printCard
{
    NSAttributedString *card;

    if ([self.cards count]) {
        for (card in self.cards) {
            NSLog(@"Count: %d, Array index: %d, Card from the deck is: %@, ", [self.cards count], [self.cards indexOfObject:card], card);
        }
    } 
    return card;
}

The output I get from the NSLog in addCard method is as below.

2014-04-01 22:40:56.184 UnitTest[1008:60b] Count: 9, Array index: 0, Card from the deck is: Triangle{
    NSStrokeColor = "UIDeviceRGBColorSpace 0.5 0 0.5 1";
    NSStrokeWidth = 3;
}, 
2014-04-01 22:40:56.185 UnitTest[1008:60b] Count: 9, Array index: 0, Card from the deck is: Triangle{
    NSStrokeColor = "UIDeviceRGBColorSpace 0.5 0 0.5 1";
    NSStrokeWidth = 3;
}, 
2014-04-01 22:40:56.186 UnitTest[1008:60b] Count: 9, Array index: 0, Card from the deck is: Triangle{
    NSStrokeColor = "UIDeviceRGBColorSpace 0.5 0 0.5 1";
    NSStrokeWidth = 3;
}, 
2014-04-01 22:40:56.187 UnitTest[1008:60b] Count: 9, Array index: 3, Card from the deck is: Square{
    NSStrokeColor = "UIDeviceRGBColorSpace 0.5 0 0.5 1";
    NSStrokeWidth = 3;
}, 
2014-04-01 22:40:56.188 UnitTest[1008:60b] Count: 9, Array index: 3, Card from the deck is: Square{
    NSStrokeColor = "UIDeviceRGBColorSpace 0.5 0 0.5 1";
    NSStrokeWidth = 3;
}, 
2014-04-01 22:40:56.189 UnitTest[1008:60b] Count: 9, Array index: 3, Card from the deck is: Square{
    NSStrokeColor = "UIDeviceRGBColorSpace 0.5 0 0.5 1";
    NSStrokeWidth = 3;
}, 
2014-04-01 22:40:56.190 UnitTest[1008:60b] Count: 9, Array index: 6, Card from the deck is: Round{
    NSStrokeColor = "UIDeviceRGBColorSpace 0.5 0 0.5 1";
    NSStrokeWidth = 3;
}, 
2014-04-01 22:40:56.191 UnitTest[1008:60b] Count: 9, Array index: 6, Card from the deck is: Round{
    NSStrokeColor = "UIDeviceRGBColorSpace 0.5 0 0.5 1";
    NSStrokeWidth = 3;
}, 
2014-04-01 22:40:56.192 UnitTest[1008:60b] Count: 9, Array index: 6, Card from the deck is: Round{
    NSStrokeColor = "UIDeviceRGBColorSpace 0.5 0 0.5 1";
    NSStrokeWidth = 3;
},
1
  • 1
    be aware that indexOfObject will give you the first index of an object that isEqual. it doesn't do address comparison. you probably want to use indexOfObjectIdenticalTo Commented Apr 1, 2014 at 15:01

3 Answers 3

2

Your printCard method is wrong change it to:

- (NSAttributedString *)printCard
{
    if ([self.cards count] > 0) {
        for (NSAttributedString *card in self.cards) {
            NSLog(@"Count: %d, Card from the deck is: %@, ", [self.cards count], card);
        }
    } 
    return card;
}

If you want index you can use:

for (int i = 0; i < self.cards.count; i++) {
    NSAttributedString *card = self.cards[i];
    NSLog(@"Count: %d, Array index: %d, Card from the deck is: %@, ", [self.cards count], i, card);
}

You should review how for in loop works.

Sign up to request clarification or add additional context in comments.

1 Comment

I think the OP confusion is less about for-loop and more about equality.
2

In your init method, the outer for loop iterates over each of your 3 strings, while the inner for loop iterates over each of your 3 colors, so a total of 9 calls to addCard:. Thus, three copies of each string in the array, but each with a different color.

The reason that the logs give the same index for each of those objects, is that indexOfObject: uses [NSObject isEqual:] to determine equality. Apparently, NSMutableString's implementation of isEqual: compares only the string content, not colors, so each copy of the string is "the same" as far as the array is concerned.

If you use enumerateObjectsUsingBlock:, you'll see that each object does in fact have a different index.

[self.cards enumerareObjectsUsingBlock:^(NSMutableAttributedString *card, NSUInteger idx, BOOL *stop) {
    NSLog(@"Count: %d, Array index: %d, Card from the deck is: %@, ", [self.cards count], idx, card);
}];

Comments

0

if we examine the code doing the set up, you have (in summary):

foreach symbol:
    foreach color:
        symbol.color = color
        [array addObject:symbol];

What this is actually doing is creating a single NSMutableString for each symbol, and then changing its color and adding to the array 3 times. Since you don't ever create a copy of the string, but instead just change the attributes of a single string, you have the same object in the array 3 times.

Now, when you use indexOfObject: to look that object up, that you added 3 times, it only finds the first instance.

The fix is to create a copy of the string to add instead of adding the original string:

[self addCard:[attrString copy]];

A better solution might be to just return an array of strings:

return @[ @"triangle", @"square", @"round"];

and use a different constructor to create the attributed string:

attrString = [[NSAttributedString alloc] initWithString:shape attributes:color];

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.