skip to Main Content

I currentl have a class B which inhertis from the AVCapturePhotoCaptureDelegate protocol, which is set on B’s initiazation, however, when i try to use this delegate inside class A, the protocol methods ends up not being called. E.g it looks something like this:

class A {
    private var photoOutput = AVCapturePhotoOutput()
    
    @objc func captureFrame() {
        ...
        let b = B(a: self)
        photoOutput.capturePhoto(with: photoSettings, delegate: b)
    }

    func somemethod() {
        ...
    }

}

class B: AVCapturePhotoCaptureDelegate {
    weak var a: A?
    weak var delegate: AVCapturePhotoCaptureDelegate?

    init(a: A) {
        self.a = a
        super.init()
        self.delegate = self
    }
    
    // photoOutput does not end up getting called...
    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
        print("Delegate method called")
        a.somemethod()
    }
}

The reason that i want to do it like this is that the captureFrame method might end up being called in an iteration and we want to persist some data passed in as agurments to avoid race conditions.

It’s quite possible that i’ve misunderstood how this should work.

I have also tried calling the delegate variable direcrly, e.g:

photoOutput.capturePhoto(with: photoSettings, delegate: b.delegate)

2

Answers


  1. Since you make it week here

    weak var a: A?
    

    You need to retain it inside class A so replace

    @objc func captureFrame() {
        ...
        let b = B(a: self)
    

    With

    var b:B! 
    @objc func captureFrame() {
        ...
        b = B(a: self)
    

    Edit:

    When you make the code like this

    @objc func captureFrame() {
        ...
        let b = B(a: self)
        photoOutput.capturePhoto(with: photoSettings, delegate: b)
    }
    

    everything will be deallocated at the end of the function execution , so b will be released and when this happens the 2 variables inside it

    weak var a: A?
    weak var delegate: AVCapturePhotoCaptureDelegate?
    

    Will also be released with the main object , leaving your delegate listener a nil so your method photoOutput won’t be called as the containing object is deallocated , but when you make b as instance variable it will exist as the containing object A exists , so the method will be called eventually.

    let b inside the function is considered a weak local variable who’s life time is the end of function execution

    Login or Signup to reply.
  2. You need to consider how you want to manage the lifetime of B here. At the end of captureFrame, there’s no reference to b anymore, so it is released. You likely want to manage it as a property of A, maybe along these lines:

    class A {
        lazy var b: B = B(a: self)
    
        @objc func captureFrame() { ... }
    }
    

    This would allow all calls to captureFrame to share a single B (which could be correct). Alternately, you could hold them in a Dictionary or Array if you need to manage separate instances of B for each call.

    If you want calls to captureFrame to cancel previous in-progress calls, then @Shehata’s approach is a way to do that (though I would use a regular Optional there rather than !). Every time b is set, it releases the previous object.


    Based on your comments below about managing multiple calls, and dealing with a payload, I believe this is the approach you’re looking for:

    class A {
        private var photoOutput = AVCapturePhotoOutput()
    
        // I don't know what your "payload" is, but it doesn't matter
        typealias Payload = String
    
        // A dictionary of all "in-flight" captures based on their uniqueID
        var captures: [Int64: Payload] = [:]
    
        // All captures share the same delegate
        lazy var b = B(a: self)
    
        @objc func captureFrame(attach payload: Payload) {
    
            // Every capture requires its own settings
            let photoSettings = AVCapturePhotoSettings()
    
            // Request the capture
            photoOutput.capturePhoto(with: photoSettings, delegate: b)
    
            // Store the ID of the capture in the "in-flight" dictionary
            captures[photoSettings.uniqueID] = payload
        }
    
        func somemethod(output: AVCapturePhotoOutput, photo: AVCapturePhoto, error: Error?) {
            let id = photo.resolvedSettings.uniqueID
    
            // Look up the payload in the "in-flight" dictionary (and remove it)
            if let payload = captures.removeValue(forKey: id) {
                // process output, photo, and payload, considering possible errors
            }
        }
    }
    
    class B: NSObject, AVCapturePhotoCaptureDelegate {
        weak var a: A?
    
        // I removed `delegate` here. I'm not sure what it was for.
    
        init(a: A) {
            self.a = a
            super.init()
        }
    
        func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
            print("Delegate method called")
    
            // Just pass all the data back to `A`. 
            // Or you could some processing here, and pass it back to A.
            // Just make sure to pass the uniqueID in some way.
            a?.somemethod(output: output, photo: photo, error: error)
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search