iPhone SDK: updating objects in an NSArray - iphone

I have an NSArray of (Product) objects that are created by parsing an XML response from a server.
In the object, it has images, and text, and ints, URLs. etc.
There are 2 requests to the server
1: list of matching products from a search - small amount of detail
2: product details: the full details.
When the second request is parsed I am trying to update the existing object in the array.
- (void) setProduct:(Product *) _product atIndex: (int) index
{
[_product retain];
[productList replaceObjectAtIndex:index withObject:_product];
}
This doesn't seem to work as when I call update and table reloadData, the new values are not present.
Should I remove the object in the array first?

replaceObjectAtIndex: is a method of NSMutableArray. So you would need to do make your productLists a NSMutableArray to use it.

-(void)updateprevious:(int)index withArg2:(NSString *)date
{
NSLog(#"%#",date);
NSLog(#"%d",index);
for (int i=0;i < index; i++)
{
[final_X replaceObjectAtIndex:i withObject:#""];
}
}

You'll have to post more code from your data source methods. What you are doing here should work fine.
Your "retain" method is unnecessary, you're leaking _product.

Related

NSInteger value not valid when passing in a variable, for custom table view class delegate method

I'm testing a custom table view style class:
HorizontalTable
It produces a horizontal table view.
One of the delegate methods equivalent to tableView:numberOfRowsInSection: is:
- (NSInteger)numberOfColumnsForTableView:(HorizontalTableView *)tableView.
If I give this a number (ex: return 10;) it is happy and it give me the number "cells" that I want. But if I feed it a value of someArray.count or an int or NSInteger variable, the table view just comes out blank, delivering no cells.
I think that the method in the custom table view class that receives the NSInteger value is this:
- (NSUInteger)numberOfPages {
NSInteger numPages = 0;
if (_delegate)
numPages = [_delegate numberOfColumnsForTableView:self];
return numPages;
}
Do I need to cast the result of someArray.count to an NSInteger?
Here you get the value from array means your array not nil so just debug and check the if condition that its come in that condition or not and what you get from NSLog
- (NSUInteger)numberOfPages {
NSInteger numPages = 0;
if (_delegate){
numPages = [_delegate numberOfColumnsForTableView:self];
NSLog(#"Total record %d",numPages);//what you get here?
}
return numPages;
}
numPages = [_delegate numberOfColumnsForTableView:self];
//self requires an object of type HorizontalTableView
NSMutableArray is editable, where as NSArray is read-only.
NSMutableArray is a subclass of NSArray and responds to messages such as addObject, removeObject and so forth; i.e. it is mutable, like the name says. Instead, NSArray is immutable, i.e. you can't add/remove objects.
In fact converting the value to be returned to the count of an NSArray, as opposed to the count an NSMutableArray fixes the issue. Why? I am not sure why an NSArray's count value is valid but not the NSMutableArray's. Anyone?

iPhone Table View: Making Sections, UILocalizedIndexedCollation selector

I'm having trouble making the sections in a UITableView. I've looked at the documentation for UILocalizedIndexedCollation as well as this sample code project:
https://developer.apple.com/library/ios/#samplecode/TableViewSuite/Listings/3_SimpleIndexedTableView_Classes_RootViewController_m.html#//apple_ref/doc/uid/DTS40007318-3_SimpleIndexedTableView_Classes_RootViewController_m-DontLinkElementID_18
What I have below is basically a straight copy/paste from the sample project. However, the sample project uses a custom object (TimeZoneWrapper.h) and then places the object in the correct section based on the object's instance variable (TimeZoneWrapper.localeName). However, I'm not using custom objects. I'm using just a bunch of regular NSStrings. So my question is what method on NSString should I pass to the #selector() to compare and place the string in the correct section array?
Currently, I'm calling NSString's copy method as a temporary hack to get things working (which it does), but I'm not sure if this is correct. A little explanation would be much appreciated!
- (void)configureSections {
// Get the current collation and keep a reference to it.
self.collation = [UILocalizedIndexedCollation currentCollation];
NSInteger index, sectionTitlesCount = [[collation sectionTitles] count]; // sectionTitles are A, B, C, etc.
NSMutableArray *newSectionsArray = [[NSMutableArray alloc] initWithCapacity:sectionTitlesCount];
// Set up the sections array: elements are mutable arrays that will contain the locations for that section.
for (index = 0; index < sectionTitlesCount; index++) {
NSMutableArray *array = [[NSMutableArray alloc] init];
[newSectionsArray addObject:array];
}
// Segregate the loctions into the appropriate arrays.
for (NSString *location in locationList) {
// Ask the collation which section number the location belongs in, based on its locale name.
NSInteger sectionNumber = [collation sectionForObject:location collationStringSelector:#selector(/* what do I put here? */)];
// Get the array for the section.
NSMutableArray *sectionLocations = [newSectionsArray objectAtIndex:sectionNumber];
// Add the location to the section.
[sectionLocations addObject:location];
}
// Now that all the data's in place, each section array needs to be sorted.
for (index = 0; index < sectionTitlesCount; index++) {
NSMutableArray *locationsArrayForSection = [newSectionsArray objectAtIndex:index];
// If the table view or its contents were editable, you would make a mutable copy here.
NSArray *sortedLocationsArrayForSection = [collation sortedArrayFromArray:locationsArrayForSection collationStringSelector:#selector(/* what do I put here */)];
// Replace the existing array with the sorted array.
[newSectionsArray replaceObjectAtIndex:index withObject:sortedLocationsArrayForSection];
}
self.sectionsArray = newSectionsArray;
}
Thanks in advance!
You should use #selector(self).
Using #selector(copy) will cause memory leaks in your project

Navigating plists and settings objects

I have a resource file (plist) added to my project. I am currently coding some sort of a "helper" for reading and writing to it. I have 3 get and 3 set methods. First one returns an object at the top, second one returns object which is inside of another dictionary (see code) and the third one returns an object at any given depth I just have to specify the node names so it can get there. (I hope you can understand me)
The problem comes with setters. Setting an "surface" object is no big deal so is setting an object that is in another dictionary. The problem comes when I try to set an object at a depth.
Before I write anything else I will post the code so you can understand what I'm saying.
fullContent is a NSMutableDictionary containing the file.
//This one is easy, just return the object for the key.
- (id)getSurfaceObjectForKey:(NSString*)key
{
return [fullContent objectForKey:key];
}
//Hope you understand this one. Main parent is a string with the name of the first node. It gets a dictionary out of my plist and returns an object for key (I have a dictionary structured plist)
- (id)getMainParentChildObjectForKey:(NSString*)key
{
NSAssert(!mainParent, #"Main parent must not be nil");
return [[fullContent objectForKey:mainParent] objectForKey:key];
}
//This one gets the element at any given depth I just have to pass in an array containing node names
- (id)getObjectForKey:(NSString *)key atDepthWithChildren:(NSArray *)children
{
id depthElement = fullContent;
for (int i = 0; i < children.count; i++)
depthElement = [depthElement objectForKey:[children objectAtIndex:i]];
return [depthElement objectForKey:key];
}
//Sets a top (surface) object
- (void)setSurfaceObject:(id)object ForKey:(NSString *)key
{
[fullContent setObject:object forKey:key];
[self writePlistContent];
}
//Sets an object inside a dictionary (mainParent - string with the name of dictionary node)
- (void)setMainParentChildObject:(id)object forKey:(NSString *)key
{
[[fullContent objectForKey:mainParent] setObject:object forKey:key];
[self writePlistContent]; //Self explanatory. I write this to file
}
//This is where my problem comes. How do I save this to plist without making any other changes to it? Im guessing I have to rebuild it from inside up?
- (void)setObject:(id)object forKey:(NSString *)key atDepthWithChildren:(NSArray *)children
{
id depthElement = fullContent;
for (int i = 0; i < children.count; i++)
depthElement = [depthElement objectForKey:[children objectAtIndex:i]];
[depthElement setObject:object forKey:key]; //I set the desired object but I dont know how to save it
for (int i = 0; i < children.count - 1; i++)
{
//Here i guess i would have to build the NSDictionary from inside out. Using a NSMutable array perhaps?
}
}
I hope you understand my problem. I hope Im not complicating things too much. Im just really tired and have been up for nearly 24 hours now and cant think of a way to solve this.
Thank you in advance.
I don't understand why you don't just use your:
[self writePlistContent];
to save it.
Surely it will save the entire contents of the plist.

objective c perform selector in background and autoreleasepool

I am developing an iphone application which has some data stored in a sqllite database. When my view loads i would like to load the data from the database on a background thread. The problem is the application keeps crashing and i dont know why.
The code:
-(id) init
{
if((self=[super init]))
{
[self performSelectorInBackground:#selector(loadList) withObject:nil];
}
}
-(void) loadList
{
#autoreleasepool
{
Loader * loader = [[Loader alloc] init];
NSMutableArray * array = [loader getItemList];
[array retain];
NSLog(#"Got %d items",[array count]);
[self performSelectorOnMainThread:#selector(createList:) withObject:array waitUntilDone:false];
[loader release];
}
}
-(void) createList: (NSMutableArray*) array
{
items = array;
int i;
Item * it;
for(i = 0; i < [items count]; i++)
{
it = [items objectAtIndex: i];
[it getName]; // crashes
// populate the list
}
}
Loader returns a NSMutableArray with Item objects. The application crashes when i call the item getName (which returns a NSString*). From what i understand it crashes because the item name properties is being released. What am i doing wrong?
Thanks!
It's likely to be a problem with whatever type of object you're using to populate array.
I'm unable to find finger-on-paper proof but I'm confident that performSelectorOnMainThread:withObject:waitUntilDone: retains its object. However if each of the items in array keeps a reference to loader then they need to take responsibility for retaining that object. It looks like you're attempting to keep it alive manually but — as Chuck alludes to — your call to performSelector... will return instantly and not wait for the call you've made to complete.
This particular bug appears to be that you're passing waitUntilDone:NO, so the array is being released immediately and consequently so are its items.
But in general, UIKit is not thread-safe, so this is just a touchy design. I would probably put the loading of this stuff in another class that handles the task for you instead of right in the view.
I'd put a breakpoint on the line:
it = [items objectAtIndex: i];
Then type
po it
in the debugger, and see what's in the name field. As a guess, I'd say one of two things: 1) the field that getName returns isn't initialized with an object (i.e. isn't a real NSString *) or that you're getting a C string from SQLite (which is what it usually returns) and you're trying to treat it as an NSString *. If it's the latter you can use [myCString stringWithUTF8String] to convert the C string into an NSString *

Check if something exists in an NSMutableArray

I have an NSMutableArray which can hold several objects. I would like to check if an object exists, and if so, alter it. I was wondering about checking it. I thought this would work:
if ([[[self.myLibrary objectAtIndex:1] subObject] objectAtIndex:1]) // do something
However, this will crash if there aren't any subObjects at index 1.
So I guess the problem is that the above does not return nil if there isn't anything at this Index.
Is there another easy way to check or will I have to count through the array etc.? I know there are other posts on stackoverflow on this, but I haven't found a simple answer yet.
Any explanations / suggestions welcome. Thanks!
No check simply using :
[myArray indexOfObject:myObject];
or
[myArray containsObject:myObject];
These methods check every object using isEqual.
For example:
NSUInteger objIdx = [myArray indexOfObject: myObject];
if(objIdx != NSNotFound) {
id myObj = [myArray objectAtIndex: objIdx];
// Do some alter stuff here
}
If this is a pattern you use a lot, you could add a category to NSArray called something like safeObjectAtIndex:, which takes an index, does the bounds checking internally:
-(id)safeObjectAtIndex:(NSUInteger)index {
if (index >= [self count])
return nil;
return [self objectAtIndex:index];
}
Assuming the object you are using to search with and the actual object in the array are the exact same instance, and not two different objects that are equal according to an overridden isEqual: method, you can do this:
if ([array containsObject:objectToSearchFor]) {
// modify objectToSearchFor
}
If the two objects are different instances which are equal according to isEqual:, you will have to use code like this:
NSUInteger index = [array indexOfObject:objectToSearchFor];
if (index != NSNotFound) {
id objectInArray = [array objectAtIndex:index];
// modify objectInArray
}
NSArray (which is the NSMUtableArray superclass) has lots of methods for finding objects. Have a look at the documentation.
You can either rely on the equals method (e.g. indexOfObject:) or provide a block (e.g indexOfObjectPassingTest:) which is pretty funky.
It's fairly common in Objective C to be using the Mutable version of a class but rely on methods in the non mutable superclass so it's always a good idea when checking the online documentation to look at the superclass.