Skip to content

Commit

Permalink
[UIKit] UIGestureRecognizer, support a way of unsubscribing without c…
Browse files Browse the repository at this point in the history
…reating cycles

This now tracks all the uses of AddTarget with delegates by recording
the Token/Selector pair and making `Dispose()` release all the linkage
as well as providing an enumerator that can be used to get all the
registered Action handlers - this can then be used with .First() and
then passed to `RemoveTarget`.

This addresses dotnet#4190

This initial patch is here for discussion of the approach, want to
review and test this before we merge.
  • Loading branch information
migueldeicaza committed Aug 30, 2018
1 parent c1f759e commit d282075
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 20 deletions.
52 changes: 32 additions & 20 deletions src/UIKit/UIGestureRecognizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@

using System;
using System.Collections;
using System.Collections.Generic;
using Foundation;
using ObjCRuntime;
using CoreGraphics;

namespace UIKit {
public partial class UIGestureRecognizer {
object recognizers;
//
// Tracks the targets (NSObject, which we always enforce to be Token) to the Selector the point to, used when disposing
//
Dictionary<Token,IntPtr> recognizers = new Dictionary<Token,IntPtr> ();
const string tsel = "target";
internal const string parametrized_selector = "target:";
#if !XAMCORE_2_0
Expand All @@ -30,18 +34,26 @@ public partial class UIGestureRecognizer {
{
}

// Called by the Dispose() method
void OnDispose ()
{
foreach (var kv in recognizers)
RemoveTarget (kv.Key, kv.Value);
recognizers = null;
}

//
// Signature swapped, this is only used so we can store the "token" in recognizers
//
public UIGestureRecognizer (Selector sel, Token token) : this (token, sel)
{
recognizers = token;
recognizers [token] = sel.Handle;
MarkDirty ();
}

internal UIGestureRecognizer (IntPtr sel, Token token) : this (token, sel)
{
recognizers = token;
recognizers [token] = sel;
MarkDirty ();
}

Expand Down Expand Up @@ -111,17 +123,7 @@ void RegisterTarget (Token target, IntPtr sel)
{
AddTarget (target, sel);
MarkDirty ();
if (recognizers == null)
recognizers = target;
else {
Hashtable table = recognizers as Hashtable;
if (table == null){
table = new Hashtable ();
table [recognizers] = recognizers;
recognizers = table;
}
table [target] = target;
}
recognizers [target] = sel;
}

public void RemoveTarget (Token token)
Expand All @@ -130,12 +132,22 @@ public void RemoveTarget (Token token)
throw new ArgumentNullException ("token");
if (recognizers == null)
return;
if (recognizers == token)
recognizers = null;
Hashtable asHash = recognizers as Hashtable;
if (asHash != null)
asHash.Remove (token);
RemoveTarget (token, token is ParametrizedDispatch ? Selector.GetHandle (parametrized_selector) : Selector.GetHandle (tsel));
if (recognizers.ContainsKey (token)){
var sel = recognizers [token];
recognizers.Remove (token);
RemoveTarget (token, sel);
}
}

//
// Used to enumerate all the registered handlers for this UIGestureRecognizer
//
public IEnumerable<Token> GetTargets ()
{
if (recognizers == null)
yield break;
foreach (var kv in recognizers)
yield return kv.Key;
}
}

Expand Down
1 change: 1 addition & 0 deletions src/uikit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5868,6 +5868,7 @@ partial interface UIFontDescriptor : NSSecureCoding, NSCopying {

#if !WATCH
[BaseType (typeof(NSObject), Delegates=new string [] {"WeakDelegate"}, Events=new Type[] {typeof (UIGestureRecognizerDelegate)})]
[Dispose ("OnDispose ();")]
interface UIGestureRecognizer {
[DesignatedInitializer]
[Export ("initWithTarget:action:")]
Expand Down

0 comments on commit d282075

Please sign in to comment.