为什么在类需要的结构中不需要声明自我?

时间:2020-10-06 20:29:43

标签: swift

为什么在类需要的结构中声明self不需要?我不知道是否还有其他示例,但是使用转义的闭包,是这样。如果闭包是非可选的(因此是不可转义的),则无需在两者之一中声明self

class SomeClass {
    let someProperty = 1
    
    func someMethod(completion: (() -> Void)?) {}
    
    func anotherMethod() {
        someMethod {
            print(self.someProperty) // declaring self is required
        }
    }
}

struct SomeStruct {
    let someProperty = 1
    
    func someMethod(completion: (() -> Void)?) {}
    
    func anotherMethod() {
        someMethod {
            print(someProperty) // declaring self is not required
        }
    }
}

2 个答案:

答案 0 :(得分:5)

在转义的闭包(无论是可选的闭包还是显式标记为self的内部)中使用属性时,包括@escaping的目的是使捕获语义明确。如编译器警告我们,如果我们删除self参考,

在闭包中引用属性'someProperty'要求显式使用'self'以使捕获语义明确。

但是结构没有捕获模棱两可的语义。您始终在转义的闭包内部处理副本。引用类型仅是模棱两可的,您需要在self上清楚说明可能在何处引入强引用周期,要引用的实例等。


顺便说一句,对于类类型,将self与属性一起引用并不是使捕获语义明确的唯一方法。例如,您可以使用“捕获列表”使意图明确:

  • 仅捕获属性:

     class SomeClass {
         var someProperty = 1
    
         func someMethod(completion: @escaping () -> Void) { ... }
    
         func anotherMethod() {
             someMethod { [someProperty] in    // this captures the property, but not `self`
                 print(someProperty)
             }
         }
     }
    
  • 或捕获self

     class SomeClass {
         var someProperty = 1
    
         func someMethod(completion: @escaping () -> Void) { ... }
    
         func anotherMethod() {
             someMethod { [self] in            // this explicitly captures `self`
                 print(someProperty)
             }
         }
     }
    

这两种方法都可以使您清楚地捕获所捕获的内容。

答案 1 :(得分:3)

对于类,闭包提供一种增加引用计数的机制,从而“使对象保持活动状态”。

也许您只需捕获someProperty就可以了。也许不吧!编译器不知道您是否使用闭包来增加引用,因此可以使您清楚自己的意图。

不仅是没有结构的问题,而且突变的可能性也是如此,这是严格禁止的。

假设您希望anotherMethod在结构中允许任何类型的突变。您可以先将其标记为变异…

struct SomeStruct {
  func someMethod(completion: (() -> Void)?) {}

  mutating func anotherMethod() {
    someMethod {
      self
    }
  }
}

…但是不,那是一个错误:

转义转义符捕获变异的'self'参数

尽管捕获self……

  mutating func anotherMethod() {
    someMethod { [self] in
      self
    }
  }

...那很好。

这也是Swift允许的唯一选项。在结构中使用转义的闭包时,只能使用实例的不可变捕获。即[self] in是隐式的,用于非突变方法。

这会产生意想不到的结果。小心点。

struct SomeStruct {
  var someProperty = 1

  func anotherMethod() {
    DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
      print(someProperty)
    }
  }
}

var s = SomeStruct()
s.anotherMethod() // 1, even though it's printed after the following reassignment 
s.someProperty = 2
s.anotherMethod() // 2

我认为思考一下方法语法的简写会有所帮助。

s.anotherMethod()

是真的

SomeStruct.anotherMethod(s)()

您可以看到那里的不变性,因为这里没有&。