Mac Problem with Cocoa Bindings saving-loading. Help!

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
I have developed an application that will hold the lessons of each day of the week. On the right tableview, the user creates a day, then he/she creates the lessons for this day and sets the assignment for this day. I have done the application in Cocoa bindings for the first time, and I have some problems with saving-loading.

This is what I have done so far

When I create some information on the 2 tableviews, all is fine. When I save them and try to load them, nothing is being changed, and when I try to create some more days and lessons, there is an "invisible" day allocated, that can be pressed but cannot be edited. Nothing more is loaded properly. Any ideas of what may be wrong?

PS. The application is in testing stage: There are some really obvious mistakes. However, think that the solution to my problem is really simple, only I cannot see it! Any help would be appreciated.
 

Attachments

robbieduncan

Moderator emeritus
Jul 24, 2002
24,873
219
Harrogate
There are a few problems here. Simple ones: your setter for the days array is wrong! I changed it to:

Code:
- (void) setDays: (NSArray *)newDays
{
	[days release];
	days = [newDays retain];
}
I then changed the end of the load code to:

Code:
array = [NSUnarchiver unarchiveObjectWithFile:[sheet filename]];
[self setDays:array];
Note that you could (and should) do the above on a single line.

So now if we save a file with 3 days and quit the app and reload the file we get 3 days reloaded. Unfortunately they don't have names so the table view does not appear to update but a few clicks on the rows shows they are there.

So why are there not any names? Because they did not get archived. Why? Because NSObjects do not archive anything when they are archived so any instance variables will be lost, in particular your properties dictionary. How do we fix this? Write a custom archiver method for your NSObject subclass. I'd read this as a starting point.
 

robbieduncan

Moderator emeritus
Jul 24, 2002
24,873
219
Harrogate
OK so I see you do have encodeWithCoder: I need to look at this a bit more. Is there a reason for having custom day and lesson classes? You could have just used dictionaries...
 

robbieduncan

Moderator emeritus
Jul 24, 2002
24,873
219
Harrogate
Another simple fix. Your setter for the properties array in the day class was wrong as well. It should read:

Code:
- (void) setProperties: (NSDictionary *)newProperties
{
	[properties release];
	properties = [newProperties retain];
}
Check ALL your accessors. Your way of doing it does not work at all.
 

HiRez

macrumors 603
Jan 6, 2004
5,848
1,803
Western US
robbieduncan said:
Another simple fix. Your setter for the properties array in the day class was wrong as well. It should read:

Code:
- (void) setProperties: (NSDictionary *)newProperties
{
	[properties release];
	properties = [newProperties retain];
}
Isn't this code dangerous? What happens when newProperties is the same object as properties? It'll get released on the first line and you'll have a crash when you try to assign the now invalid object on the second line. I think it should be:
Code:
- (void)setProperties:(NSDictionary*)newProperties {
    if (properties != newProperties) {
        [properties release];
        properties = [newProperties retain];
    }
}
 

Catfish_Man

macrumors 68030
Sep 13, 2001
2,579
1
Portland, OR
or
Code:
- (void) setFoo:(Bar *)newFoo
{
    [foo autorelease];
    foo = [newFoo retain];
}
or, of course, once we get 10.5...
(in the .h file)
Code:
@property(ivar) foo;
and nothing at all in the .m :) Information from publicly available GCC sources, just in case someone thinks I'm breaking my NDA.
 

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
By fixing the setter methods, the program now works just fine. Thanks RobbieDuncan and HiRez!

But I would like to have something explained: Why the previews setter methods didn't work? What's wrong to have the contents of an an NSArray replaced with the -setArray method?
 

wjsdelicious

macrumors newbie
Oct 26, 2006
2
0
Yipes!

The biggest problem you have is that you aren't notifying the bindings layer when you change a variable. You've got to do this, in one of two ways:

1) You can call

[self setValue:value forKey:mad:"variable"];

instead of

[variable autorelease];
variable = [value retain];

And -setValue:... will automatically post the proper notifications, which will tell the UI layer to update itself, if anyone is watching "variable".

2) You can post notifications yourself, in your set methods, as such:

- (void)setVariable:(id)newValue;
{
[self willChangeValueForKey:mad:"variable"];
[variable autorelease];
variable = [newValue retain];
[self didChangeValueForKey:mad:"variable"];
}

This is how you need to write accessor methods, so you need to go back and rewrite yours to be this way, and then make sure you either call them directly or call -setValue:forKey: to change ANY variable which is being bound to.

--

Other, lesser issues include (a) you should use CoreData, not archiving (which kind of sucks), and (b) your -init methods are very ugly, (c) you should be using a subclass of NSDocument, not NSObject, for your main controller, and (d) don't use [NSDictionary initWithObjects:forKeys:] if you have a static list, use [NSDictionary initWithObjectsAndKeys:], which lets you write it all in a much more compact and much less error-prone way, (e) instead of having a generic dictionary of "properties", if the "properties" are always the same you should use a real class with those properties as ivears (or, if you are using CoreData, a real entity).

--

Sorry about the double-post, my spacebar went nuts.
 

robbieduncan

Moderator emeritus
Jul 24, 2002
24,873
219
Harrogate
Soulstorm said:
By fixing the setter methods, the program now works just fine. Thanks RobbieDuncan and HiRez!

But I would like to have something explained: Why the previews setter methods didn't work? What's wrong to have the contents of an an NSArray replaced with the -setArray method?
I'm now at work and don't have the code in front of me so I will assume that you are actually talking about NSMutableArray not NSArray (as you can't change an NSArray). I'm not sure why this doesn't work. I simply changed the accessors to be the same as the ones I normally write, are in all the examples and work!
 

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
@wjsdelicious:

Why do my -init methods suck? I would also use CoreData, but since CoreData uses CoreBindings, I tried doing it in CoreBinding first to understand the under-the-hood code that is generated.

I will also use NSDocument for this kind of work. I just made a test project.

don't use [NSDictionary initWithObjects:forKeys:] if you have a static list, use [NSDictionary initWithObjectsAndKeys:], which lets you write it all in a much more compact and much less error-prone way,
Can you explain this to me more, please? I'm not sure why this is better.

Last question: Lets just sayt that for the table views I want to add 2 more buttons: Move up and Move Down. These will take the selected item and move it one place up or down accordingly. How can I do this? Could this be done by just writing methods in MyController that alter the contents of the arrays in each class (the "days" and "lessons" arrays, I mean)?

This is my project so far:
 

Attachments

robbieduncan

Moderator emeritus
Jul 24, 2002
24,873
219
Harrogate
Whilst move up/down buttons are easier to program I'd go for drag-and-drop for re-ordering. This is a significant pain with table views normally, but it's what the user will expect...

Edit to add:

I think you use initWithObjectsAndKeys like this:

Code:
NSDictionary *myDict = [NSDictionary initWithObjectsAndKeys:[NSArray arrayWithObjects:@"key1",@"object1",@"key2",@"object2",nil]];
Obviously you can use any objects you like instead of strings.
 

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
robbieduncan said:
Whilst move up/down buttons are easier to program I'd go for drag-and-drop for re-ordering. This is a significant pain with table views normally, but it's what the user will expect...
Yes, but how can I do that? I am not asking for the exact solution, just a reference or some classes I could use.
 

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
robbieduncan said:
Well, you can read the selection from the Array controller. Then you can remove that object using the Array Controller and re-insert it using the array controllaer at the correct index.
But where do I write the proper methods for it? I mean... how do I access the nsArrayControllers in my project? I haven't written any code for the ArrayControllers to manage the tableviews. Where do I write the code that can access the arrayControllers set in Interface Builder? Is there any tutorial that I can read for this stuff?
 

robbieduncan

Moderator emeritus
Jul 24, 2002
24,873
219
Harrogate
The NSArrayControllers were in your nib. Simple add IBOutlets to your controller and connect them to the array controllers. Then add some IBActions and connect the buttons to them.
 

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
Thanks robbieduncan!

I have one last problem. I managed to create the code for the 2 buttons that handle the lessons, but for the days, I have one problem: Although the code is nearly the same, the dayArrayController doesn't seem to recognize how many objects it contains! When I press the "move up" or "move down" buttons, a message appears in the run log, saying that there is an index error because the indexes of the arrayController are from 0 to -1! Can anyone point me to the source of the problem?
 

Attachments

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
Problem solved. It was relly stupid, actually. Thanks a lot everyone who helped me on this. I really don't know how at first I had the patience to get involved with these kinds of projects without knowing coredata and cocoa bindings.
 

wjsdelicious

macrumors newbie
Oct 26, 2006
2
0
More guides.

Soulstorm said:
Why do my -init methods suck?
I wrote a long description of writing init methods at http://wilshipley.com/blog/2005/07/code-insults-mark-i.html .

Soulstorm said:
Can you explain this to me more, please? I'm not sure why this is better.
With NSDictionarh -initWithObjectsAndKeys: you can take eliminate two lines and two variables. Less code is better code!

Soulstorm said:
Last question: Lets just sayt that for the table views I want to add 2 more buttons: Move up and Move Down. These will take the selected item and move it one place up or down accordingly. How can I do this? Could this be done by just writing methods in MyController that alter the contents of the arrays in each class (the "days" and "lessons" arrays, I mean)?
Yes, you just modify the array, making sure you call -willChangeValueForKey:... first and -didChangeValueForKey:... after.

-OR- you could ask your object for a "magic" array that'll post these for you, by saying:

NSMutableArray *mutableMagicDaysArray = [self mutableArrayValueForKey:"days"];

And then just modifying 'mutableMagicDaysArray'. Your choice.

-W
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.