Table view not updating according to bindings - objective-c

This is a very newbie question, and this is something I have done many times before, but there's something I'm missing this time.
In my AppDelegate.h file I declare an NSArray and set it as a property:
#interface AppDelegate : NSObject {
NSArray *lines;
}
#property(readwrite, retain) NSArray *lines;
#end
And then in the AppDelegate.m file in the awakeFromNib method I alloc it:
lines = [[NSArray alloc] init];
Then I have a method that sets the lines array:
NSString *fileContents = [NSString stringWithContentsOfFile:[NSHomeDirectory() stringByAppendingPathComponent:#"Desktop/sometextfile.txt"] encoding:NSUTF8StringEncoding error:NULL];
lines = [fileContents componentsSeparatedByString:#"\n"];
I have an array controller thats bound to AppDelegate.self.lines then I have a table column bound to Array Controller.arrangedObjects. I can confirm that the array is being updated (tested using NSLog) however the contents of the table are not being update (it remains blank).
Is there something obvious I'm missing here?

You don't need a data source if you're using Bindings. One or the other.
I have an array controller thats bound to "AppDelegate.self.lines" …
Why self?
#property(readwrite, retain) NSArray *lines;
No, use copy here. Otherwise, you'll find yourself retaining someone else's mutable array, which they will then mutate. Then “your” array will have changed without you knowing it.
Then I have a method that sets the lines array:
lines = [fileContents componentsSeparatedByString:#"\n"];
This is why the table doesn't show anything. You're not going through the property, you're accessing the instance variable directly. Direct instance variable accesses do not cause KVO notifications, so the array controller never finds out about the change.
Even worse, you're leaking the old array (since you simply assign over it without releasing it) and under-retaining this new array. Because you're not retaining the new array, that instance variable will hold a dead object shortly. The automatic retaining is done by the setLines: method, which only gets called when you call it.
You need to go through the property:
self.lines = [fileContents componentsSeparatedByString:#"\n"];
A property access is an implicit accessor message, so this both retains the array (or copies it, once you correct the #property as I suggested above) and posts KVO notifications.

When you say you have a arrangedObjects bound to the column do you mean you set the tablview datasource? If not you to set the tableview datasource to the lines array

You might want to read through this, it's got some good diagrams and explanations. What ennuikiller is saying is correct, I think it's a problem with your datasource. This is done by calling
[aTable setDelegate:aDelegate];

Related

Adding 'data' to an NSMutable Array - Xcode

I want to add 'email' data to my NSMutableArray so every time the button is pushed it adds another email to the array, so I end up with an array of email addresses but can't seem to get it working.
I think my problem is I am reinitialising the array so clearing it out and maybe need to put the initialisation somewhere else..which I have tried.
The below code lets me add an address but each time the IBAction is called I think I am clearing the array, any pointers or help would be great
#property (strong, nonatomic) NSMutableArray *mutable;
#synthesize mutable;
- (IBAction)array:(id)sender {
mutable = [[NSMutableArray alloc]initWithObjects:self.person.email, nil];
[mutable addObject:self.person.email];
NSLog(#"ARRAY OF EMAILS %#",mutable);
}
You are right - you are initializing the array each time the array: method is called. You can change the code to initialize it only once:
- (IBAction)array:(id)sender {
if(!self.mutable) {
self.mutable = [[NSMutableArray alloc]initWithObjects:self.person.email, nil];
}
[self.mutable addObject:self.person.email];
}
As a side note - you don't have to use #synthesize for this property. Read "Adopting Modern Objective-C" for more details.
Did you use UIViewController? If yes then reset the array in viewDidAppear method.
If you are using the UIView, then create the custom method to reset the array and call from the parent viewController.

Passing a simple NSMutableArray change original

I am having hard time with a simple array that i want to pass .
I have a class with some NSMutableArray that i pass to another class(the array is global from singleton)
[mgzm saveWithArray:[Globals sharedGlobals].allImages];
To this function :
-(void)saveWithArray:(NSMutableArray*)currentArray
{
dataToSave=[[NSMutableArray alloc] init]; //local
dataToSave=[currentArray mutableCopy]; //copy
Than i saw that is is changing the original array which i don't want .
So i did this :
dataToSave=[[NSMutableArray alloc] initWithArray:currentArray copyItems:YES];
Which result in a situation that i can't change the dataToSave array get get a crash when trying to.(it needs to be changed).
Than i did this :
for(NSMutableDictionary *dic in currentArray)
[dataToSave addObject:dic];
Which again if i change dataToSave it change also the original array (?! )
Is there a way in this language to COPY array without changing the original one ????
When you make a copy of a mutable array, changing the array copy does not change the original array, not the objects inside the array. Here is what happens when you call [currentArray mutableCopy]:
The two arrays are pointing to the same objects. If you remove an object from the copy, the original array would still have it. However, if you modify the object itself (say, change the name of A to X) the change will reflect on the object in the original array, because it is the same object.
Here is what you want to happen:
Now the two arrays are completely independent of each other. This is the effect that you achieve when you call
dataToSave=[[NSMutableArray alloc] initWithArray:currentArray copyItems:YES];
However, there is a catch: in order for this to work, the objects inside the array must conform to NSCopying protocol, otherwise the code is going to crash.
To fix this, make sure that the objects inside NSMutableArray implement NSCopying. Here is an answer that explains how it is done.
The problem is although you are creating a new array the NSDictionary objects within the new array are still the same ones. So you need to make copies of the NSDictionary objects, you we're close but you need to do something like this...
-(void)saveWithArray:(NSMutableArray*)currentArray
{
dataToSave=[[NSMutableArray alloc] init]; //local
for(NSMutableDictionary *dic in currentArray)
[dataToSave addObject:[NSDictionary dictionaryWithDictionary:dic];
}

I can't add my data object to my NSArray

So I have this method:
-(void)addLaneToRacingLanes:(UITapGestureRecognizer*)sender{
laneDataObject *data=[self.laneDataObjects objectAtIndex:sender.view.tag];
[self.racingLanes addObject:data];
NSLog(#"%i",self.racingLanes.count);
[sender.view setBackgroundColor:[UIColor yellowColor]];
}
It uses the tag from the senders view to find out which data object corresponds to that view.I'm using this to add to my racingLanes which is how I update these views, but my problem is that for some reason I cant add my laneDataObjects to my array racingLanes. Any ideas?
This is how the properties are set up:
#property (strong,nonatomic)NSArray *laneDataObjects;
#property (strong,nonatomic) NSMutableArray *racingLanes;
I have already run through the tags and they all work. The tags work such that lane 1 is tag 0 with its data object at 0, then lane 2 is tag 1 and its data is 1, so on and so forth. I already pre-tested this. And I have checked that both the laneDataObject array has been properly set up. Is it because my racingLanes isn't using a custom getter or setter? How would I go about changing that?
Incase it matters I used
NSLog(#" %i",self.racingLanes.count);
to find out if the array was empty.
It is a near certainty that the racingLanes has not been initialized: since the objects that you are adding are non-nil (you'd see an exception thrown otherwise) the racingLanes must be nil then.
You need to set racingLanes to NSMutableArray in the designated initializer:
_racingLanes = [NSMutableArray array];
Did you make sure to initialize your NSMutableArray in your class's -init or -viewDidLoad function?
// WITH ARC
self.racingLanes = [NSMutableArray array];
// WITHOUT ARC
self.racingLanes = [[NSMutableArray alloc] init];

returning an nsmutuableArray as a pointer that is an mutuablecopy

I dont want to return manch because if i autorelease before i return it ,it becomes invalid to others. so i was thinking of this :
classA
-(NSMutableArray*)set:(NSMutableArray*)data
{
manch= [[data mutableCopy]autorelease] ;
int count=2*[data count]; //to not enter infinity loop
for(int k=0;k< count;k=k+2)
{
if(k==count-1)
[manch addObject:[NSNumber numberWithInt:![[manch objectAtIndex:k] integerValue] ] ];
}
data=[manch mutuableCopy];
return data;
}
My goal is to create a class that gets an NSMutuableArray do some calculations, than return it, and NOT TO BE DEPEND on this class anymore .
EDIT :
As people here ask.
in another classB(the user of the method above), i have in the interface :
NSMutuableArray *data ;
and on the .m file init method i have
data=[[NSMutuableArray alloc]init];
to use the function from my question, i do :
mIns=[[classA alloc]init];
data= [mIns set:[self decimalToBinary:autoWord]];
than i loose data later.
I dont want to return manch because if i autorelease before i return it ,it becomes invalid to others. so i was thinking of this:
This is an incorrect statement, you can return an autoreleased object, that's a sane thing to do. It's worth noting that you should design your method names correctly to inform the user what sort of object is returned. Any method whose name begins with alloc, new, copy, or mutableCopy will return a retained object. (Source)
In your case, your method name is set:, which informs the user of this method that it will return a non retained object (almost always an autoreleased object). This is because it isn't prefixed with any of those words mentioned above.
In that case, the issue you have is with the user of the method; they are not retaining a reference to the object being returned. As such, the user of the method should use it as so:
#interface ClassName () {
NSMutableArray* m_ivarArray;
}
#property (nonatomic, retain) NSMutableArray* propertyArray;
#end
NSMutableArray* data = ...;
// If using a property with retain, setting via "self." will retain it for you
self.propertyArray = [self set:data];
// If using an ivar (which doesn't do the retain for you)
m_ivarArray = [[self set:data] retain];
You can avoid these issues by using Automatic Reference Counting (ARC, More Information), which will handle this sort of memory management for you. It is still important that you use the correct naming conventions, as ARC will judge how to manage your memory based on this (in certain situations)
Update: After seeing your update, I can see the problem.
data=[[NSMutuableArray alloc]init];
This is creating a new instance of NSMutableArray, one which is correctly retained (due to what I mentioned before).
data= [mIns set:[self decimalToBinary:autoWord]];
This is replacing the object held in data with a new NSMutableArray, one that is autoreleased. The previous instance you created has been lost, and you've replaced it with another one. This new instance has not been retained, and as such, will be released unexpectedly.
To fix, you need to use this instead:
NSMutableArray* data = [[mIns set:[self decimalToBinary:autoWord]] retain];
You don't need to alloc/init a variable if it will be populated by some other object later on. I strongly suggest brushing up on how this all works, this might be a good start.

Passing values from UITableView to UIView in table header

My CoreData model drives series of UITableViewControllers.
One of the detail tables has a header that contains a UIView "GraphView" that uses data from the rows of the table. (It's like iTunes having a custom header to chart song length in a given playlist.) My "playlist" populates from CoreData correctly. But how do I pass data from the TableView to the UIView in its header?
It seems wasteful and redundant to fetch and parse it again, especially when the data is sitting in a convenient mutable array called "steps". Can I just pass the mutable array to the UIView in the header? If so, how?
I've tried creating an NSArray property in the UIView's class called "passedStepsArray", and then setting it from the StepDetailViewController like so:
graphView.passedStepsArray = [[NSArray alloc] initWithArray:steps copyItems:YES];
But back in the GraphView class, it remains NULL. No value gets passed.
Any ideas?
From my visualization of the problem, it seems you are on the right tracks by having an NSArray property in the GraphView. You probably don't need to copy the existing array using the initWithArray:copyItems: method, just pass a non-mutable copy of the populated steps array:
graphView.passedStepsArray = [[steps copy] autorelease];
This is assuming that the graphView passedStepsArray property is set to retain. (Then don't forget to set self.passedStepsArray = nil; in the graphView dealloc methood.)