UIView, CMDeviceMotionHandler : unowned may only b

2019-09-05 16:51发布

问题:

I'm creating a UIView that listens to CMDeviceMotion Events:

class MyView: UIView{


    private var motionManager = CMMotionManager()
    let motionQueue = NSOperationQueue()


    override func awakeFromNib() {
        self.setupView()
    }

    func setupView(){
        self.motionManager.deviceMotionUpdateInterval = 0.5
        self.motionManager.startDeviceMotionUpdatesUsingReferenceFrame(.XArbitraryZVertical, toQueue: self.motionQueue, withHandler: self.motionHandler)
    }

    // MARK: - CMDeviceMotionHandler

    let motionHandler : CMDeviceMotionHandler = {
        [unowned self] (motion,error) in
    }
}

I'd like to declare my CMDeviceMotionHandler closure as a member variable however I get the error:

'unowned' may only be applied to class and class-bound protocol types, not 'MyView -> () -> MyView'

MyView is a UIView which in turn is a class so I don't get why it's complaining that unowned can not be applied.

I've searched for other questions with the same issue but most of them dealt with lazily computed variables. How do I resolve this error for my scenario?

回答1:

The line of code you're on is actually run during the init function. self is not available until after all stored properties are initialized. You're in the middle of the process.

The error message is quite confusing and useless, because self in the context of property initializers is not an instance of a MyView, but a tricky meta-type: a class-member function that is unbound to its instance, but becomes bound and usable once the instance is passed in as the first argument. It's to do with member functions being implemented in Swift with currying, and is rather academic unless you love type calculus.

You have two options:

  1. Declare it indeed as lazy var instead of let, so the code is not run during init but in fact at first use.

  2. Declare it without initialization as an Optional. Depending on your design constraints this is either cumbersome or elegant. No way to know. Anyway, before it is needed, initialize it to a non-nil value. An easy place to do this, if this UIView is used strictly within Storyboard, is to initialize it within awakeFromNib().