2016-08-11 16 views
5

Cần sự giúp đỡ của bạn trong việc tìm hiểu cách Swift nắm bắt ngữ nghĩa hoạt động khi hàm lồng nhau được gọi từ đóng. Vì vậy, tôi có hai phương pháp loadHappinessV1loadHappinessV2.Swift: Chụp ngữ nghĩa khi gọi hàm lồng nhau từ đóng. Tại sao trình biên dịch không tăng lỗi?

Trong phương pháp loadHappinessV1:

  • biên dịch nâng cao một lỗi nếu self không được xác định: lỗi: tham chiếu đến bất động sản 'callbackQueue' trong việc đóng cửa đòi hỏi rõ ràng 'tự' để làm cho ngữ nghĩa chụp rõ ràng
  • Để ngăn lỗi trình biên dịch, tôi chỉ định tham chiếu yếu đến self.

Trong phương pháp loadHappinessV2:

  • tôi quyết định giới thiệu hai chức năng lồng nhau và đơn giản hóa "cơ thể" hoạt động.
  • Trình biên dịch không tăng lỗi về ngữ nghĩa bắt giữ.

Tại sao trong phương pháp loadHappinessV2 trình biên dịch không gây ra lỗi về ngữ nghĩa chụp? Các hàm lồng nhau (cùng với biến số callbackQueue) không bị bắt?

Cảm ơn!

import PlaygroundSupport 
import Cocoa 

PlaygroundPage.current.needsIndefiniteExecution = true 

struct Happiness { 

    final class Net { 

     enum LoadResult { 
     case success 
     case failure 
     } 

     private var callbackQueue: DispatchQueue 
     private lazy var operationQueue = OperationQueue() 

     init(callbackQueue: DispatchQueue) { 
     self.callbackQueue = callbackQueue 
     } 

     func loadHappinessV1(completion: (LoadResult) -> Void) { 
     operationQueue.cancelAllOperations() 

     let hapynessOp = BlockOperation { [weak self] in 
      let hapynessGeneratorValue = arc4random_uniform(10) 
      if hapynessGeneratorValue % 2 == 0 { 
       // callbackQueue.async { completion(.success) } // Compile error 
       self?.callbackQueue.async { completion(.success) } 
      } else { 
       // callbackQueue.async { completion(.failure) } // Compile error 
       self?.callbackQueue.async { completion(.failure) } 
      } 
     } 
     operationQueue.addOperation(hapynessOp) 
     } 

     func loadHappinessV2(completion: (LoadResult) -> Void) { 
     operationQueue.cancelAllOperations() 

     func completeWithFailure() { 
      callbackQueue.async { completion(.failure) } 
     } 

     func completeWithSuccess() { 
      callbackQueue.async { completion(.success) } 
     } 

     let hapynessOp = BlockOperation { 
      let hapynessGeneratorValue = arc4random_uniform(10) 
      if hapynessGeneratorValue % 2 == 0 { 
       completeWithSuccess() 
      } else { 
       completeWithFailure() 
      } 
     } 
     operationQueue.addOperation(hapynessOp) 
     } 
    } 
} 

// Usage 
let happinessNetV1 = Happiness.Net(callbackQueue: DispatchQueue.main) 
happinessNetV1.loadHappinessV1 { 
    switch $0 { 
    case .success: print("Happiness V1 delivered .)") 
    case .failure: print("Happiness V1 not available at the moment .(") 
    } 
} 

let happinessNetV2 = Happiness.Net(callbackQueue: DispatchQueue.main) 
happinessNetV2.loadHappinessV2 { 
    switch $0 { 
    case .success: print("Happiness V2 delivered .)") 
    case .failure: print("Happiness V2 not available at the moment .(") 
    } 
} 
+1

Tôi không hoàn toàn chắc chắn về những gì lý do là nhưng tôi có thể hình dung ra là cả hai chức năng của bạn không tồn tại trong bối cảnh của bản thân và đó là tại sao trình biên dịch không phàn nàn ở đâu trong V1 bạn đang cố gắng gọi một phương thức tồn tại trong ngữ cảnh của bản thân. – Harsh

Trả lời

-1

Đoán đầu tiên của tôi là xác định rõ ràng các hàm lồng nhau dưới dạng chức năng @noescape hoặc Autoclosure. (some info here). Với một trong các loại đó, bạn không phải sử dụng "self", và khối hapynessOp sẽ nắm bắt các tham chiếu đến các hàm lồng nhau, do đó sẽ không có bất kỳ vấn đề nào ở đó

Nếu không, nó có thể là trường hợp lồng nhau các hàm thực sự được thêm vào chữ ký của lớp. Tôi nghĩ rằng nó có thể làm một số thử nghiệm và tìm hiểu (Có thể nhận được xung quanh để điều đó).

2

Tôi đã tìm thấy một số giải thích cách ngữ nghĩa nắm bắt hoạt động với các hàm lồng nhau. Nguồn: Nested functions and reference capturing.

xem xét sau Ví dụ:

class Test { 

    var bar: Int = 0 

    func functionA() -> (() ->()) { 
     func nestedA() { 
      bar += 1 
     } 
     return nestedA 
    } 

    func closureA() -> (() ->()) { 
     let nestedClosureA = { [unowned self]() ->() in 
      self.bar += 1 
     } 
     return nestedClosureA 
    } 
} 

Compiler nhắc nhở chúng ta phải duy trì quyền sở hữu chức năng closureA. Nhưng không nói gì về việc chụp self trong hàm functionA.

Hãy nhìn Swift Intermediate Language (SIL):
xcrun swiftc -emit-silgen Test.swift | xcrun swift-demangle > Test.silgen

sil_scope 2 { loc "Test.swift":5:10 parent @Test.Test.functionA() ->() ->() : [email protected](method) (@guaranteed Test) -> @owned @callee_owned() ->() } 
sil_scope 3 { loc "Test.swift":10:5 parent 2 } 

// Test.functionA() ->() ->() 
sil hidden @Test.Test.functionA() ->() ->() : [email protected](method) (@guaranteed Test) -> @owned @callee_owned() ->() { 
// %0            // users: %4, %3, %1 
bb0(%0 : $Test): 
    debug_value %0 : $Test, let, name "self", argno 1, loc "Test.swift":5:10, scope 2 // id: %1 
    // function_ref Test.(functionA() ->() ->()).(nestedA #1)() ->() 
    %2 = function_ref @Test.Test.(functionA() ->() ->()).(nestedA #1)() ->() : [email protected](thin) (@owned Test) ->(), loc "Test.swift":9:16, scope 3 // user: %4 
    strong_retain %0 : $Test, loc "Test.swift":9:16, scope 3 // id: %3 
    %4 = partial_apply %2(%0) : [email protected](thin) (@owned Test) ->(), loc "Test.swift":9:16, scope 3 // user: %5 
    return %4 : [email protected]_owned() ->(), loc "Test.swift":9:9, scope 3 // id: %5 
} 

Dòng strong_retain %0 : $Test, loc "Test.swift":9:16, scope 3 // id: %3 cho chúng ta biết rằng trình biên dịch làm tài liệu tham khảo mạnh mẽ cho $Test (được định nghĩa là self), tài liệu tham khảo này cuộc sống trong phạm vi 3 (là functionA) và không được phát hành tại thời điểm rời khỏi phạm vi 3.

Chức năng thứ hai closureA giao dịch với tham chiếu tùy chọn đến self. Nó được thể hiện bằng mã số là %2 = alloc_box [email protected]_weak Optional<Test>, var, name "self", loc "Test.swift":13:38, scope 8 // users: %13, %11, %9, %3.

sil [transparent] [fragile] @Swift.Int.init (_builtinIntegerLiteral : Builtin.Int2048) -> Swift.Int : [email protected](method) (Builtin.Int2048, @thin Int.Type) -> Int 

sil_scope 6 { loc "Test.swift":12:10 parent @Test.Test.closureA() ->() ->() : [email protected](method) (@guaranteed Test) -> @owned @callee_owned() ->() } 
sil_scope 7 { loc "Test.swift":17:5 parent 6 } 
sil_scope 8 { loc "Test.swift":15:9 parent 7 } 

// Test.closureA() ->() ->() 
sil hidden @Test.Test.closureA() ->() ->() : [email protected](method) (@guaranteed Test) -> @owned @callee_owned() ->() { 
// %0            // users: %5, %4, %1 
bb0(%0 : $Test): 
    debug_value %0 : $Test, let, name "self", argno 1, loc "Test.swift":12:10, scope 6 // id: %1 
    %2 = alloc_box [email protected]_weak Optional<Test>, var, name "self", loc "Test.swift":13:38, scope 8 // users: %13, %11, %9, %3 
    %3 = project_box %2 : [email protected] @sil_weak Optional<Test>, loc "Test.swift":13:38, scope 8 // users: %10, %6 
    strong_retain %0 : $Test, loc "Test.swift":13:38, scope 8 // id: %4 
    %5 = enum $Optional<Test>, #Optional.some!enumelt.1, %0 : $Test, loc "Test.swift":13:38, scope 8 // users: %7, %6 
    store_weak %5 to [initialization] %3 : $*@sil_weak Optional<Test>, loc "Test.swift":13:38, scope 8 // id: %6 
    release_value %5 : $Optional<Test>, loc "Test.swift":13:38, scope 8 // id: %7 
    // function_ref Test.(closureA() ->() ->()).(closure #1) 
    %8 = function_ref @Test.Test.(closureA() ->() ->()).(closure #1) : [email protected](thin) (@owned @box @sil_weak Optional<Test>) ->(), loc "Test.swift":13:30, scope 8 // user: %11 
    strong_retain %2 : [email protected] @sil_weak Optional<Test>, loc "Test.swift":13:30, scope 8 // id: %9 
    mark_function_escape %3 : $*@sil_weak Optional<Test>, loc "Test.swift":13:30, scope 8 // id: %10 
    %11 = partial_apply %8(%2) : [email protected](thin) (@owned @box @sil_weak Optional<Test>) ->(), loc "Test.swift":13:30, scope 8 // users: %14, %12 
    debug_value %11 : [email protected]_owned() ->(), let, name "nestedClosureA", loc "Test.swift":13:13, scope 7 // id: %12 
    strong_release %2 : [email protected] @sil_weak Optional<Test>, loc "Test.swift":15:9, scope 7 // id: %13 
    return %11 : [email protected]_owned() ->(), loc "Test.swift":16:9, scope 7 // id: %14 
} 

Vì vậy, nếu chức năng lồng truy cập một số tính chất quy định tại self, sau đó chức năng lồng nhau giữ tham chiếu mạnh để self. Trình biên dịch không thông báo về nó (Swift 3.0.1).

Để tránh hành vi này, chúng tôi chỉ cần sử dụng các bao đóng thay cho các chức năng lồng nhau. Sau đó trình biên dịch sẽ thông báo về việc sử dụng self.

gốc ví dụ có thể được rewtitten như sau:

import PlaygroundSupport 
import Cocoa 

PlaygroundPage.current.needsIndefiniteExecution = true 

struct Happiness { 

    final class Net { 

     enum LoadResult { 
     case success 
     case failure 
     } 

     private var callbackQueue: DispatchQueue 
     private lazy var operationQueue = OperationQueue() 

     init(callbackQueue: DispatchQueue) { 
     self.callbackQueue = callbackQueue 
     } 

     func loadHappinessV1(completion: @escaping (LoadResult) -> Void) { 
     operationQueue.cancelAllOperations() 

     let hapynessOp = BlockOperation { [weak self] in 
      let hapynessGeneratorValue = arc4random_uniform(10) 
      if hapynessGeneratorValue % 2 == 0 { 
       // callbackQueue.async { completion(.success) } // Compile error 
       self?.callbackQueue.async { completion(.success) } 
      } else { 
       // callbackQueue.async { completion(.failure) } // Compile error 
       self?.callbackQueue.async { completion(.failure) } 
      } 
     } 
     operationQueue.addOperation(hapynessOp) 
     } 

     func loadHappinessV2(completion: @escaping (LoadResult) -> Void) { 
     operationQueue.cancelAllOperations() 

     // Closure used instead of nested function. 
     let completeWithFailure = { [weak self] in 
      self?.callbackQueue.async { completion(.failure) } 
     } 

     // Closure used instead of nested function. 
     let completeWithSuccess = { [weak self] in 
      self?.callbackQueue.async { completion(.success) } 
     } 

     let hapynessOp = BlockOperation { 
      let hapynessGeneratorValue = arc4random_uniform(10) 
      if hapynessGeneratorValue % 2 == 0 { 
       completeWithSuccess() 
      } else { 
       completeWithFailure() 
      } 
     } 
     operationQueue.addOperation(hapynessOp) 
     } 
    } 
} 

// Usage 
let happinessNetV1 = Happiness.Net(callbackQueue: DispatchQueue.main) 
happinessNetV1.loadHappinessV1 { 
    switch $0 { 
    case .success: print("Happiness V1 delivered .)") 
    case .failure: print("Happiness V1 not available at the moment .(") 
    } 
} 

let happinessNetV2 = Happiness.Net(callbackQueue: DispatchQueue.main) 
happinessNetV2.loadHappinessV2 { 
    switch $0 { 
    case .success: print("Happiness V2 delivered .)") 
    case .failure: print("Happiness V2 not available at the moment .(") 
    } 
} 
Các vấn đề liên quan