Pierce T. Wetter III
pierc****@twinf*****
Mon Apr 16 01:36:52 JST 2007
On Apr 15, 2007, at 7:58 AM, Jacob Wallström wrote: > I'm also having problems with CoreData.define_wrapper. I have > isolated the problem to the following code: Hmmm... That's different behavior then what I've been seeing. I've been digging into this code the last few days with various iterations of the problem. I've come to the conclusion that a few minute thought needs to be put into thinking about KVC & NSManagedObject. It's probably going to be a problem Laurent will have to solve but I'll help where I can. The first problem I was having was because the define_wrapper code, which runs when a model file gets loaded ends up overwriting any accessor methods defined on the ruby side. Since rb_main.rb loads all the ruby files first, that meant that at model time my accessor methods had already been defined... I was able to work around that by having the define_wrapper method check for definitions and NOT define a method if there was already one there as I posted earlier. I had to add my own version of valueForKey on the object as well. Once I got everything working, I switched to one of my existing data files instead of the simple test data file I was using. I then noticed how excruciatingly slow my app was. Running with the -d flag, I realized that crossing the bridge 3 times every time I accessed a value in an NSManagedObject was excruciating: def foo willAccessValueForKey "foo" result= primitiveValueForKey "foo" didAccessValueForKey "foo" result end Since most of the data has standardized accessors, and mogenerator exists, I thought "well, what I'll do is store those in an objective-C side object. Boom, lots of endless loops. Near as I can tell, the problem is that the rb<->cocoa bridge is relying on KVC to make its life easier. That is, if you do obj.foo, somewhere that translates into obj.valueForKey("foo"). The reason that causes endless loops is because it turns into: obj.foo -> obj.valueForKey("foo") -> obj.foo -> obj.valueForKey ("foo") That is, it never makes the leap to turn into obj.oc_send("foo"). Especially if define_wrapper has run, because it defines a bunch of accessors like this: def foo valueForKey "foo" end So if you have "foo" defined in an objective-c superclass, you hit an endless loop instead of the above code. I guess that's because the responds_to? check I made doesn't show the methods from the Objective-C side? So I had to disable the define_wrapper entirely. The other problem I ran into was that: def foo super end Would also cause an endless loop. I guess that's a known thing, given that the docs all say to use instead: def foo super_foo end So I think there's some sort of fundamental flaw with define_wrapper and the KVC stuff: 1. As is, if you have defined/customized any accessors on the ruby side, define_wrapper blows them away. While it might be possible to tweak define_wrapper to not do that if it sees an existing definition using responds_to?, it really has to check for both the ruby and the objective-c side before creating the definition. 2. If you have accessors on the objective-C side define_wrapper will cause an endless loop rather then call them because it makes new accessors that call valueForKey instead which causes them to call the new accessor again. Now complicating this whole situation is the fact that accessors are _optional_ with Core Data as NSManagedObject will deal with them by default in valueForUndefinedKey. But I haven't experimented much with that. That may be why the Core Data sample projects seem OK, but its more typical for CoreData programmers to define all the accessors (especially with mogenerator), and then tweak only the ones that need custom logic. Pierce P.S. Hey Jason! Ghost Action is a Ruby Cocoa app? Cool! I'm porting Frictionless to RubyCocoa and making it open source. (http:// www.twinforces.com)