2015-01-21 16 views
10

Khi ghi đè trình quan sát didSet của thuộc tính dẫn đến đệ quy, tại sao?Swift: Ghi đè kết quả didSet trong đệ quy

class TwiceInt { 
    var value:Int = 0 { 
     didSet { 
      value *= 2 
     } 
    } 
} 

class QuadInt : TwiceInt { 
    override var value:Int { 
     didSet { 
      value *= 4 
     } 
    } 
} 

let t = TwiceInt() 
t.value = 5 // this works fine 


let q = QuadInt() 
q.value = 5 // this ends up in recursion 

Nếu tôi cập nhật QuadInt với

class QuadInt : TwiceInt { 
    override var value:Int { 
     didSet { 
      super.value *= 4 
     } 
    } 
} 

q.value = 5 // q.value = 80 

Vì vậy, tôi đoán cuộc gọi đến được một cái gì đó như:

value = 5 
QuadInt:didSet (value *= 4) 
value = 20 
TwiceInt:didSet (value *= 2) 
value = 40 
TwiceInt:didSet (value *= 2) 
value = 80 

này là nhiều hơn hoặc ít hơn như chụp trong bóng tối. Có bất kỳ tài liệu nào về những gì xảy ra khi một tài sản cập nhật không?

Trả lời

1

Vấn đề là bạn không nên sử dụng didSet để thay đổi giá trị vì nó sẽ gây ra đệ quy, thay vào đó bạn nên sử dụng thiết lập:

var TwiceInt : Int { 
    get { 
     return TwiceInt 
    } 
    set (newValue) { 
     TwiceInt *= 2 
    } 
} 
+0

Nhưng nếu đó là trường hợp, thì tại sao '' 'let t = TwiceInt() t.value = 5''' hoạt động tốt? – chunkyguy

+0

Theo tài liệu Swift của Apple, việc thay đổi giá trị trong 'didSet' không gây ra đệ quy (đó là nguyên văn trong tài liệu). – Mecki

3

Đưa một println() trong cả hai khối didSet, bạn có thể nhìn thấy rằng nó liên tục gọi siêu thực hiện trước, sau đó ghi đè, sau đó siêu, sau đó ghi đè ... cho đến khi nó phát nổ.

Tôi chỉ có thể hình ảnh rằng đây là lỗi trong Swift. Tôi nhận được cùng một vấn đề trong Swift 1.2 (đi kèm với bản Xcode 6.3 beta).


Chắc chắn nó sẽ hoạt động, ít nhất là khi tôi đọc nó. Từ https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html#//apple_ref/doc/uid/TP40014097-CH14-ID254:

LƯU Ý

Nếu bạn gán giá trị cho một tài sản trong phạm vi quan sát didSet riêng của mình, giá trị mới mà bạn gán sẽ thay thế một trong đó đã chỉ cần thiết lập.

và sau khi mẫu của họ AudioChannel (trích dẫn dưới note):

LƯU Ý

Trong lần đầu tiên của hai kiểm tra này, người quan sát didSet đặt currentLevel đến một giá trị khác nhau. Tuy nhiên, điều này không làm cho người quan sát được gọi lại.

struct AudioChannel { 
    static let thresholdLevel = 10 
    static var maxInputLevelForAllChannels = 0 
    var currentLevel: Int = 0 { 
     didSet { 
      if currentLevel > AudioChannel.thresholdLevel { 
       // cap the new audio level to the threshold level 
       currentLevel = AudioChannel.thresholdLevel 
      } 
      if currentLevel > AudioChannel.maxInputLevelForAllChannels { 
       // store this as the new overall maximum input level 
       AudioChannel.maxInputLevelForAllChannels = currentLevel 
      } 
     } 
    } 
} 
1

nó xuất hiện đối với một số lý do, mặc dù ghi đè lên nó vẫn đang kêu gọi các lớp cha didSet.

Trong ví dụ đầu tiên bạn kết thúc trong đệ quy vì thiết lập quad đặt ra khỏi superclass didSet mà lần lượt đặt ra quads đã thiết lập vv vv.

Trong ví dụ thứ hai, giá trị làm cho cả hai doSets xảy ra một lần, sau đó quad didSet cũng đặt siêu didSet vào lần trước.

quad.value = 5

value = * 2 (lớp cha didSet) * 4 (lớp con didSet) * 2 (lớp cha didSet) = 80

12

Bạn không có thể ghi đè didSet, nó không phải là một phương pháp thông thường. Trên thực tế, bạn đã không ghi đè lên didSet, bạn sẽ tự bỏ qua thuộc tính.

didSet hoạt động như người quan sát hoạt động và chỉ vì bạn đặt người quan sát của riêng bạn trên thuộc tính được thừa kế không có nghĩa là bất kỳ người quan sát nào khác sẽ tự động bị hủy đăng ký. Vì vậy, người quan sát của lớp cha của bạn hoàn toàn không bị ảnh hưởng bởi điều này und do đó cả hai phương pháp didSet sẽ được gọi là cuối cùng.

Bây giờ nếu bạn thay đổi giá trị trong máy quan sát didSet của riêng mình, điều này sẽ không gây ra đệ quy khi thời gian chạy Swift đủ thông minh để hiểu rằng việc thực hiện thay đổi thuộc tính quan sát của chính nó sẽ không được gọi lại sau khi thực hiện vì thế. Thời gian chạy biết phương thức didSet hiện đang thực thi và sẽ không thực hiện lại phương thức đó nếu biến thay đổi trước khi phương thức này đã trả về. Kiểm tra này dường như không hoạt động trên các lớp siêu lớp.

Vì vậy, các *= 4 làm cho người quan sát lớp siêu được gọi, mà đặt *= 2 và gây ra quan sát lớp con được gọi là một lần nữa, mà một lần nữa sẽ thiết lập *= 4 gây lớp quan sát siêu được gọi là một lần nữa ... và vân vân .

Bằng cách sử dụng rõ ràng super, bạn phá vỡ chu kỳ đó, vì bây giờ bạn không đặt thuộc tính bị ghi đè của bạn, nhưng tài sản siêu thừa kế và bạn không thực sự quan sát siêu tài sản đó, bạn chỉ đang quan sát một thuộc tính được ghi đè của riêng bạn.

Bạn có thể gặp sự cố tương tự với phương pháp ghi đè trong một số ngôn ngữ, trong đó giải pháp thông thường cũng sử dụng rõ ràng super tại một trong các cuộc gọi.

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