Swift Selectors and Closures Discussion

2020-07-30 04:29发布

问题:

I searched a lot and haven't found anything that really helped to solve my problem...

I'm trying to build a simple UIControl from scratch, like a UIButton. It can't be subclass of UIControl for particular reasons.

I need this control to do something like this:

myControl.addTarget(target: AnyObject?, action: Selector, forControlEvents: UIControlEvents)

The problem is that the implementation of the method that execute this selector when the button is touched needs the "performSelector:" method.

Swift doesn't have a "performSelector:". So I taught it could be implemented using closures.

I couldn't figure out how to capture the objects I want to modify inside the closure. And I'm not sure how I would deal with reference cycles and other things like that.

I don't even know if I am on the right path to succeed this. I'm sure you guys can put me on the right track!

I'm from Brazil, sorry for my poor English! Thanks! :D

Here is what a have so far...

struct ClosureForEvent {
    var closure:(control:MyControl!)->()
    var event:UIControlEvents 
}

class MyControl {
     private var closures:[ClosureForEvent]?

     init() {}

     func addClosureFor(event:UIControlEvents, closure:(control:MyControl!)->()) {

          if closures == nil {
              closures = [ClosureForEvent(closure: closure, event: event)]
          }
          else {
              closures!.append(ClosureForEvent(closure: closure, event: event))
          }
      }


      func executeClosuresOf(event:UIControlEvents) {
          if closures != nil {
              for closure in closures! {
                  if closure.event == event {
                      closure.closure(control: control)
                  }
              }
          }
      }
  }

  class Test {
       var testProperty = "Default String"

       init() {
            let control = MyControl()
            control.addClosureFor(UIControlEvents.TouchUpInside, closure: { (control) -> () in
            self.testProperty = "This is making a reference cycle?"
            })
       }
  }

回答1:

In your case at the moment, it seems that no cyclic-reference is formed. But it's possible to accidentally be formed in the future. Like the followings for the instance.

// In the `Test` class.
let control = MyControl()
init() {
    control.addClosureFor(UIControlEvents.TouchUpInside, closure: { (control) -> () in
        self.testProperty = "This is making a reference cycle?"
    })
}

In this code, self has a reference to the control as its field and the control has a reference to self through the closure added by addClosureFor. This forms a cyclic reference so these objects in the cycle are never released.

closure capture list helps this issue.

control.addClosureFor(UIControlEvents.TouchUpInside) { [weak self] /* <-- */ control -> () in
    // Here, `self` is visible as `Optional<Test>` so the `?` operator is required.
    self?.testProperty = "This is making a reference cycle?"
    return
}

In the block in this code above, self is weakly captured so that an only one unidirectional reference is formed from self to the control. Note that, now in the closure, the access to self isn't assured because self is weakly-referenced. You have to consider self an optional value.

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html