无主变量和异步函数

问题描述

我有一个具有无主变量的类。像这样:

class Student {
  uNowned var school: School

  init(_ school: School) {
    self.school = school
  }
}

现在假设我在类中有一个转义异步函数

class Student {
  uNowned var school: School

  init(_ school: School) {
    self.school = school
  }

  // When this function is first called
  // I can guarantee school has not been de-initialized
  func reportAttendance(completionHandler: @escaping (() -> Void)) {
    database.studentDidReportAttendance(student: self) {
      // I cannot guarantee school has not been deinitialized 
      // when this callback function returns!
      school.updateAttendance() 
      completionHandler()
    }
  }
}

我的问题是我可以保证当 reportAttendance()调用时,学校没有被取消初始化,但是在数据库响应所需的时间内,我不能保证学校不会被取消初始化。因此,如果是这样,那么当异步块返回并尝试访问学校时,我会收到运行时错误

我的问题是如何在第一次调用函数时创建一个对 school 的临时强引用,然后在我能够安全地在 school 上运行 updateAttendance 后释放它?

谢谢

解决方法

你可以在你的函数中创建一个局部变量来保持强引用;看这个测试:

class School {
    deinit {
        print ("School.deinit")
    }
}

class Student {
    unowned var school:School
    
    init(_ school: School) {
      self.school = school
    }
    
    func printSchool() -> (() -> Void) {
        let strongSchool = self.school
        return { print ("I'm going to \(strongSchool)") }
    }
}


if (1==1) {
    var s:School? = School()
    let student = Student(s!)
    let p = student.printSchool()
    s = nil
    p() // Won't crash
}

没有 strongSchool,代码段会崩溃。

问题是我们在闭包外创建了一个强引用——此时你保证它仍然存在——然后在闭包内引用它。这样,我们

  • 不捕获 student/self,而只捕获 school
  • 避免循环引用,以防学生可以强访问 database 并将其自身作为属性保留(因为 self -> database -> closure - > self) 并且只要 school 不也引用数据库(嗯,写起来比想象的要复杂)

但正如@matt 所说,unowned 非常危险,应该首选 weak,除非在一些不常见的情况下,您拥有数以万计的无主对象,这会导致大量的内务管理开销。

,

似乎是一个 x-y 问题。如果 Student 的 school 可以消失而该 Student 仍然存在,那就是对 unowned 的错误使用。说 unowned 的前提是那必须是绝对不可能的。