Объект, созданный внутри функции, исчезает, несмотря на то, что передается дальше - PullRequest
1 голос
/ 08 февраля 2020

Исходя из другого языка, я был удивлен молчаливой ошибкой, когда объект, который передается методу в качестве обратного вызова, внезапно никогда не вызывается. Ссылка на обратный вызов каким-то образом потеряна.

Минимальный (не запускаемый) пример проблемы:

class Foo: NSObject, AVCaptureFileOutputRecordingDelegate {
    func bar() {
        let out = AVCaptureMovieFileOutput()
        let delegate = Foo() //nonsensical in this case, in normal case diff. object will be used
        out.startRecording(to: /*...*/, recordingDelegate: delegate)

        //Result: delegate methods are never called
    }
}

Минимальный (не запускаемый) пример «решение»:

class Foo: NSObject, AVCaptureFileOutputRecordingDelegate {
    func bar() {
        let out = AVCaptureMovieFileOutput()
        out.startRecording(to: /*...*/, recordingDelegate: self)

        //Result: delegate methods are called when approperiate
    }
}

Я озадачен ...

  • Почему это происходит?
  • Как предотвратить такую ​​ситуацию?
  • Является ли такой тихий сбой по замыслу?

Этот вопрос возникает из AVCaptureMovieFileOutput никогда не вызывает делегат при записи экрана

1 Ответ

1 голос
/ 08 февраля 2020

Большинство delgates имеют значение weak, поэтому они не создают цикл сохранения, см. Automati c Подсчет ссылок (AR C) . Большая часть этого предполагает, что хранилище делегата для того, что вы используете, является weak.

В вашем первом примере единственная надежная ссылка на объект содержится в функции bar, так как делегат является слабая ссылка. Как только функция завершается, единственная оставшаяся сильная ссылка исчезает, и объект может быть удален.

class Foo: NSObject, AVCaptureFileOutputRecordingDelegate {
    func bar() {
        let out = AVCaptureMovieFileOutput()
        let delegate = Foo() //object created, and strong reference stored in variable called delegate
        out.startRecording(to: /*...*/, recordingDelegate: delegate) // object passed in and likely stored in a weak variable inside of the `out` object. This means it will not keep a strong reference to your Foo object.

        //Result: delegate methods are never called
    }// local variable called delegate goes out of scope, strong reference to your Foo object goes away, there are no more strong references, can be deleted.
}

Во втором примере при использовании self в качестве делегата, self, вероятно, застревает вокруг после окончания функции bar, поэтому делегат остается.

class Foo: NSObject, AVCaptureFileOutputRecordingDelegate {
    func bar() {
        let out = AVCaptureMovieFileOutput()
        out.startRecording(to: /*...*/, recordingDelegate: self) // pass `self`, which presumably has something else referencing it with a strong reference, so it stays alive

        //Result: delegate methods are called when approperiate
    } // `self` still has strong references to it (somewhere else) keeping it alive after the function call, so the weak reference that is being used to call the delegate methods can still operate! Yay!
}

Надеюсь, это ответит на вопрос «почему».

Что касается предотвращения, вам необходимо убедиться, что вы сохраняете строгое указание на любые делегаты (или слабые переменные), которым вы хотите остаться в живых.

Это поведение разработано так, как оно используется предотвратить сохранение циклов и утечек памяти. При разработке ваших собственных классов с делегатами вы должны использовать weak соответствующим образом, чтобы предотвратить сохранение циклов при необходимости.

Что касается молчания сбоя, во многих случаях делегаты являются необязательными, и для делегата не считается ошибкой ноль, а для функций делегата не вызывается. Много раз функции вызываются как delegate?.delegateMethod() намеренно, так что функция будет вызываться, если вы хотите иметь делегата, и это не вызовет проблемы, если вы не хотите иметь делегата.

...