2014-10-26 16 views
5

Tôi phát hiện ra Swift đóng cửa không giữ lại các biến bị bắt không giống như mong đợi của tôi.Việc đóng cửa Swift có giữ lại các biến bị bắt không?

class AAA { 
} 
var a1 = AAA() as AAA?     // expects RC == 1 
var a2 = {()->AAA? in return a1 }  // expects RC == 2, retained by `Optional<AAA>` 
a1  = nil       // expects RC == 1 
a2()          // prints nil, ???? 

Tôi rất bối rối với điều này vì tôi đã tin rằng các biến bị bắt giữ sẽ được giữ lại theo mặc định. Nhưng, nếu tôi chụp nó một cách rõ ràng bằng cách sử dụng danh sách chụp, nó đang được giữ lại.

class AAA { 
} 
var a1 = AAA() as AAA? 
var a2 = { [a1]()->AAA? in return a1 } 
a1  = nil 
a2() // prints {AAA}, alive as expected. 

Tôi đọc lại hướng dẫn Swift, nhưng tôi không thể tìm thấy mô tả liên quan. Danh sách chụp được sử dụng để đặt unowned một cách rõ ràng và tôi vẫn còn bối rối. Hành vi chính xác là gì và tại sao điều này lại xảy ra?

Trả lời

5

Có đó là tài liệu trong Capturing Values:

Swift xác định những gì cần được chụp bằng cách tham khảo và những gì nên được sao chép theo giá trị. Bạn không cần chú thích số lượng hoặc runTotal để nói rằng chúng có thể được sử dụng trong hàm gia tăng lồng nhau. Swift cũng xử lý tất cả các quản lý bộ nhớ liên quan đến việc xử lý runTotal khi nó không còn cần thiết bởi hàm incrementor.

Quy tắc là: nếu bạn tham chiếu biến đã chụp mà không sửa đổi, biến đó được ghi lại theo giá trị. Nếu thay vào đó bạn sửa đổi nó, nó sẽ bị bắt bởi tham chiếu. Tất nhiên trừ khi bạn ghi đè rõ ràng điều đó bằng cách xác định danh sách chụp.

Hợp đồng bổ sung Các câu trên có vẻ không chính xác. Các ảnh chụp được thực hiện bằng cách tham chiếu bất kể chúng được sửa đổi hay không nằm trong phần đóng. Đọc nhận xét @newacct.

+1

Không đúng sự thật. Ví dụ, 'func test() {var x = 42; cho f = {println (x)}; x = 43; f()}; test() 'in' 43', điều này có nghĩa là đóng cửa sẽ nắm bắt biến bằng cách tham chiếu, mặc dù nó tham chiếu biến mà không sửa đổi nó. – newacct

+0

Cảm ơn @newacct. Mặc dù không nói rõ ràng những gì tôi đã viết, tài liệu này khiến tôi nghĩ rằng nó hoạt động theo cách đó. – Antonio

+0

Có cách nào để buộc trình biên dịch nắm bắt bằng tham chiếu không? Tôi có một trường hợp mà tôi có hai đóng cửa mỗi cố gắng truy cập một giá trị được chia sẻ. Một đóng cửa thay đổi giá trị đã capture và giá trị kia chỉ đọc nó. Tuy nhiên, một trong đó đọc nó là nhận được một bản sao. Tôi cần phải ép nó để nắm bắt bằng cách tham chiếu. –

1

Tôi đã đăng cùng một câu hỏi trên Diễn đàn nhà phát triển Apple và có a discussion. Mặc dù mọi người không nói về tài liệu tham khảo đếm rất nhiều, nhưng tôi có một số ý tưởng. Đây là kết luận của tôi:

  • Giá trị luôn được sao chép khi được gắn với tên (rõ ràng) mới. Bất kể var hoặc let.
  • Đã sao chép trong các loại tham chiếu có nghĩa là RC + 1. Bởi vì nó sao chép con trỏ mạnh như C++ shared_ptr<T>.
  • Đóng cửa (đóng cửa chụp) không thay đổi bất cứ điều gì vì không có tên mới. Nó giống như bạn sử dụng chúng trong cùng một ngăn xếp. Đây là phương thức bằng cách tham chiếu. Không có gì liên quan đến RC, và không thay đổi RC bởi vì nó là một cái gì đó giống như tài liệu tham khảo trong C + +.
  • Danh sách chụp là tên rõ ràng (let). Vì vậy, nó gây ra sao chép và nó làm cho RC + 1.
  • Khi đóng cửa được trả về từ một hàm, nó có thể bị ràng buộc với tên. Nếu có, RC + 1 vì ràng buộc mới với tên.
  • RC-1 khi giá trị (để con trỏ mạnh) không bị ràng buộc từ tên của nó.
  • Tham chiếu ngầm định đến self là ngoại lệ duy nhất làm cho liên kết tên ẩn.

Và có rất nhiều tối ưu hóa mà không vi phạm các quy tắc này, dù sao đó chỉ là chi tiết triển khai.

1

Ví dụ của bạn không có ý nghĩa @newacct. Biến x thực sự được sửa đổi bên ngoài khối. Swift đủ thông minh để tìm hiểu xem bạn có sửa đổi một biến bên trong hay bên ngoài phần đóng.

Khi tài liệu nói:

Là một tối ưu hóa, Swift có thể thay vì nắm bắt và lưu trữ một bản sao của một giá trị nếu giá trị mà không bị biến đổi bởi hoặc bên ngoài đóng cửa.

Đối với bài đăng câu hỏi của @Eonil, trong đoạn đầu tiên, bạn có tham chiếu mạnh đến Optional<AAA> khi đóng. Tuy nhiên khi bạn đặt a1 đến nil, những gì bạn thực sự làm là loại bỏ giá trị của loại AAA được gói bên trong tùy chọn.

Khi bạn gọi đóng a2, bạn trả về cùng một tùy chọn không có giá trị bên trong, chính xác là phương tiện nil. Vì vậy, giữ một tham chiếu mạnh mẽ có nghĩa là đóng cửa của bạn có cùng giá trị tùy chọn như a1. Nó không có nghĩa là không thể thiết lập tùy chọn này thành không.

Trong đoạn mã thứ hai, bạn đã chụp a1 trong danh sách chụp. Điều này có nghĩa là bạn sao chép a1 và giá trị a1 bên trong phần đóng không có gì liên quan đến giá trị a1 bên ngoài phần đóng. Vì vậy, ngay cả khi bạn đặt a1 đến nil, nó không ảnh hưởng đến những gì bạn nhận được từ việc đóng cửa.

Tiếng Anh của tôi không tốt và tôi hy vọng tôi đã thể hiện rõ ý kiến ​​của mình. Hoặc bạn có thể đọc this article, tôi tin rằng nó sẽ giúp bạn rất nhiều.

Các vấn đề liên quan