简体   繁体   中英

How to implement the ios delegate / data source pattern in c# Xamarin?

How can I implement the equivalent to the "delegate" or "data source" pattern that iOS uses for table views, in c# Xamarin? For example, I'd like to have view classes like:

public class MyDataView : UIView
{
  public interface ISource
  {
    int NumberOfPages();
  }

  public ISource DataSource { get; set; }
}

but the problem is when I call this from MyViewController with code like:

this.myDataView.DataSource=new ViewSource(this)

public class ViewSource : MyDataView.ISource
{
  private readonly MyViewController parentController;

  public ViewSource(MyViewController parentController)
  {
    this.parentController=parentController;
  }
}

then MyViewController can never be garbage collected because of the circular reference created.

This seems like a common requirement, so what design do people use?

Should I make Source a weak reference? How is that done in c#? And isn't that dangerous as the caller may not know its a weak reference?

I'd suggest you to use the Profiler to see if that's really a memory leak. If you find that it's really an issue, you may weak reference the delegate using WeakReference class , that holds a weak reference to an object.

The garbage collector is quite capable of handling circular references. You don't have to worry about it.

I personally struggled often with abandoned memory on MonoTouch. What I do now is that I call Dispose() and set null every objects that exist in the objective c layer.

public class AView : UIView {

    private UIView tip;
    private UIView top;

    protected override void Dispose(bool disposing) {
        base.Dispose(disposing);
        this.ReleaseDesignerOutlets();

        if (this.tip != null) {
            this.tip.Dispose();
            this.tip = null;
        }

        if (this.top != null) {
            this.top.Dispose();
            this.top = null;
        }
    }

}

I don't think that "dealloc" is bound to the Dispose() method, as this one is only called when the managed object gets collected by the GC in the managed environment. It doesn't mean that the native object is gonna die, it just means that there is no reference in the managed environment to it. When a native object is going to surface in the managed environment, the Mono runtime creates (or reuse the one already created) a managed object that binds to the native one and increase the reference counter of the latter. When GC collects the managed object, it decreases the reference counter. If the Dispose() methods is never called though, it means that the GC didn't collect the object and therefore the object has still a ref counter that is at least one.

What I'm going to say now is going to contradict a little against what I just said. I think that the Mono Runtime doesn't allow a GCed object to surface again into the C# World (we don't want to have mysterious new C# objects with an empty state do we?). Thus, they actually don't garbage collect the object until the ref counter is at exactly one, even though the object has no reference to it in the managed environment. Therefore, when GC collects the object, they are actually released from the memory.

What (I think) is going on in your case is the View holds a reference to a DataSource that holds a reference to a ViewController. The ViewController is not GCed because the View holds a reference to it and even if no reference points to this View, the GC doesn't collect it because the object still has a ref count of 2. Indeed, the ViewController in the native world has still a reference to this view.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM