1
votes

Comment transmettre correctement une référence faible à l'appelant d'une fonction?

Disons que vous avez ...

 Worker.compute(forMe: self)

et que vous avez quelque chose comme ...

class Worker {
   static func compute(forMe: Boss) {
      .. do some work that takes 10 seconds .. 
      forMe?.callback()
   }
}

Donc: quelque part dans une instance particulière de Boss , disons b , vous appelez quelque chose comme ...

class Boss {
   func callback() { }
}

Dites que les 10 secondes passent. Dites à 7 secondes, b cesse d'exister (enfin, je veux qu'il cesse d'exister). À la fin des 10 secondes, je veux que compute dise: "Oh, putain, il semble que forMe n'existe plus. Je ne ferai pas le rappel et je donnerai up. "

Ainsi, le" forMe "à l'intérieur de" compute "devrait être faible. (Je suppose?)

Vraiment, comment réalisez-vous ce concept de base dans Swift?


3 commentaires

En supposant que la fonction de calcul fasse quelque chose de façon asynchrone avec une fermeture, vous captureriez le paramètre faiblement dans la liste de capture de cette fermeture


vous devez en donner un exemple pour, euh, d'autres personnes @dan! :)


Il est impossible que forMe devienne nul à moins que vous ne fassiez quelque chose d'asynchrone, car les arguments sont capturés fortement.


3 Réponses :


1
votes

Le mieux que j'ai pu trouver était cette réponse

Fondamentalement, cela consiste à créer votre propre structure Weak, car les arguments ne peut être "fort" que dans Swift, pour le moment.

public struct Weak<T> where T: AnyObject {
    public weak var object: T?

    public init(_ object: T?) {
        self.object = object
    }
}

 func compute(forMe: Weak<Boss>) {        
    if let boss = forMe.object {
        boss.callback()
    }
    else {
        print("Oh, damn, it seems that forMe no longer exists. I will not do the callback, and give up.")
    }
}


0 commentaires

1
votes

Si votre fonction compute fonctionne de manière asynchrone et reçoit un rappel, elle peut capturer faiblement votre paramètre lors de la fermeture de ce travail:

static func compute(forMe: Boss) {
    doSomethingAsync { [weak forMe] in
        // forMe will be nil in here if it was deallocated before this is called
        forMe?.callback()
    }
}


0 commentaires

1
votes

dan a probablement offert la réponse la plus simple pour votre cas d'utilisation.

Si jamais vous avez besoin de stocker une "méthode faible", vous pouvez utiliser ceci:

final class WeakMethodTestCase: XCTestCase {
  func test_method_noParameters() throws {
    var reference: Reference? = Reference()

    let assign1234 = WeakMethod(reference: reference, method: Reference.assign1234)

    try assign1234()
    XCTAssertEqual(reference?.property, 1234)

    reference = nil
    XCTAssertThrowsError( try assign1234() ) {
      XCTAssert($0 is WeakMethod<Reference, (), Void>.ReferenceDeallocatedError)
    }
  }

  func test_method_2Parameters() throws {
    var reference: Reference? = Reference()

    let assignSum = WeakMethod(reference: reference, method: Reference.assignSum)

    try assignSum(2, 3)
    XCTAssertEqual(reference?.property, 5)

    reference = nil
    XCTAssertThrowsError( try assignSum(2, 3) ) {
      XCTAssert($0 is WeakMethod<Reference, (Int, Int), Void>.ReferenceDeallocatedError)
    }
  }

  func test_closure_noParameters() throws {
    var reference: Reference? = Reference()

    let assign1234 = WeakMethod(reference: reference) {
      reference in { reference.property = 1234 }
    }

    try assign1234()
    XCTAssertEqual(reference?.property, 1234)

    reference = nil
    XCTAssertThrowsError( try assign1234() ) {
      XCTAssert($0 is WeakMethod<Reference, (), Void>.ReferenceDeallocatedError)
    }
  }

  func test_closure_1Parameter() throws {
    var reference: Reference? = Reference()

    let assign = WeakMethod(reference: reference) {
      reference in { reference.property = $0 }
    }

    try assign(1234)
    XCTAssertEqual(reference?.property, 1234)

    reference = nil
    XCTAssertThrowsError( try assign(1234) ) {
      XCTAssert($0 is WeakMethod<Reference, Int, Void>.ReferenceDeallocatedError)
    }
  }
}

private final class Reference {
  var property = 1

  func assign1234() {
    property = 1234
  }

  func assignSum(int0: Int, int1: Int) {
    property = int0 + int1
  }
}
var boss: Boss? = .init()
let callBack = WeakMethod(reference: boss, method: Boss.callBack)
try callBack()
boss = nil
try callBack() // ReferenceDeallocatedError

J'ai mis le "B" en majuscule car callBack est un prédicat (verbe + objet), pas un nom. Je déconseille cependant de l'utiliser sous quelque forme que ce soit. «callback» est un terme de l'antiquité qui n'est pas utilisé dans Swift.

Remarque: vous pouvez également créer vos propres «méthodes» en utilisant des fermetures, si vous avez une raison.

XXX


0 commentaires