2014-12-06 29 views
9

Cụ thể, cách quản lý bộ nhớ Swift hoạt động với các tùy chọn bằng cách sử dụng mẫu đại biểu?Quản lý bộ nhớ Swift hoạt động như thế nào?

Đã quen với việc viết mẫu đại biểu trong Mục tiêu-C, bản năng của tôi là làm cho đại biểu weak. Ví dụ: trong Mục tiêu-C:

@property (weak) id<FooDelegate> delegate; 

Tuy nhiên, thực hiện điều này trong Swift không quá thẳng thắn.

Nếu chúng ta có chỉ là một giao thức trai bình thường:

protocol FooDelegate { 
    func doStuff() 
} 

Chúng ta không thể khai báo các biến thuộc loại này như yếu:

weak var delegate: FooDelegate? 

Tạo các lỗi:

'weak' cannot be applied to non-class type 'FooDelegate'

Vì vậy, chúng tôi hoặc không sử dụng từ khóa weak, cho phép chúng tôi sử dụng structsenums như các đại biểu, hoặc chúng ta thay đổi giao thức của chúng tôi như sau:

protocol FooDelegate: class { 
    func doStuff() 
} 

nào cho phép chúng ta sử dụng weak, nhưng không cho phép chúng ta sử dụng structs hoặc enums.

Nếu tôi không thực hiện giao thức của mình một giao thức lớp, và do đó không sử dụng weak cho biến của tôi, tôi đang tạo chu trình giữ lại, đúng không?

Có lý do nào có thể tưởng tượng được tại sao bất kỳ giao thức nào được sử dụng làm giao thức đại biểu không phải là giao thức lớp sao cho các biến thuộc loại này có thể là weak?

tôi chủ yếu hỏi, bởi vì trong phần phái đoàn của the Apple official documentation on Swift protocols, họ cung cấp một ví dụ về một giao thức phi hạng nhất và một biến phi yếu được sử dụng như các đại biểu đến lớp học của họ:

protocol DiceGameDelegate { 
    func gameDidStart(game: DiceGame) 
    func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) 
    func gameDidEnd(game: DiceGame) 
} 
class SnakesAndLadders: DiceGame { 
    let finalSquare = 25 
    let dice = Dice(sides: 6, generator: LinearCongruentialGenerator()) 
    var square = 0 
    var board: [Int] 
    init() { 
     board = [Int](count: finalSquare + 1, repeatedValue: 0) 
     board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02 
     board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08 
    } 
    var delegate: DiceGameDelegate? 
    func play() { 
     square = 0 
     delegate?.gameDidStart(self) 
     gameLoop: while square != finalSquare { 
      let diceRoll = dice.roll() 
      delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll) 
      switch square + diceRoll { 
      case finalSquare: 
       break gameLoop 
      case let newSquare where newSquare > finalSquare: 
       continue gameLoop 
      default: 
       square += diceRoll 
       square += board[square] 
      } 
     } 
     delegate?.gameDidEnd(self) 
    } 
} 

Chúng ta có nên coi đây là gợi ý cho rằng Apple cho rằng chúng ta nên sử dụng cấu trúc với tư cách là đại biểu không? Hay đây chỉ là một ví dụ xấu, và thực tế, các giao thức ủy nhiệm nên được khai báo như là các giao thức chỉ lớp để đối tượng được ủy quyền có thể chứa một tham chiếu yếu tới đại biểu của nó?

+0

"Nếu tôi không làm giao thức của mình thành giao thức lớp và do đó không sử dụng yếu cho biến của tôi, tôi đang tạo chu trình giữ lại, đúng không?" Không, không đúng. Không, trừ khi các đại biểu giữ lại một tham chiếu đến máy chủ của nó. Trong ví dụ Swift, điều đó không xảy ra. DiceGameDelegate có các tham số DiceGame trong các hàm của nó, nhưng nó không có DiceGame _property_ mà có thể gây ra một tham chiếu liên tục đến máy chủ của nó. – matt

+0

Nhưng trong hầu hết các trường hợp sử dụng thực tế, đại biểu có một tham chiếu đến đối tượng được ủy quyền. – nhgrif

+0

Đúng, và đó chính xác là câu trả lời của tôi. - Bạn hỏi, về cơ bản, những gì đặc biệt về ví dụ này của Apple mà không _not_ sử dụng một lớp và một đại biểu yếu, và tôi đã nói với bạn. – matt

Trả lời

7

Should we take this as a hint that Apple thinks we should be using structs as delegates? Or is this simply a bad example, and realistically, delegate protocols should be declared as class-only protocols so that the delegated object can hold a weak reference to its delegate?

Đây là điều. Trong lập trình Cocoa thực tế, đại biểu có thể là một lớp học hiện có. Nó là một lớp bởi vì nó tồn tại cho một số mục đích khác mà chỉ có một lớp có thể đáp ứng - bởi vì Ca cao yêu cầu nó.

Ví dụ, rất thường xuyên, để đưa iOS làm ví dụ, một bộ điều khiển chế độ xem cần hoạt động như một đại biểu của bộ điều khiển chế độ xem khác nhằm mục đích sắp xếp một thông điệp qua lại giữa chúng. Quyền sở hữu của bộ điều khiển chế độ xem được quyết định bởi hệ thống phân cấp bộ điều khiển chế độ xem và không có gì khác. Vì vậy, trong Swift, cũng như trong Objective-C, bạn có tốt hơn làm cho thuộc tính delegateweak, bởi vì nó sẽ là khủng khiếp nếu một bộ điều khiển xem đột nhiên mất quyền sở hữu quản lý bộ nhớ của một bộ điều khiển chế độ xem khác!

Vì vậy, trong thế giới thực của khuôn khổ Ca cao, có nguy cơ nghiêm trọng về quyền sở hữu không đúng hoặc chu kỳ lưu giữ. Và đó là vấn đề giải quyết weak. Nhưng nó chỉ hoạt động, như bạn nói đúng, với các lớp học.

Ví dụ trong sách, tuy nhiên, là về một số đối tượng sống trong một thế giới giả tạo Swift đơn giản trừu tượng. Trong thế giới đó, miễn là bạn không gặp nguy hiểm về tính tròn (chu kỳ lưu giữ), không có lý do gì để không sử dụng cấu trúc và không có lý do gì để lo lắng về quản lý bộ nhớ. Nhưng thế giới đó không phải là thế giới mà bạn thường sẽ lập trình! Và thế giới đó không phải là khuôn khổ của thế giới ca cao mà mô hình đại biểu mục tiêu C của bạn xuất phát từ và thuộc về.

+0

Câu hỏi này không được gắn thẻ với iOS và mặc dù tôi đã thực hiện rất ít Lập trình OS X, tôi khá chắc chắn rằng tôi đã thấy một lớp con 'NSArray' được sử dụng như một' NSTableDataSource'. Nó không làm cho ít nhất một số ý nghĩa rằng tôi có thể muốn sử dụng một mảng (đó là một cấu trúc trong Swift) như là một đại biểu cấp dữ liệu-loại? – nhgrif

+2

Mảng Swift là một cấu trúc nhưng nó được nối với NSArray (lớp). -Hoặc, trong câu trả lời cho câu hỏi của bạn, không: trong kịch bản nào có thể một mảng sẽ là nguồn dữ liệu? Nguồn dữ liệu là những thứ có khả năng trả lời các câu hỏi về giao thức nguồn dữ liệu. Bạn sẽ không bao giờ có một mảng làm điều đó. – matt

+0

@nhgrif: Sử dụng 'NSArray' * như * một nguồn dữ liệu bảng (trong OS X hoặc iOS) là một ý tưởng thực sự tồi. Điều đó có nghĩa là mở rộng lớp 'NSArray' để thực hiện các phương thức' Table'D '' '' '' '' '' '' '', có nghĩa là * mọi mảng trong ứng dụng của bạn * có thể hoạt động như một nguồn dữ liệu có thể cho xem bảng của bạn. Bạn có thể đã thấy * các lớp * thực hiện các phương thức nguồn dữ liệu của bảng xem * bằng cách sử dụng * một mảng. Lập luận của Matt giữ, ít nhất là đối với Cocoa: những nơi mà Cocoa (Touch) muốn một đại biểu là những nơi yêu cầu một loại lớp, và có lẽ sẽ tiếp tục. – rickster

2

Có, ví dụ này hơi kỳ quặc.

Vì ví dụ sử dụng loại giao thức không thuộc lớp, nên phải mong đợi một cấu trúc có thể triển khai giao thức, có nghĩa là phiên bản DiceGame sở hữu đại biểu của nó. Và quả thực, điều đó vi phạm các giả định điển hình về mô hình đại biểu.

Nó không dẫn đến một chu kỳ tài liệu tham khảo trong trường hợp nàyDiceGameTracker là một đối tượng hư cấu mà không sở hữu DiceGame bản thân - nhưng trong một thế giới thực ứng dụng đó là có thể, thậm chí có khả năng, mà một đại biểu cũng có thể là chủ sở hữu của đối tượng ủy nhiệm. (Ví dụ: bộ điều khiển chế độ xem có thể sở hữu số DiceGame và triển khai DiceGameDelegate để nó có thể cập nhật giao diện người dùng của nó theo các sự kiện của trò chơi.)

Loại tham chiếu đó có thể biến thành một mớ hỗn độn khó hiểu nếu trò chơi, đại biểu hoặc loại thực hiện một hoặc cả hai giao thức này là một loại giá trị - vì các loại giá trị được sao chép, một số biến trong thiết lập của bạn sẽ là bản sao riêng biệt của trò chơi (hoặc chủ sở hữu trò chơi).

Thực tế người ta sẽ sử dụng loại tham chiếu (lớp) để thực hiện điều này, ngay cả khi tuyên bố giao thức để mở khả năng làm điều đó. Ngay cả trong thế giới giả tưởng chỉ có Swift, nó có thể có ý nghĩa để làm theo cách đó ... thường bất cứ khi nào bạn có một cái gì đó với cuộc sống lâu dài, trạng thái có thể thay đổi nội bộ, và một mô hình sử dụng có khả năng được truy cập bởi nhiều diễn viên khác, bạn muốn một loại lớp, ngay cả khi bạn có thể phân loại giả khác với các loại giá trị và var.

2

Nếu bạn phải có structsemums trong giao thức của mình, thì nếu bạn đặt đại biểu nil ngay trước khi đóng bộ điều khiển chế độ xem thì thao tác này sẽ phá vỡ chu kỳ lưu giữ. Tôi đã xác minh điều này trong Allocations in Instruments.

// view controller about to close 
objectCreatedByMe.delegate = nil 

Đây là tùy chọn, vì vậy điều này hợp lệ.

+0

Rõ ràng là điều này sẽ phá vỡ chu trình. Nhưng có rất nhiều lý do tại sao điều này là không thực sự đủ tốt ... – nhgrif

+0

Tôi đã chỉ cố gắng tìm một giải pháp cho vấn đề. Tôi đã xác nhận điều này. Không chắc tại sao tôi lại bỏ phiếu cho điều đó. Bạn có thể mở rộng được không. – skymook

+1

Tôi đã bỏ phiếu cho bạn. Bạn đã cố gắng trả lời, bạn đã không đặt bất cứ điều gì sai mà tôi có thể nói. Và nếu nó sai, trong một hoặc hai tuần, Apple sẽ thay đổi ngôn ngữ và có thể bạn sẽ đúng. Swift đang di chuyển với tốc độ nhanh. Tôi không nhận được quá trong vòng tay về bất kỳ của nó cho đến khi nó cao nguyên ra một số. Heck họ vừa thêm 'Set' vào tuần trước. – Darrell

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