SpokaneDude
SpokaneDude

Reputation: 4974

Why is my class (struct) not being added to the NSMutableArray correctly?

I have two (2) classes defined in one .m file:

@interface vendorData : NSObject  {  //  <  first class

@public
  NSString *vendor;
  NSString *price;
  NSString *condition;
}
@end
@implementation vendorData

@end
@interface DetailViewController () < UIAlertViewDelegate>

@end

@implementation DetailViewController  //  <--  second (main) class

//  code for the main class follows

I initialize both the class and NSMutableArray like this:

NSMutableArray *arrayOfPrices = [[NSMutableArray alloc]initWithCapacity:100];
vendorData *data = [[vendorData alloc] init];

I fill the class data using NSScanner, then put the vendorData object into a NSMutableArray; this is how I'm populating the data object:

    NSData *siteHTMLData = [NSData dataWithContentsOfURL: siteUrl];
siteHTMLData = [NSData dataWithContentsOfURL: siteUrl];  //  get past first page ("searching")
NSString *htmlString = [[NSString alloc] initWithData:siteHTMLData encoding:NSUTF8StringEncoding];

//  start the search
NSScanner *scanner = [[NSScanner alloc] initWithString:htmlString];
NSString *priceString, *vendorString, *conditionString;
vendorData *data = [[vendorData alloc]init];

do {
   @try {
        
        
        //  find vendor
        [scanner scanUpToString:@"<tr  id=\"" intoString:nil];
        scanner.scanLocation += 9;

        [scanner scanUpToString: @"\r\n" intoString: &vendorString];  //  find end of vendor
        data->vendor = vendorString;
        
        //  find price
        scanner.scanLocation += 2;
        [scanner scanUpToString:@"<span >$" intoString:nil];  //  find beginning of price
        scanner.scanLocation += 8;
        [scanner scanUpToString: @"</span>" intoString: &priceString];  //  find end of price
        data->price = priceString;
        scanner.scanLocation += 7;
    }
    
    @catch (NSException *exception)  {
        if([[exception reason] isEqualToString: NSRangeException])
            break;
    }
    
    // see if vendor/price is already in array; if so ignore it
    
    NSLog(@"\ndata->vendor: %@,\n->price: %@", data->vendor, data->price);  //  <---------
    [arrayOfPrices addObject:data];  //  add struct to array
    

    
} while (([scanner isAtEnd] == NO) || (scanner.scanLocation > htmlString.length));

This is a portion of the log:

2014-08-24 11:53:03.154 BookInventoryManager[3741:60b] data->vendor: Thriftbooks.comUsed", ->price: 1.00 2014-08-24 11:53:03.155 BookInventoryManager[3741:60b] data->vendor: ValoreBooksUsed", ->price: 0.01 2014-08-24 11:53:03.155 BookInventoryManager[3741:60b]

and this is what the debugger shows in the arrayOfPrices array:

enter image description here

Amazon.ca.Marketplace should be the last entry added to arrayOfPrices, but for some reason it appears in each and every element of the mutable array. Why?

Upvotes: 0

Views: 37

Answers (2)

meda
meda

Reputation: 45490

Every time you want to append a element you should create a new instance of the object.

You can create your own custom init to make it easier. Here is an example:

@interface vendorData : NSObject  { 

@public
  NSString *vendor;
  NSString *price;
  NSString *condition;
}
-(id) initWithVendor:(NSString) vendor price:(NSString *)price  condition:(NSString *) condition;
@end

@implementation vendorData
-(id) initWithVendor:(NSString) vendor price:(NSString *)price  condition:(NSString *) condition{
    self = [super init];
    if(self) {
        self.vendor = vendor;
        self.price = price;
        self.condition = condition;
    }
    return(self);
}
@end

And here is how you would fill your mutable array:

NSMutableArray *arrayOfPrices = [[NSMutableArray alloc]initWithCapacity:100];
vendorData *data1 = [[vendorData alloc] initWithVendor:@"Amazon" price:@"31.75" condition: nil];
[arrayOfPrices addObject:data1];
vendorData *data2 = [[vendorData alloc] initWithVendor:@"Ebay" price:@"20.00" condition: @"new"];
[arrayOfPrices addObject:data2];  

EDIT: thank you for updating your post, all you need to do is move the object declaration inside of the do-while loop:

do {
vendorData *data = [[vendorData alloc]init];
   @try {
         ...

or if you want to use the custom init in my example, use it at like this:

do {
   @try {

        //  find vendor
        [scanner scanUpToString:@"<tr  id=\"" intoString:nil];
        scanner.scanLocation += 9;
        [scanner scanUpToString: @"\r\n" intoString: &vendorString];  //  find end of vendor

        //  find price
        scanner.scanLocation += 2;
        [scanner scanUpToString:@"<span >$" intoString:nil];  //  find beginning of price
        scanner.scanLocation += 8;
        [scanner scanUpToString: @"</span>" intoString: &priceString];  //  find end of price
        scanner.scanLocation += 7;
    }

    @catch (NSException *exception)  {
        if([[exception reason] isEqualToString: NSRangeException])
            break;
    }

    // see if vendor/price is already in array; if so ignore it
    vendorData *data = [[vendorData alloc] initWithVendor:vendorString price:priceString condition: nil];
    NSLog(@"\ndata->vendor: %@,\n->price: %@", data->vendor, data->price);  
    [arrayOfPrices addObject:data];  //  add struct to array


} while (([scanner isAtEnd] == NO) || (scanner.scanLocation > htmlString.length));

Upvotes: 1

gnasher729
gnasher729

Reputation: 52530

As with most problems, odds are that this is a bug in your code. That's what always should be your first thought, especially when you are doing something that thousands of people have done before: It's a bug in your code.

We don't have all the code. We have one line where you create a vendorData object. We don't see where you set the vendor and price. We have one line where you add a vendorData object to a mutable array, and we see that the mutable array contains the same object twice.

The most obvious explanation would be that you create a vendorData object once, then set vendor and price to different values, and always add the same vendorData object to the mutable array. Adding an object to a mutable array adds that object, not a copy of it.

Upvotes: 1

Related Questions