How to copy NSMutableArray value into another NSMutableArray in iPhone? - iphone

Here, i have a NSMutableArray in my AppDelegate page, the array will be updated when the user add the values (user name) in the database. Now i want the array values(user name) to be displayed in the next view to be list out in the Picker View. I know there is more related questions is there, but i can't find the solution. Any help appreciated.
Here is my code:
The array i have declared in the AppDelegate page, named jobarray, now i need to copy the items in the jobarray into next view, there i have declared pickerarray.
pickerarray = [[NSMutableArray alloc] initWithArray:appDelegate.jobarray copyItems:YES];
But it returns error message,
['NSInvalidArgumentException', reason: '-[Coffee copyWithZone:]: unrecognized selector sent to instance 0x822d410'] where 'Coffee' is my sqlite3 object file.

Your SQLite object does not conform to the NSCopying protocol - you haven't implemented the
- (id)copyWithZone:(NSZone *)zone;
method. You can either implement this method to make your object copyable, but if you only need the copying for displaying, you'd better copy the array itself only:
NSMutableArray *newArray = [originalArray mutableCopy];
this only retains the first array's objects, no need to mess with implementing the copying protocol.

[arr1 initWithObjects:#"1",#"2", nil];
makes no sense, what you probably want is to use the NSMutableArray:
NSArray *arr1 = [NSArray arrayWithObjects:#"1",#"2", nil];
NSMutableArray *arr2 = [arr1 mutableCopy];
NSLog(#"arr1: %p, %#", arr1, arr1);
NSLog(#"arr2: %p, %#", arr2, arr2);
NSLog output:
arr1: 0x7924c50, (
1,
2
)
arr2: 0x7924f00, (
1,
2
)

To populate a UIPickerView from an array you need to first create a connection for the UIPickeView Data Source and Delegate back to the ViewController. You can do this by CTRL dragging in Interface Builder in the normal way - from your UIPickerView control.
You will also need to ensure that you include the delegate protocal to your interface file. So something like this;
#ViewController : UIViewController<UIPickerViewDataSource, UIPickerViewDelegate> {
}
Then you will need to create the delegated methods for the population of the UIPickerView in your implementation file.
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
//One column
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
//set number of rows
return myArrayObject.count;
}
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
//set item per row
return [myArrayObject objectAtIndex:row];
}
This assumes that myArrayObject has been properly declared and populated elsewhere. I.e. in viewDidLoad - if content is static.

Related

Passing an NSMutableArray to a DetailView from UITableViewController - iPhone

I have a UITableViewController which presents another UITableViewController when a cell is tapped. I have an NSMutableArray which I want to pass into the new UITableViewController when instantiated.
I would usually do something like :
- (void)loadStationList {
StationListView * listView = [[StationListView alloc] initWithNibName:#"StationListView" bundle:nil];
listView.dataList = newParser.stationData;
// ...
// Pass the selected object to the new view controller.
NSLog(#"New Parser is %d", [newParser.stationData count]); //This is fine - all objects in array here.
[self.navigationController pushViewController:listView animated:NO];
}
The odd thing is that dataList (the NSMutableArray pointer in the new class) is empty (I am in checking in the number of rows delegate method).
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSLog(#"Data List is %d", [dataList count]);
return [dataList count];
}
I have tried several different approaches (such as instantiating a new NSMutable array in the parent class) however nothing seems to work. This may be ARC related as I am still relatively new to ARC. Can anyone help ?
How did you declare dataList,newParser.stationData ?
Should be sth like this
#property (nonatomic, strong) NSMutableArray *dataList;
Anyway, to ensure that you do not loose any values, you may want to copy/assign each element from newParser.stationData to dataList.
Like here:
NSMutableArray * dataList = [[NSMutableArray alloc] init];
for (id arrayElement in newParser.stationData) {
[dataList addObject:arrayElement];
}
Try this instead of the simple assignment listView.dataList = newParser.stationData;. ps don't worry about efficieny this is so fast it will not matter.
Well I got round this by created an instance variable in the app delegate and then saving and reading the array from there. This does look like a bug in arc - it works with other variable types.

NSArray Set property not working

Basically what i am trying to do is make a network refresh and fetch objects, store it in a nsmutable array in my app delegate. Then i have a listviewController which uses that mutable array to display data.
Setting nsarray is not working here is the code:
//Appdelegate code called after pulldown to refresh is done on listview:
[ListView setArrayElements:(NSMutableArray*)sortedArray ];
NSLog(#"sortedArray count:%d",sortedArray);
NSLog(#"ListView Array count:%d",[ListView.ArrayElements count]);
Result i get in log : "sortedArray count:12" (which is perfect)&"ListView Array count:0" (this is not the right result)
It's hard to assume without seeing more of your code but how do you define the ArrayElements property? It may not be retaining itself and you may not have initialized it when the ListView object is created.
Let me know if this works;
Make sure ArrayElements is created in your ListView.h like the following:
#property (nonatomic, retain) NSMutableArray *ArrayElements;
Or on -init or -viewDidLoad of your ListView,
self.ArrayElements = [[NSMutableArray alloc] init];
Don't forget to release what you retained:
- (void)dealloc
{
//.....
[ArrayElements release];
[super dealloc];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.mTableView reloadData];
}
use this method
Do not make the UITableView object as a property. Just use tableView object , remove 'self.' portion.
Tell me if it helps!

UIPickerView appears but doesn't show data

I've been all over the place for the last two days trying to find the answer to this, but I can't seem to get it. As far as I can tell, I have everything setup correctly, but it just doesn't want to work.
My app scans QRCode tags and saves them to a mutable array. I need to create a way so the user can remove some of the tag objects from the array, so I'm using a UIPickerView to display the list of tags numbers from which to select the tags to delete.
Here's what I have:
in the .h file
#interface ViewController : UIViewController
UIPickerView *tagPickerView;
NSMutableArray *tagPickerData;
#property (nonatomic, retain) IBOutlet UIPickerView *tagPickerView;
#property (nonatomic, retain) NSMutableArray *tagPickerData;
in the .m file
#synthesize tagPickerView;
#synthesize tagPickerData;
-(void) viewDidLoad{
tagPickerData = [[NSMutableArray alloc]init];
[tagPickerView setDelegate:self];
[tagPickerView setDataSource:self];
}
- (void) dealloc{
[tagPickerView release];
[tagPickerData release];
[super dealloc];
}
- (void) imagePickerController: (UIImagePickerController*) reader
didFinishPickingMediaWithInfo: (NSDictionary*) info
{
[tagPickerData addObject:tagString]; //tagString is the value returned from the QRCode reader
}
#pragma mark -
#pragma mark tagPickerView Data Source Methods
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)tagPickerView{
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)tagPickerView
numberOfRowsInComponent:(NSInteger)component{
return [tagPickerData count];
}
#pragma mark tagPickerView Delegate Methods
-(NSString *)pickerView:(UIPickerView *)tagPickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component{
return [self.tagPickerData objectAtIndex:row];
}
(I think that's everything.)
In IB I have dataSource, delegate and tagPickerView set for File's Owner. The UIPickerView is hidden until the delete command is called (in this case, shaking the phone), and then it appears on top of everything.
What I am getting is a blank picker view.
I can verify using NSLog that the array is being populated each time the scan is performed, so I know it's not that I am loading an empty array. I can also verify that the Data Source methods are apparently being read, because I can change the number of components to 2 and it is reflected when the picker comes up -- two spinning wheels. But I don't know how to verify that the Delegate method is working.
I wondered if there's a way to populate the picker on command, like when the phone is shaken, instead of using the delegate... or would it even work that way?
The problem with all the reference materials is that it demonstrates how to use an array that is initialized with objects, i.e.
NSArray *array = [[NSArray alloc] initWithObjects:#"String 1",#"string 2",#string 3",(etc.) ,nil];
But I can't seem to find anything that shows how to load the picker from an array created from information collected by the user. Common sense tells me that it should work the same way, but I have learned to abandon common sense when working with Objective-C.
Is there a UIPickerView expert that can help me out here?
Thanks
Whenever you change the contents of the tagPickerData array, you need to call [tagPickerView reloadAllComponents]. The picker doesn’t have any knowledge of its underlying data—you have to send it that message to let it know that it should call its data-source methods.

Plugging a sortedArrayUsingSelector and/or initWithArray Memory Leak Between Classes

I've been struggling to solve this memory leak for awhile now, so I'm hoping the community can provide some help. Memory Management is still an issue I'm working to understand (and yes I have the memory management guide).
According to the Instruments Leak tool, I'm leaking an NSArray as soon as I navigate backward (back button w/ Nav Controller) off of the pertinent screen. I'm show all the relevant code I can think of, below, and can share more if needed.
I know that I'm alloc/initing an array in the ordered array function. This is because, to the best of my understanding, sortedArrayUsingSelector returns only pointers to the old array, not a true copy, so if I want to keep the array, I need to copy the values.
The problem is then how do I pass this sorted array to a different class while still properly managing my ownership of it? I release it in the dealloc, and I release it if the function is going to assign a new value, etc. But tbh I don't know if I'm doing this correctly.
Like I said, I'm really struggling still to properly understand how to correctly juggle all the memory management stuff, so any help would be much appreciated.
.h file of the relevant model class
#interface InstalledDataTracker : NSObject {
...other code...
NSArray *orderedZonesArray;
...other code...
}
#property (nonatomic, retain) NSArray *orderedZonesArray;
.m file of the relevant model class
#synthesize orderedZonesArray;
...other code...
- (NSArray *)orderedZonesArray {
if (!orderedZonesArray || installedDataChangedSinceLastRead) {
if (orderedZonesArray) {
[orderedZonesArray release];
}
NSArray *unorderedZones = [NSArray arrayWithArray:[self.installedAreas allKeys]];
orderedZonesArray = [[NSArray alloc] initWithArray:[unorderedZones sortedArrayUsingSelector:#selector(localizedCompare:)]];
}
return orderedZonesArray;
}
- (void) dealloc {
...other code...
[orderedZonesArray release], orderedZonesArray = nil;
[super dealloc];
}
.h in View Controller
#import <UIKit/UIKit.h>
#class InstalledDataTracker;
#interface SBVC_LSC01_ZoneSelect : UIViewController <UITableViewDataSource, UITableViewDelegate> {
... other stuff...
InstalledDataTracker *_dataTracker;
}
#property (nonatomic, retain) InstalledDataTracker *dataTracker;
.m init in View Controller
#synthesize dataTracker = _dataTracker;
- (id)initWithPerson:(NSString *)person {
if (self = [super init]) {
...other stuff...
self.dataTracker = [[InstalledDataTracker alloc] init];
}
return self;
}
- (void)dealloc
{
...other stuff...
[self.dataTracker release];
[super dealloc];
}
Leaking Method in View Controller
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
AbbreviationLookup *lookup = [[AbbreviationLookup alloc] init];
NSString *abbreviatedZone = [self.dataTracker.orderedZonesArray objectAtIndex:[indexPath section]];
cell.textLabel.text = [lookup zoneForAbbreviation:abbreviatedZone];
[lookup release];
return cell;
}
Instruments Leak Trace:
0 libSystem.B.dylib calloc
1 libobjc.A.dylib class_createInstance
2 CoreFoundation __CFAllocateObject2
3 CoreFoundation +[__NSArrayI __new::]
4 CoreFoundation -[NSArray initWithArray:range:copyItems:]
5 CoreFoundation -[NSArray initWithArray:]
6 -[InstalledDataTracker orderedZonesArray]
7 -[SBVC_LSC01_ZoneSelect tableView:cellForRowAtIndexPath:]
Things I've tried
orderedZonesArray = [[[NSArray alloc] initWithArray:[unorderedZones sortedArrayUsingSelector:#selector(localizedCompare:)]] autorelease];
return [orderedZonesArray autorelease];
And a bunch of other stuff I can't remember. Many attempts I've made to properly "release" the ownership created by alloc/init result in some sort of crash/bad access in the view controller. This is contributing to my confusion over where to properly release the array...
Detailed replies very welcome. I still have a great deal to learn!
Thanks a bunch. (Also, I've had to change some class and methods names for project security, so if something doesn't seem to match please mention it and I'll recheck for a typo)
Edit:
#Daniel Hicks, when I remove the initWithArray copy of the sorted array, as follows:
orderedZonesArray = [unorderedZones sortedArrayUsingSelector:#selector(localizedCompare:)];
, I get an EXC_BAD_ACCESS crash when the class tries to access the array from within the View Controller didSelectRowAtIndexPath method (likely the next time the array is accessed, I believe). Here's the method. It crashes on the second NSLog line, so I've left that in for good measure:
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"indexPath = %#", indexPath);
NSLog(#"self.dataTracker.orderedZonesArray = %#", self.dataTracker.orderedZonesArray);
NSString *abbreviatedZone = [self.dataTracker.orderedZonesArray objectAtIndex:[indexPath section]];
SBVC_LSC02_ZoneSelect *slz2 = [[SBVC_LSC02_ZoneSelect alloc] initWithPerson:self.selectedPerson andZone:abbreviatedZone];
[self.navigationController pushViewController:slz2 animated:YES];
[slz2 release];
}
In your viewController code, you allocate an InstalledDataTracker object, and then pass it to a retain property. This results in a retain count of 2, not 1. Later, when you release the dataTracker object, you only reduce the retain count by one.
The easiest way to fix it would be to remove the self. prefix so that you are not invoking the automatic retain that is performed by the property accessor. In fact, I would recommend not using the dot syntax at all in your init and dealloc methods. There is some debate on the matter, but in general I think it is best to avoid calling your property accessors unless you have a very good reason to do so.
Here is how I would write it:
- (id)initWithPerson:(NSString *)person {
if (self = [super init]) {
...other stuff...
dataTracker = [[InstalledDataTracker alloc] init];
}
return self;
}
- (void)dealloc {
...other stuff...
[dataTracker release];
[super dealloc];
}
This is because, to the best of my understanding, sortedArrayUsingSelector returns only pointers to the old array, not a true copy, so if I want to keep the array, I need to copy the values.
This is a misinterpretation. sortedArrayUsing..., similar other such functions, returns an array which contains the same pointer VALUES as in the original array. And, as the sorted array copy was made, the reference counts of the OBJECTS pointed to were incremented (ie, retain was done on each copied pointer). So the sorted array and the original are both "equals", and neither "owns" the objects more than the other one does. (In fact, examining the innards of the two arrays you'd not be able to tell which was copied from which, other than if you noticed that one is sorted and the other isn't.)
So there's absolutely no need to make the additional copy of the sorted array.
When you make that additional copy, you're using an [[alloc] init...] operation which returns a retained array. You then return that array to your caller without doing autorelease on it, meaning that it will leak if your caller does not explicitly release it, and meaning that the Analyzer will complain about it (since you can only return a retained object from copy... et al).

iPhone SDK: NSInvalidArgumentException -UIView numberOfComponentsInPickerView

I am trying to develop a simple search mechanism on a UIPicker. The approach I am using is to keep two arrays. My problem is that for some reason I am getting this run-time error.
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[UIView numberOfComponentsInPickerView:]
Here are the array declarations.
//data source for UIPicker NSArray
*arrayCountryChoices;
//search results buffer
NSMutableArray *arraySearchResults;
//properties
#property(nonatomic,retain) NSArray*arrayCountryChoices;
#property(nonatomic,retain) NSMutableArray *arraySearchResults;
Here is where I initialize the data
//create data
arrayCountryChoices = [NSArray arrayWithObjects:#"foo",#"bar",#"baz",nil];
//copy the original array to searchable array
arraySearchResults = [arrayCountryChoices mutableCopy];
An the picker methods.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)thePickerView {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)thePickerView numberOfRowsInComponent:(NSInteger)component {
return [arraySearchResults count];
}
- (NSString *)pickerView:(UIPickerView *)thePickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return [arraySearchResults objectAtIndex:row];
}
- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
//grab the selected country
strUserChoice = [arraySearchResults objectAtIndex:row];
}
Here is the search code for completeness although not really relevant yet as the app dies before we ever get here.
//filter on search term
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#", strSearchText];
[arraySearchResults filterUsingPredicate: predicate];
[pickerCountry reloadComponent:0];
I have also dragged datasource and delegate connections from the UIPicker to Files Owner in Interface Builder.
Any ideas?
Thanks in advance.
Looks like you've got the picker's data source set to something other than the object that implements the code you've posted there—apparently a UIView somewhere. Make sure the picker's outlets point to your actual data-source object.