NSMutablearray's last element gets corrupted and becomes standard NSObject from custom object - objective-c

I run into a weird problem with a NSMutableArray today.
I'm parsing an XML file and I add the parsed items as custom objects. There are 37 items in total.
So, when my view loads, I did this, as a test:
[parser loadDataBase];
ProductItem* item = [parser.productDetail.prodItems objectAtIndex:36];
NSLog(#"test 1 %#", item.idItem);
self.product = parser.productDetail;
item = [self.product.prodItems objectAtIndex:36];
NSLog(#"test 2 %#", item.idItem);
[parser release];
At this point, everything works just fine. Both NSLog print the correct value for the last item in the mutable array.
The problem is when I try to add these items into a table.
When the app tries to get the item at index 36, to display its properties, instead of a ProductItem custom object, it gets a NSObject object... everything is lost for the last item, being replaced with a mere NSObject.
I do absolutely nothing with the array in that class, or any other class, except the parser.
Everything is ok when it leaves the parser, everything is ok when I read it from the parser, everything is ok when I check to see if I got all the values correctly from the parser. But somehow, the last value gets corrupted after this, even though I don't do anything that might cause this.
Here's the code I use in the cellForRow:
NSLog(#"index %i", indexPath.row);
ProductItem* item = [self.product.prodItems objectAtIndex:indexPath.row];
cell2.itemName.text = item.name;
The row is 36, the last one, and boom! EXC_BAD_ACCESS when I try to read the name property.
Does anyone here have a clue about what might be happening? I never ran into such a problem before
Thank you for your time and attention!

With some help from a colleague, I found out what was happening.
I was releasing my currentParsedItem in the parser's dealloc, so everything looked ok before the [parser release]; but after that the, "currentParsedItem" that was the last object in my mutable array was being set to nil in the dealloc function of the parser, and I ended up with a blank NSObject
Hope this tip helps others with a similar problem!

Related

collectionView:cellForItemAtIndexPath: doesn't get called

I want to add new cells in my collection view, but nothing shows up when I add data.
I have a custom UICollectionViewLayout class, which has been working just fine, and I've been keeping dummy data in my datasource to adjust the layout. Now that I got rid of the dummy data, nothing's showing up.
Since the app didn't break and there weren't any warnings, it was difficult to track down where the problem was, and here's where I found a clue:
(UICollectionViewLayout class)
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSLog(#"ElementsInRect: – Visible cells info: %#", [self.collectionView.visibleCells description]);
...
}
Here, -visibleCells returns an empty array, even when I add data, call -reloadData and invalidate the layout. So I placed a breakpoint in -collectionView:cellForItemAtIndexPath:, and it turns out this method is not called at all. How did the cells show up before?
Any help would be appreciated.
The data source method, collectionView:numberOfItemsInSection:, has to return a non-zero number for collectionView:cellForItemAtIndexPath: to be called. When you had dummy data in your data source, it was. Now that you removed that dummy data, that method is probably returning 0. When you add data, it should put items into your data source, and then a call to reloadData should work. You should put a log in collectionView:numberOfItemsInSection:, and see what it's returning.
Okay, it turns out the issue was in UICollectionViewLayout. I doubt anyone else will be having this problem, but I'll write my answer for the sake of completeness:
I'd been tweaking my custom UICollectionViewLayout class, and after I'd thought that it was working well, I made the code look neat by deleting old code that was commented out, move methods, etc.
While doing that, I recalled having read somewhere that it's good practice to create attributes in -prepareLayout method, and return those attributes when -layoutAttributesForItemAtIndexPath: or -layoutAttributesForElementsInRect: is called. For me, it was a matter of moving a block of code, so I thought no biggie. And during this "cleaning process" I must have made a mistake.
What's really frustrating is that the code itself actually works regardless of where the attributes are created, and I can't tell what went wrong for the last few days.
The following is a snippet of code that I used to create the attributes objects. My initial question was asking why -collectionView:cellForItemAtIndexPath: was not called while executing the 3rd line. I did not change this part of the code, other than moving it around.
for (int i = 0; i < 3; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:self.topLayer];
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
if (cell) {
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
[self.array addObject:attributes];
} else {
NSLog(#"prepLayout: the cell doesn't exist for the index path {%d – %d}", indexPath.section, indexPath.item);
}
}
Number of Rows in Section - the count that can be used will determine if the cellForItemAtIndexPath gets called.
Initially when the view loads this will be called. Within the numberOfItemsInSection, if you have an array, the [array count] might return a nil value.
Complete the procedure where the array is populated, then reload the data in the collection view which will re-assess the numberOfItemsInSection. This can be done with the following code:
[self.myCollectionView reloadData];
"myCollectionView is the name given to the collection view item in your view"

Understanding Objective-C method value passing

Lets say I have in viewDidLoad:
NSMutableArray *entries = [NSMutableArray array];
[self doSomethingWithArray:entries];
NSLog(#"%#", entries);
Then in method I have:
- (void)doSomethingWithArray:(NSMutableArray *)entries
{
// create some custom data here, lets say - Something *something...
[entries addObject:something];
}
How is it possible that entries (one at the top) now (after method is finished) contain object something, since object "something" is not added to property or instance variable, and nslog will log class "Something" ? And doSomethingWithArray doesn't return anything since its "void".
I have encountered this for first time and dunno if there is any name of this appearance ?
I have seen this for second time in some examples and really dunno how its done.
If anyone could explain this a bit whats happening here I would be very very grateful.
Thank you a lot.
Because Objective-C instances are passed by reference (as you can tell by the * pointer syntax). You basically pass the address of the array to the doSomethingWithArray: method. In that method you add something to the array referenced by that address. And of course once the method returns, your array will contain that new object.
When you are adding the something object to the array, the array always retains it i.e it maintains copy of the Something object.
So NSLog prints the something.
Hope that helps.

Mysterious release of an NSDictionary in Objective-C?

I'm working on the finishing touches of a custom patch for Quartz Composer. As of right now, I have almost everything stripped out of the patch, and it's crashing telling me BAD ACCESS when I try to NSLog a NSDictionary value that is an ivar, and that worked perfectly in the last execution when I assigned it.
My code looks like this:
- (BOOL) startExecution:(id<QCPlugInContext>)context
{
lastBoutData = [[NSDictionary alloc] init ];
return YES;
}
- (BOOL) execute:(id<QCPlugInContext>)context atTime:(NSTimeInterval)time withArguments:(NSDictionary*)arguments
{
NSLog(#"self.inputBoutData: %#", self.inputBoutData);
NSLog(#"lastBoutData: %#", lastBoutData);
// have new data, put it on the output
self.lastBoutData = [NSDictionary dictionaryWithDictionary:self.inputBoutData];
NSLog(#"assigned: %#", lastBoutData);
return YES;
}
I can see the log shows that all three NSLog lines work perfectly until self.inputBoutData has input. Then, I see that self.inputBoutData is successfully copied to lastBoutData in the last NSLog line of the loop.
In the very next run of execute:atTime:withArguments:, self.inputBoutData is still full, but lastBoutData is blank again!!! I'm can't see how that can happen. Then, it runs one more loop, just like the last, and successfully copies the self.inputBoutData to lastBoutData, and it's logged again. The next time through, I get BAD ACCESS just before the second NSLog statement.
I was getting some error messages that told me that lastBoutData wasn't an NSDictionary, so out of desperation, I added a [lastBoutData retain], and it doesn't crash. I'm not releasing this ivar, so I'm not sure why I have to retain it. I do very similar things with other ivars in many other patches with no issues. What could I be missing? Why is this thing releasing on me, or is that even what is happening?
self.lastBoutData = [NSDictionary dictionaryWithDictionary:self.inputBoutData];
dictionaryWithDictionary: returns an autoreleased dictionary. And since your property was not (retain) nothing was retaining it. So your previous dictionary is de-referenced and leaked and your new dictionary is not retained.
consider:
[lastBoutData release];
lastBoutData = [[NSDictionary alloc] initWithDictionary:self.inputBoutData];
Or use your code as is and add retain to the property.

Enumeration and removing a particular object from NSMutableArray

I'm having trouble removing items from my NSMutableArray. I'm extremely new to Objective-C, so please bear with me.
So far, I have the following: I'm trying to remove a line from the array if it has certain text inside. I cannot do this while fast-enumerating, so I'm trying to store the index, for removal after the enumeration has finished. However, I'm being told that this makes a pointer from an integer without a cast. Confused!
//First remove any previous Offending entry.
//Read hostfile into array.
NSString *hostFileOriginalString = [[NSString alloc] initWithContentsOfFile:#"/etc/hosts"];
NSMutableArray *hostFileOriginalArray = [[hostFileOriginalString componentsSeparatedByString:#"\n"] mutableCopy];
NSInteger hostFileOffendingLocation;
//Use a for each loop to iterate through the array.
for (NSString *lineOfHosts in hostFileOriginalArray) {
if ([lineOfHosts rangeOfString:#"Offending"].location != NSNotFound) {
//Offending entry found, so remove it.
//[hostFileOriginalArray removeObject:lineOfHosts];
hostFileOffendingLocation = [hostFileOriginalArray indexOfObject:lineOfHosts];
//NSLog(#"%#", hostFileOffendingLocation);
}
}
//Release the Array.
[hostFileOriginalArray release];
//Remove offending entry from Array.
[hostFileOriginalArray removeObject:hostFileOffendingLocation];
My real question is why are you releasing your array before modifying it
try moving
[hostFileOriginalArray release];
to after
[hostFileOriginalArray removeObject:hostFileOffendingLocation];
You can do this without the loop by calling [hostFileOriginalArray removeObjectIdenticalTo:#"Offending"];
Note that it will remove multiple instances of the offending object, but that looks like what you want anyway. It will also do the operation in a fast way, without you having to worry about the implementation detail of which loop to use.
As a general rule (especially with the really commonly used objects like containers and NSString), check the class reference to see if Apple already has a way of doing what you want to do. It makes your code more readable to other Cocoa users (including future you), and reduces code maintenance- you're now leaving it up to Apple to add new technologies like Fast Enumeration to their code, and you get it for free when you link against new versions of the SDK.
Also, you should probably return hostFileOriginalArray at the end of the function, so it can do something useful- you can return it as an autoreleased object.
//Remove offending entry from Array.
[hostFileOriginalArray removeObjectAtIndex:hostFileOffendingLocation];
//Release the Array.
[hostFileOriginalArray release];
you should have been getting compiler warnings... take a look at them, they are usually very helpful, I always try to have 0 warnings... that way I know where I have done something careless.

Iterating through an array

Hey everyone,
I am trying to iterate though an array of strings when I click a button in my iPhone app. In the viewDidLoad method this code works great, however, I am using a Tab Bar to switch between views, and I want the view to refresh itself when i switch to it from another view. This is the code it is breaking around, very similar to code I am using in another button that works perfectly
NSEnumerator *e = [deckList objectEnumerator];
id obj;
It crashes whenever it gets to "id obj"
Any help would be most appreciated.
for (NSString* string in arrayOfStrings ){
NSLog(#"%#", string);
}
The Objective-C Programming Language — Fast Enumeration
It turns out that as #vikingosegundo pointed me to in an NSZombieEngabled thread, I had not initialized the ids to nil. The simple fix of:
id obj = nil;
solved all my problems!