It may be because this has been a long day... but I am having with some basic scope issues. I am creating an object, passing it in to a delegate method, and adding it to an array wihin the method.
When I check the value of device within the method, it contains the device information.
Here is the code for the delegate function in the class that registered the delegate:
- (void) newAmeriscanDevice:(AmeriscanDevice *)device {
if (!self.deviceArray)
self.deviceArray = [[NSMutableArray alloc] init];
// add the newly created device...
[self.deviceArray addObject:device];
}
This method is within the same class of the earlier function. The deviceArray shows that it contains one object (supposed to the the driver object from above). When I look at the value of the device object in here, it is always 0x0.
- (void) endDevices:(NSNumber *)numberOfDevices {
// get out of here is there is no device in the device array
if (!self.deviceArray)
return;
// lets sort the array by order of the devices sort order
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"sortOrder" ascending:YES];
NSArray *sortDescriptorArray = [NSArray arrayWithObject:sortDescriptor];
// the array should now be sorted correctly...
[self.deviceArray sortUsingDescriptors:sortDescriptorArray];
// we now have data -- so.... lets reload the table
[self.tableView reloadData];
}
So.... any idea on how to make sure the object in the array retains its values?
Thanks All
Mike
write code some thing like this
- (void) newAmeriscanDevice:(AmeriscanDevice *)device {
if (!self.deviceArray)
{
NSMutableArray *tempArray= [[NSMutableArray alloc] init];
self.deviceArray=tempArray;
[tempArray release];
}
[self.deviceArray addObject:device];
}
And check some where you are releasing the array or any reference which having the same location(any other array you have released in which you copy the array by using '=' operator).release it in dealloc.
I made a mistake with my assessment. I was looking at the debugger values when hovering over the array. It would show that there was one object and the object pointer was 0x0. I changed the code:
- (void) endDevices:(NSNumber *)numberOfDevices {
// get out of here is there is no device in the device array
if (!self.deviceArray)
return;
NSLog(#"Array Count: %i", [self.deviceArray count]);
for (id object in self.deviceArray) {
if (object == nil) {
NSLog(#"Nil Object");
} else {
AmeriscanDevice *dev = (AmeriscanDevice *)object;
NSLog(#"Device: %#", [dev description]);
}
}
// lets sort the array by order of the devices sort order
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"sortOrder" ascending:YES];
NSArray *sortDescriptorArray = [NSArray arrayWithObject:sortDescriptor];
// the array should now be sorted correctly...
[self.deviceArray sortUsingDescriptors:sortDescriptorArray];
// we now have data -- so.... lets reload the table
[self.tableView reloadData];
}
Once I changed this code, it showed that the object in the array was in face the proper type object.
My problem was in the display of a table. Apparently the cellForRowAtIndexPath method is not being called in the table when I called the reloadData or when the view is first shown. I created this table view using xcode 4, so I am heading into the xib file to see whats not linked :)
Related
In my app I have a method that, when I press a button, adds a string to a NSMutableArray which is the model for a UITableView.
- (void)addPressed:(id)sender
{
NSString *string = #"aString";
[self.array addObject:string];
NSLog(#"Array count: %d",[self.array count]);
[self.tableView reloadData];
}
Problem is that the adding works the first time only if I press twice the button connected to this action I get this output:
2012-09-16 21:33:08.766 iUni[3066:c07] Array count: 1 //Which is fine since it worked
2012-09-16 21:33:08.952 iUni[3066:c07] Array count: 1 //Now count should be 2!!
Anyone has a guess on why is this happening?
I added the #property, synthesized it and lazy instatiated it this way:
- (NSMutableArray *)array
{
if (!_array) {
NSMutableArray *array = [NSMutableArray array];
_array = array;
}
return _array;
}
Your array is being created as an unowned (autoreleased, actually) object, which means that it is destroyed shortly after each time your accessor is called. It is then recreated the next time you access it, which gives you a new, empty array.
You need to create an owned version of the array to store into your instance variable:
if (!_array) {
_array = [[NSMutableArray alloc] init];
// Note no need to create a temp variable.
}
return _array;
You could also turn on ARC, which would have taken care of this for you and is a good idea anyways.
Is there a way to reverse the tableView order?
For example, I have a tableView that sorts by firstUpdated to LastUpdated. It does automatically cause its composed by plist data. But what if I want to put the newest data on top and the older on bottom?
The other solution will work fine but this one is a bit shorter.
NSArray *reversedArray = [[originalArray reverseObjectEnumerator] allObjects];
You should sort the array you use to populate the table view.
The other two answers would work, but I have another way of doing this if you need to sort by any property of the object:
You can create a method -sortMyArray
and it will look like:
-(void)sortMyArray
{
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"SomeObjectSortProperty" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[MyArray sortUsingDescriptors:sortDescriptors];
[sortDescriptor release];
}
and of course, every time after you call this method, you need to call your table view reload data method.
Hope this helps.
Another alternative (to maintain only a copy of the original array), is to use the original array and just grab objects in backwards order in the UITableViewDataSource functions.
Example:
id currentObject = [originalArray objectAtIndex:(originalArray.count - indexPath.row - 1)];
This is actually probably a better solution because you don't need to maintain 2 copies of the same data simply for reversed order.
You were using the array to populate the data in table view ...
Sort that array rather than sorting the table order..
self.yourCurrentArrayOfObjects = ...;
NSMutableArray *reversedArray = [NSMutableArray arrayWithCapacity:yourCurrentArrayOfObjects.count];
for (id object in yourCurrentArrayOfObjects.reverseObjectEnumerator)
{
[reversedArray addObject:object];
}
self.yourCurrentArrayOfObjects = [reversedArray copy];
[self.tableView reloadData];
I have an array of NSMutableDictionaries which has been sorted.
This array has been declared and synthesized so that its reachable anywhere in the code. However, its not.
When I try to read it out in cellForRowAtIndexPath, I get 0x5852400 does not appear to point to a valid object in debugger by using the po command, and I get the EXC_BAD_ACCESS error when using NSLog.
Code:
- (void)request:(FBRequest *)request didLoad:(NSArray*)result
{
int counter;
friendsArray = [[NSMutableArray alloc] init];
for (NSDictionary *d in [result objectForKey:#"data"])
{
[friendsArray addObject:d];
counter++;
}
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"first_name" ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
newfriendsArray = [[NSArray alloc] init];
newfriendsArray = [friendsArray sortedArrayUsingDescriptors:sortDescriptors];
NSLog(#"The new array which works and has been sorted: %#", newfriendsArray);
[[self tableView] reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSLog(#"Array still working here: %#", newfriendsArray);
return [newfriendsArray count];
}
Doing the same NSlog like the ones above in cellForRowAtIndexPath will cause the simulator to crash.
It's a memory management error. In this line:
newfriendsArray = [friendsArray sortedArrayUsingDescriptors:sortDescriptors];
sortedArrayUsingDescriptors: returns an autoreleased object that you need to retain if you want to use it for longer than the current method. The line above:
newfriendsArray = [[NSArray alloc] init];
has no effect other than introduce a memory leak to your code. It seems you have not quite understood when you need to create new objects and when other methods do this for you. In addition, you should really use properties to set your ivars (self.newFriendsArray = ...;) to avoid these simple memory management errors.
Most of the memoryleaks I solved myself, but this one is quite tough imo. The following happens. I need to load information from facebook in a table view, this table view has an refresh function. All the rows in this tablview are loaded from an array, this arrays consists of data objects as they need to be sorted. My code looks like this (I have cut out the irrelevant parts).
This parts runs through the results from facebook and places it in an array
- (void)request:(FBRequest*)request didLoad:(id)result
{
if ([result isKindOfClass:[NSDictionary class]]) {
//Setting single result into result dictionary
NSArray *resultArray = [result allObjects];
result = [resultArray objectAtIndex:0];
for(int i=0; i<13; i++){
//Set all retrieved data in containerArray
Post *newPost = [[[Post alloc] init] autorelease];
newPost.created_time = created_time1;
newPost.message = message1;
newPost.picture = picture1;
newPost.fbSource = fbSource1;
[containerArray insertObject:newPost atIndex:i];
//Reload the table in the tableView
[self.tableView reloadData];
}
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"created_time"
ascending:NO] autorelease ];
sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];
[sortedArray retain];
}
}
So far this works and gives no memory leaks. But as soon as the refresh function gets called. This function will run again. And then creates the memory leak, I think probably due the [sortedArray retain] function. But without this function the array doesn't load and I get a EXC_BAD_ACCESS. If I release sortedArray, I also get the EXC_BAD_ACCESS since the sortedArray is gone and can't be called.
Someone knows how to fix this? Thnx!
Your diagnosis is right. If you assign a value to sortedArray a second time the way you are doing, the previous object is leaked.
The solution is calling release before doing the assignment:
[sortedArray release];
sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];
[sortedArray retain];
A more elegant solution would be declaring sortedArray as a retain property:
#property (nonatomic, retain) NSArray* sortedArray;
so that you can replace the three lines above by:
self.sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];
and this will handle both releasing and retaining properly.
sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];
This line runs for first time and this is OK. But run 2nd time and you are pointing to a new array, leaking the previous one. So there is two solution.
First, release it before this line like this:
[sortedArray release];
sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];
[sortedArray retain];
Or make sortedArray a retained property in your class.
#property (nonatomic, retain) NSArray *sortedArray;
self.sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];
I'm trying to retrieve data from Core Data and put it into a Mutable Array
I have an Entity called 'Stock' and in Properties, attributes called : code, price & description...
How do I get the data stored in these attributes into a simple Mutable Array?
I've added this code...
NSMutableArray *array = [[NSMutableArray alloc]init];
[array addObject:[stock valueForKey:#"code"]];
and I get this error...
'-[NSCFArray insertObject:atIndex:]: attempt to insert nil'
I have a 'Managed Object Class' called 'Stock' and declared called stock. Am I missing something?
If I do this in the -cellForRowAtIndexPath...
Stock *stock1 = [fetchedResultsController objectAtIndexPath:indexPath];
array = [[NSMutableArray alloc] init];
[array addObject:stock1.code];
NSLog(#"Filtered List is? %#", array);
In the console I can see these 2 items
'The Filtered array is 810005'
'The Filtered array is 810007
'
What must I do to get these items(810005 & 810007) into an array set up in the -viewDidLoad method? Like it does in the -cellForRowAtIndexPath?
Update
Hi Marcus,
Finally got it working (well, 80%)
I put this in the -cellForRowAtIndexPath
Stock *product = nil;
if (tableView == self.searchDisplayController.searchResultsTableView)
{
filteredListContent = [NSMutableArray arrayWithObjects:stock1.code, nil];
product = [self.filteredListContent objectAtIndex:indexPath.row];
[self configureFilteredCell:cell atIndexPath:indexPath];
[filteredListContent objectAtIndex:indexPath.row];
NSLog(#"Filtered List Array List is? %#", stock1.code);
}
else
{
listContent = [NSMutableArray arrayWithObjects:stock1.code, nil];
[self configureCell:cell atIndexPath:indexPath];
NSLog(#"List Array List is? %#", stock1.code);
}
Then I used this code in the scope
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
self.savedSearchTerm = searchText;
if (searchText !=nil)
{
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"code beginsWith[cd] %#", searchText];
[fetchedResultsController.fetchRequest setPredicate:predicate];
}
else
{
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"code contains[cd] %#", searchText];
[fetchedResultsController.fetchRequest setPredicate:predicate];
[self.tableView reloadData];
}
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error])
{
// Handle error
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
[self.tableView reloadData];
Everything is filtering fine but when I hit cancel on the search, it's not reloading the original data...
I won't be defeated...!!
Thanx
Since you are having this issue in your -viewDidLoad, I am guessing (and without the code from -viewDidLoad, it is only a guess) that you are trying to fetch objects from the NSFetchedResultsController before the -executeFetch: has been called on the controller and therefore you are in the land of nils.
I would suggest setting a break point in your -viewDidLoad and watching the values and you walk through your code. This will tell you what is nil and where.
Of course a better question is, why are you trying to put NSManagedObject instances into a NSMutableArray? Since they are already in your NSFetchedResultsController is there really a need to build up another array? What is the end goal?
Update
Now I understand what you are trying to do.
Solution 1
Only populate the array when a search has been conducted. Take a look at the http://developer.apple.com/iphone/library/samplecode/TableSearch/index.html example code and you should see how to apply it to your situation.
If you want to enter the table view with a pre-defined search then you need to perform it after you have executed a -performFetch: in the NSFetchedResultsController.
Solution 2
Modify the NSPredicate on the NSFetchedResultsController to include your search terms and then execute -performFetch: on the NSFetchedResultsController, you may have to do a -reloadData on the table as well, I am not sure.
When the user clears the search field you reset the predicate and re-fetch everything. Since it is all cached there should be no performance penalty.
Solution 2 just occurred to me and I have not tested it personally but there is no reason it shouldn't work just fine. Should even give you live updates within the search.
Have you read the documentation? You fetch your Stock instances (all of them or filter them with a predicate), then do with them whatever you please.
You can then add their properties to an array individually:
[array addObject:[stockInstance valueForKey:#"price"];
... or use a combination of < NSKeyValueCoding > protocol methods such as -dictionaryWithValuesForKeys: NSDictionary methods such as -objectsForKeys:notFoundMarker: to get an array for given keys.
This may or may not actually be what you need to do, though. It depends on what you intend to use the resulting array for. If you want a quick sum of all matching Stock instances' "price" values, for example, you can use Set and Array Operators. It really depends on what you're trying to achieve.
When I got your error,
'-[NSCFArray insertObject:atIndex:]: attempt to insert nil'
I had given the fetchedRequest a sort descriptor that had a nil key. The error appeared when I used these lines:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:nil ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
The error disappeared when I set the key to #"name":
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];