From: Lyle Johnson Date: 2003-08-12T10:18:41+09:00 Subject: Re: A question about Circular References Scott Thompson wrote: > The problem I'm running into is in the mark routine for a CGSize > object that was created as part of a CGRect. The tricky part is that > when my mark routine is called, it recieves a pointer to the CGSize > structure in C. I don't have any way to go from that pointer to a > CGSize, to a pointer to a CGRect, to VALUE that represents the CGRect > object. To put it another way, given a pointer to a CGSize > _structure_, I don't have a way to recover the CGRect _object_ that > contains that structure. No, you definitely don't get this "for free". Obviously, this is not a problem unique to Ruby; in an arbitrary C/C++ program, if I have a pointer to a struct or object that happens to be member data for some other struct/object, there's no magic for navigating back to the parent object. As you have already deduced, you'll need to do some extra bookkeeping of your own to keep up with these relationships. > > > In the original message I was talking about code like this: > > VALUE rb_CGRect_initialize(VALUE self) > { > CGRect *owningRect = NULL; > > Data_Get_Struct(self, CGRect, owningRect); > > if(NULL != owningRect) { > VALUE originObject = Data_Wrap_Struct( > gClass_CGPoint, > 0, > 0, > &(owningRect->origin)); > > VALUE sizeObject = Data_Wrap_Struct( > gClass_CGSize, > 0, > 0, > &(owningRect->size)); > > rb_iv_set(self, "@size", sizeObject); > rb_iv_set(self, "@origin", originObject); > > rb_iv_set(sizeObject, "@parentRect", self); > rb_iv_set(originObject, "@parentRect", self); > } > return self; > } > > This is the initialize method for CGRect. In the initialize method, I > define two instance variables "@size" and "@origin" that are part of > the CGRect object. At the same time, I set instance variables in the > size and origin objects that point back to the rectangle. > > My theory is as follows: > > 1) When ruby is trying to mark the CGRect it will "automagically" mark > all of it's instance variables so the size and origin objects will get > marked. I'm not sure that this is in fact true for "Data" objects like these. I took a quick look at the Ruby source code (see rb_gc_mark_children() in gc.c) and it's not clear to me that instance variables for Data (extension) objects get marked automatically. You should add some debugging statements (or whatever) to convince yourself that this is what actually happens. > 2) Similarly, when ruby tries to mark a size or origin object that has > a parent rectangle, it will "automagically" mark the parent rectangle > as well (preventing it from being Garbage Collected). This will only happen if instance variables for Data objects are automatically marked (see previous comments). > 3) if neither the CGRect nor the CGSize/CGPoint have "external > references" then none of the objects will be marked and the GC can > suck up the whole tangled mess in one swell foop. Yes, this should hold true. > That way, if there are "external references" to the CGRect, everybody > gets marked. If there are no "external references" to the CGRect, but > there are "external references" to the size, or the origin, the CGRect > will still get marked so it's memory won't be reclaimed. Yes. > Will that work out or am I trying to do things the hard way? Your plan sounds good to me if Ruby's GC does in fact mark instance variables for Data objects, but as stated, I'm not sure that this is the case. If the GC doesn't automatically handle this for you, I still think you're on the right track by storing those values as instance variables (i.e. this is as good a bookkeeping system as any). It does mean, however, that you'll still need to write mark() functions for CGRect, CGPoint and CGSize that extract these instance variables and mark them. Good luck, Lyle