Avoiding Circular References

I want to creating a avoid circular reference but I want to make sure I’m handling this correctly and use the best method/practice of doing so.

I have a Spritesheet that has an array of Sprite(s):

Typically you’ll call Spritesheet.GetSprite(spriteName as string) as Sprite.
But I want each Sprite also to have a reference to its parent Spritesheet.

Is this okay to do? I’m okay with the SpriteSheet never releasing from memory as long as long as there is at least one of its Sprites still in use (in this case from a session). Or do I have to use a Weak Reference?

For example,
At the start of a WebApp I will generate the Spritesheets. The WebApp will keep a reference for each of these Spritesheets. WebSessions will receive WebSpriteButtons that have at least one reference to a Sprite. If I replace the Spritesheet because I want to update the UI then new Sessions will have their buttons show the new Sprites. But the old sessions Sprites would still be pointing to the original Spritesheet even though it’s no longer stored as a property of the WebApp. Then when all sessions have closed that have SpriteButtons with Sprites, the old Spritesheet will go out of memory.
^If that’s all correct then I’m happy

Use a WeakRef through a computed property. You’ll be happier. :slight_smile:

see here
http://www.mbsplugins.de/archive/2014-08-26/Tip_of_the_day_Weak_Property_p/monkeybreadsoftware_blog_archive

so all references from child to parent must be weak.

I’m worried if I use a weakRef that if I replace the spritesheet and there are still sessions that are using the sprites that were tied to that spreitesheet that they will start to get exceptions. The sprites need to know about their spritesheet in order to tell the browser where the spritesheet image is.

The problem is (the way you described it), neither the SpriteSheets nor the Sprites will ever go out of scope until and unless you manually clear them.

You create a SpriteSheet (one references), and then create each Sprite and hold a reference to the SpriteSheet. Let’s say there is only one Sprite, and it holds a reference to the SpriteSheet (two references). You replace the SpriteSheet so now you’re back to one reference. Now you try to get rid of the Sprite, but how? The SpriteSheet that you can only access through a Sprite holds a reference to the Sprite, so if you nil or otherwise take the Sprite reference out of scope, you still have both the SpriteSheet and all its Sprites in memory pointing to each other, and no way for you to reach them.

I understand I don’t want a circular reference and I understand that using a weak reference will prevent this. But based on my current understanding this doesn’t fix my problem.

Let’s say my app has a property called iconSpriteSheet.
If at some point I want to change this and assign a new sprite sheet to the iconSpriteSheet then all of the Sprites that had a weak reference to the Spritesheet would now have a reference to Nil. This is not acceptable as they still need to be able to reference the Spritesheet as long as a webSpriteButton has a reference to one of these sprites. But I cant use a normal reference either because this would cause a circular reference.

So what’s the proper solution if neither of these cases will seemingly solve the problem.

[quote=132191:@Brock Nash]I understand I don’t want a circular reference and I understand that using a weak reference will prevent this. But based on my current understanding this doesn’t fix my problem.

Let’s say my app has a property called iconSpriteSheet.
If at some point I want to change this and assign a new sprite sheet to the iconSpriteSheet then all of the Sprites that had a weak reference to the Spritesheet would now have a reference to Nil. This is not acceptable as they still need to be able to reference the Spritesheet as long as a webSpriteButton has a reference to one of these sprites. But I cant use a normal reference either because this would cause a circular reference.

So what’s the proper solution if neither of these cases will seemingly solve the problem.[/quote]

Brock, forgive me but I cannot quite understand the issue.

[quote=131894:@Brock Nash]Typically you’ll call Spritesheet.GetSprite(spriteName as string) as Sprite.
But I want each Sprite also to have a reference to its parent Spritesheet.[/quote]

Why do you need to keep a reference to the parent since you got SpriteName to know which sprite it is you got from the method ?

You’ll either need to create circular references to make sure the parents stay around or use WeakRefs and make sure the children can handle the parent not being there.

If I’m reading this right, you need the children to get destroyed if the sprite sheet does? If so, use the WeakRefs and have the sprite sheet delete it’s children when the destructor fires.

Something else to remember. Once the sprite sheet is downloaded to the browser, having it going out of scope shouldn’t matter if the browser has cached the image.

What about using a WeakRef in the other direction? The Spite will have a reference to the SpriteSheet, but the SpriteSheet will keep WeakRefs to each of the Sprites? When the last Sprite of a “deleted” SpriteSheet goes away, the SpriteSheet will too.

You can still “hide” this by using methods that do the conversions for you. For example:

Function MySprite (index As Integer) As Sprite
  dim w as WeakRef = mSprites( index )
  if w is nil then
    return nil
  else
    return Sprite( w.Value )
  end if
End Function

A Sprite that goes out of scope can even notify the SpriteSheet in its Destructor so the SpriteSheet can remove its reference. If you need to iterate your Sprites, you can do something like this:

Function MySprites () As Sprite()
  dim r() As Sprite
  for i as integer = mSprites.Ubound downto 0
    dim w as WeakRef = mSprites( i )
    if w is nil or w.Value is nil then
      mSprites.Remove i
    else
      r.Append( Sprite( w.Value ) )
    end if
  next

  return r // Assumes order doesn't matter

Not quite. The sprites need to keep their original spriteSheet around even if I replace that Global spritesheet. The spritesheet needs to go away once:

  1. it has been replaced by another sprite sheet in the app
    AND
  2. there are no longer any children sprites pointing to it.

So even if I replace the Spritesheet for the app. Anyone who has received a webSpriteButton pointing to a sprite. That sprite 's spritesheet must still be available to them

The problem is that there is no guarantee the sprite has yet been rendered on the user’s browser. For example a WebSpriteButton might have a default sprite image but when you mouseOver or mouseDown it might change. The changed image might be from a spriteSheet that has not yet been retrieved by their browser and if that spriteSheet has been replaced then this would cause issues. (I’m not sure if there’s a way to force the pre-load of the image - but that might fix the issue)

Having a weak reference in reverse doesn’t work. Because when I first create the spritesheets there are no references to it from any sprites. And there may not be for some time.

=====================
I think my solution is going to be something like this:
The children sprites don’t so much need a reference to the SpriteSheet as they do the WebImage on the spriteSheet. If I have the SpriteSheet pass down a reference to the WebImage to its Sprites then the Sprites themselves no longer need a direct reference back to their SpriteSheet. Once the spriteSheet and all passed out sprites go out of scope the WebImage will also be cleaned out of memory and all will be good! (or at least I’m hoping)

I didn’t really understand your first point (in reply to me) since, presumably, you would keep a reference to your SpriteSheet somewhere and add Sprites as you normally do.

Having said that, your solution sounds better.

Question:
Does having a parent-child both with non-weak references automatically force a memory leak - or is it just a potential for memory leaking issues?

For example:

if true then dim p as new parent dim c as new child p.children.append(c) c.parent = p end

^Would that create a memory leak? Or would this get all cleaned up after the end statement as this would all go out of scope?

If parents & children have non-weak references to each other you leak because their ref counts don’t go to 0 even when the method ends (they refer to each other)