代码之家  ›  专栏  ›  技术社区  ›  lurning too koad

为什么在课堂上需要声明自己的结构中不需要声明自己?

  •  0
  • lurning too koad  · 技术社区  · 5 年前

    为什么要宣布 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
            }
        }
    }
    
    0 回复  |  直到 5 年前
        1
  •  7
  •   Rob Md Fahim Faez Abir    5 年前

    包括的目的 self 在转义闭包(无论是可选闭包还是明确标记为 @escaping )使用引用类型是为了使捕获语义明确。如果我们删除,编译器会警告我们 自己 参考:

    在闭包中引用属性“someProperty”需要显式使用“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)
               }
           }
       }
      
    • 或捕获 自己 :

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

    这两种方法也明确了您要捕获的内容。

        2
  •  3
  •   Jessy    5 年前

    对于类,闭包提供了一种递增引用计数的机制,从而“保持对象存活”。

    也许 吧 你只需要捕捉就可以了 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)()
    

    你可以想象那里的不变性,因为没有&。