2016-03-26 14 views
7

Ở đây, tôi đã chơi với rò rỉ, vì vậy tôi đã thực hiện một chu kỳ tham khảo mạnh mẽ cố ý để xem các dụng cụ sẽ phát hiện một cái gì đó, và tôi có kết quả không mong muốn. Sự rò rỉ được hiển thị trong các dụng cụ chắc chắn có ý nghĩa, nhưng vụ tai nạn ngẫu nhiên là một chút bí ẩn (do hai sự kiện tôi sẽ đề cập sau).Sử dụng unowned bên trong một danh sách chụp gây ra một vụ tai nạn ngay cả khối chính nó không được thực hiện

Những gì tôi có ở đây là một lớp được gọi là SomeClass:

class SomeClass{ 

    //As you can guess, I will use this shady property to make a strong cycle :) 
    var closure:(()->())? 
    init(){} 
    func method(){} 
    deinit {print("SomeClass deinited")} 
} 

Ngoài ra tôi có hai cảnh, các GameScene:

class GameScene: SKScene { 

    override func didMoveToView(view: SKView) { 

     backgroundColor = .blackColor() 

     let someInstance = SomeClass() 

     let closure = {[unowned self] in 

      someInstance.method() //This causes the strong reference cycle... 
      self.method() 
     } 
     someInstance.closure = closure 
    } 

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { 

     if let nextScene = MenuScene(fileNamed: "MenuScene"){ 
      nextScene.scaleMode = .AspectFill 
      let transition = SKTransition.fadeWithDuration(1) 
      view?.presentScene(nextScene, transition: transition) 
     } 
    } 

    deinit {print("GameScene deinited")} 

    func method(){} 
} 

Và cuối cùng, MenuScene mà là giống hệt với GameScene, chỉ với phương thức rỗng didMoveToView (chỉ có phương thức touchesBegan được triển khai).

Tái Crash

Vụ tai nạn có thể sinh sản bằng chuyển giữa các cảnh trong một vài lần. Bằng cách đó, sự rò rỉ sẽ xảy ra vì someInstance được giữ lại bởi biến số closure và biến số closure được giữ lại bởi biến số someInstance, vì vậy chúng tôi có chu kỳ. Tuy nhiên, điều này sẽ không tạo ra sự cố (nó sẽ chỉ bị rò rỉ). Khi tôi thực sự cố gắng thêm self.method() bên trong đóng cửa, các tai nạn ứng dụng và tôi có được điều này:

error info

và điều này:

error info

Cùng tai nạn chính xác tôi có thể sản xuất nếu tôi cố gắng truy cập tham chiếu unowned khi đối tượng mà nó tham chiếu được deallocated, ví dụ: khi đóng cửa outlives trường hợp bị bắt. Điều đó có ý nghĩa, nhưng đó không phải là trường hợp ở đây (đóng cửa không bao giờ được thực hiện).

The Mysterious Phần

Phần bí ẩn là vụ tai nạn này xảy ra chỉ trên iOS 9.1không phải trên iOS9.3. Và một điều bí ẩn khác là thực tế là ứng dụng bị treo ngẫu nhiên, nhưng chủ yếu là trong mười lần chuyển tiếp đầu tiên. Ngoài ra, phần lạ là lý do tại sao nó bị treo nếu đóng cửa không bao giờ được thực hiện, hoặc trường hợp nó chụp không được truy cập (ít nhất là không phải bởi tôi).

Giải pháp cho các vấn đề nhưng không phải là trả lời cho câu hỏi

Tất nhiên sự sụp đổ có thể được giải quyết trong một vài cách bằng cách phá vỡ chu kỳ, và tôi biết rằng tôi nên sử dụng unowned chỉ khi tôi hoàn toàn chắc chắn rằng trường hợp đã capture sẽ không bao giờ trở thành 0 sau khi khởi tạo. Nhưng vẫn còn, vì thực tế là tôi đã không thực hiện đóng cửa này ở tất cả, sau khi nó outlived cảnh, vụ tai nạn này là khá khó xử với tôi. Ngoài ra, nó có thể là đáng nói đến là cảnh được deinited thành công sau mỗi quá trình chuyển đổi.

Thú vị

Nếu tôi sử dụng weak self bên trong một danh sách chụp, ứng dụng sẽ không sụp đổ (rò rỉ vẫn còn tồn tại tất nhiên). Điều đó có ý nghĩa, bởi vì nếu cảnh trở thành nil trước khi khối được deallocated, truy cập vào cảnh thông qua chuỗi tùy chọn sẽ ngăn ngừa sự cố. Nhưng phần thú vị là ngay cả khi tôi sử dụng forced unwrapping như thế này, nó sẽ không sụp đổ:

let closure = {[weak self] in 
     someInstance.method() 
     self!.method() 
} 

Mà làm cho tôi suy nghĩ ... Đánh giá cao bất kỳ gợi ý về cách để gỡ lỗi này hoặc giải thích về những gì gây ra tai nạn .. .

EDIT:

đây là Github repo

+0

Nếu nó không bị lỗi trên 9.3 thì đó có thể là lỗi. Đây có phải là báo cáo của bạn không? [SR-1006] (https://bugs.swift.org/browse/SR-1006) Có vẻ như cùng một vấn đề. – Sulthan

+0

@Sulthan Không, đó không phải là báo cáo lỗi của tôi ... Vâng, tôi bắt đầu một tiền thưởng để xem nếu ai đó thực sự có thể chứng minh rằng đây là một lỗi hay không, và giải thích những gì nên là hành vi mong đợi trong tình huống này. Cá nhân, tôi nghĩ rằng nó nên bị rò rỉ, nhưng không nên sụp đổ, nhưng chúng ta sẽ thấy ... – Whirlwind

+0

Tôi khá chắc chắn nó không nên sụp đổ nếu bạn không thực sự thực hiện các khối. – Sulthan

Trả lời

1

vụ tai nạn đang xảy ra vì đối tượng GameScene đã được phát hành BEF quặng hoạt ảnh kết thúc.

một cách để thực hiện việc này là chuyển đối tượng SomeClass trở lại trong phần đóng.

class SomeClass { 
    var closure: (SomeClass->Void)? 
} 

mà sẽ được sử dụng như thế này:

override func didMoveToView(view: SKView) { 
    let someInstance = SomeClass() 
    someInstance.closure = { [unowned self] someClass in 
     someClass.method() // no more retain cycle 
     self.method() 
    } 
} 

CẬP NHẬT

Hóa ra đây là một sự kết hợp của một sắc thái trong sự tiện lợi init?(fileNamed:) + lạm dụng [unowned self] gây tai nạn của bạn.

mặc dù tài liệu chính thức dường như không nêu rõ, như được giải thích ngắn gọn trong blog post trình khởi tạo tiện lợi này sẽ thực sự sử dụng lại cùng một đối tượng.

Tập tin Tài liệu tham khảo

Biên tập viên cảnh cho phép bạn tham khảo nội dung giữa .sks khác nhau (cảnh) các tập tin, có nghĩa là bạn có thể đặt cùng một loạt các sprites trong một tập tin cảnh duy nhất và sau đó tham khảo các tập tin từ một tệp cảnh.

Bạn có thể tự hỏi tại sao bạn sẽ cần nhiều hơn một cảnh, và có một vài lý do:

1) Bạn có thể tái sử dụng các bộ sưu tập cùng sprites trong nhiều cảnh khác nhau, có nghĩa là bạn không cần phải tái tạo họ hơn và hơn nữa.

2) Nếu bạn cần thay đổi nội dung được tham chiếu trong tất cả các cảnh, tất cả những gì bạn phải làm là chỉnh sửa cảnh gốc và nội dung tự động cập nhật trong mọi cảnh tham chiếu đến cảnh đó. Thông minh, phải không?

thêm khai thác gỗ xung quanh việc tạo ra, thiết lập của việc đóng cửa và deinit dẫn đến một số lượng thú vị:

GameScene init: 0x00007fe51ed023d0 
GameScene setting closure: 0x00007fe51ed023d0 
MenuScene deinited 
GameScene deinited: 0x00007fe51ed023d0 
GameScene init: 0x00007fe51ed023d0 
GameScene setting closure: 0x00007fe51ed023d0 

thông báo như thế nào địa chỉ bộ nhớ tương tự được sử dụng hai lần.tôi muốn giả sử dưới mui xe táo đang làm một số quản lý bộ nhớ thú vị cho tối ưu hóa đó là khả năng dẫn đến việc đóng cửa cũ vẫn tồn tại sau khi deinit.

đào sâu hơn có thể được thực hiện vào bên trong của SpriteKit, nhưng bây giờ tôi chỉ thay thế [unowned self] với [weak self].

+0

Vì vậy, bạn nghĩ rằng sự cố là hợp lý và hành vi đó được thấy trên iOS9.1 hoạt động chính xác, nhưng kết quả từ iOS9.3 là lỗi (vì cùng một mã không tạo ra sự cố)? – Whirlwind

+0

Ngoài ra, bạn có thể vui lòng cụ thể hơn và chi tiết hơn về ví dụ * GameScene được phát hành trước khi hoạt ảnh kết thúc * câu lệnh không? Bạn có nghĩa là phát hành trước khi 'SKTransition' thực sự kết thúc hay không? Tôi quên đề cập rằng điều này đang xảy ra chỉ khi chuyển từ 'MenuScene' ->' GameScene', chứ không phải ngược lại. – Whirlwind

+0

xem câu trả lời cập nhật – Casey

0

Từ những gì tôi có thể thấy nếu có vẻ như chu trình giữ lại sẽ được gây ra bởi vì bạn đang bao gồm một đối tượng đóng cửa riêng của nó và lưu nó vào chính nó. Xem nếu các công việc sau:

class GameScene: SKScene { 

    let someInstance = SomeClass() 

    override func didMoveToView(view: SKView) { 

     backgroundColor = .blackColor() 

     let closure = {[weak self, weak someInstance] in 

      someInstance?.method() //This causes the strong reference cycle... 
      self?.method() 
     } 
     someInstance.closure = closure 
    } 

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { 

     if let nextScene = MenuScene(fileNamed: "MenuScene"){ 
      nextScene.scaleMode = .AspectFill 
      let transition = SKTransition.fadeWithDuration(1) 
      view?.presentScene(nextScene, transition: transition) 
     } 
    } 

    deinit {print("GameScene deinited")} 

    func method(){} 
} 

tôi chuyển someInstance để một tài sản của lớp vì tôi khá chắc chắn với một tham chiếu yếu trong khối và không đi qua someInstance đâu đó ngoài chức năng, someInstance sẽ deinit ở phần cuối của chức năng đó. Nếu đó là những gì bạn muốn sau đó giữ nó someInstance bên trong chức năng. Bạn cũng có thể sử dụng unowned nếu bạn muốn nhưng như bạn biết tôi đoán tôi chỉ là một fan hâm mộ lớn hơn của việc sử dụng lol yếu. Hãy cho tôi biết nếu điều đó khắc phục sự cố và rò rỉ

+0

Được rồi, cảm ơn vì sự đáp ứng và nỗ lực ... Sẽ xem xét ngay sau khi tôi tìm thấy thời gian. – Whirlwind

+0

Không sao cả. Hãy cho tôi biết vì từ những gì tôi thấy trong câu trả lời ở trên với cùng một địa chỉ bộ nhớ đang được sử dụng, tôi nghĩ rằng việc giữ lại bộ nhớ bị rò rỉ mạnh có thể là nguyên nhân của vấn đề –

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