2011-09-06 19 views
8

Tôi có một hàm có chức năng khác làm tham số. Một cái gì đó như thế này:AS3 truyền hàm như một tham số tạo ra rò rỉ bộ nhớ

public function onHits(target : Shape, callback : Function) : void 

Tôi sử dụng nó bằng cách chuyển một hàm thành viên như một tham số cần được gọi bất cứ khi nào mục tiêu được truyền đạt đến thứ gì đó. Hàm này được gọi nhiều lần một khung. Vì vậy, nó được sử dụng bằng cách thực hiện:

//code... 
CollisionManager.onHits(myShape, onHitCB); 
//code... 

Các chức năng trên hit:

public function onHitCB(hitObject : *) : void 
{ 
    //removed all code to test this problem 
} 

Khi tôi làm điều này, tôi có một rò rỉ bộ nhớ. Tôi đã phân lập vấn đề với phương thức onHits đó và đã nhận xét mọi thứ khác. onHits là một phương thức rỗng không có mã bên trong nó, onHitCB cũng rỗng. Nếu tôi nhận xét các cuộc gọi đến onHits, không có rò rỉ bộ nhớ và nếu tôi vượt qua null thay vì onHitCB không có rò rỉ bộ nhớ.

Vì vậy, rõ ràng khi tôi chuyển onHitCB thành thông số. Vì vậy, tôi nghĩ rằng nó có thể là do Flash phân bổ một số bộ nhớ để tạo ra các con trỏ hàm và không phát hành nó nhưng tôi gọi System.gc() mỗi khung trong chế độ gỡ lỗi và rò rỉ vẫn còn đó. Điều này có nghĩa là đây là lỗi trong SDK hoặc tôi không làm điều gì đúng.

Tôi đã tìm thấy một workaround lạ bằng cách giữ một biến trỏ đến các chức năng mà tôi gán trong constructor của đối tượng của tôi:

private var func : Function; 

public function MyObject() 
{ 
    func = onHitCB; 
} 

và điều này sẽ xóa bộ nhớ bị rò rỉ ngay cả khi tôi vẫn vượt qua onHitCB làm tham số. Vì vậy, điều đó có nghĩa rằng nó không phải là "getter" chức năng để có được onHitCB nhưng cái gì khác gây ra rò rỉ bộ nhớ?

Tôi rất bối rối. Làm thế nào điều này có thể gây ra rò rỉ bộ nhớ:

public function MyObject() 
{ 
} 

public function update() : void 
{ 
    CollisionManager.onHits(myShape, onHitCB);//empty function 
} 

public function onHitCB(hitObject : *) : void 
{ 
    //removed all code to test this problem 
} 

nhưng không phải điều này? :

private var func : Function; 
public function MyObject() 
{ 
    func = onHitCB; 
} 

public function update() : void 
{ 
    CollisionManager.onHits(myShape, onHitCB);//empty function 
} 

public function onHitCB(hitObject : *) : void 
{ 
    //removed all code to test this problem 
} 

và có cách nào để không phải thực hiện giải pháp này không?

+0

Tại sao không đặt onHitCB thành viên của lớp thành CollisionManager? Âm thanh như chức năng của bạn là mất phạm vi. Trên dòng cuối cùng của onHits, hãy thử gọi lại = null; –

+0

Đã cố gắng thiết lập gọi lại thành vô giá trị ở cuối onHits nhưng vẫn có lỗ rò. Cho đến nay, việc giữ một tham chiếu cục bộ đến hàm là giải pháp duy nhất tôi có thể tìm thấy. – Godfather

Trả lời

5

[...] phương pháp ràng buộc sẽ được tự động tạo ra khi bạn vượt qua một phương pháp như một tham số. Các phương thức ràng buộc đảm bảo rằng từ khóa này luôn luôn tham chiếu đến đối tượng hoặc lớp trong đó một phương thức được định nghĩa. Source

Điều đó giống như việc tạo tham chiếu đến phương thức không sử dụng trình khởi động đơn giản. Đối tượng đóng đối tượng phương thức mới là được tạo. Giả sử của bạn là đúng.

Tôi tự hỏi tại sao các tham chiếu không được lưu vào bộ nhớ cache cho từng trường hợp và lý do tại sao chúng không được thu thập rác. Tốt hơn là tránh tạo nhiều tham chiếu.Tham khảo một phương pháp chỉ một lần là chính xác những gì tôi sẽ làm gì khi tôi sẽ phải sử dụng phương pháp đó ở nhiều nơi, vì vậy hầu hết thời gian tôi sẽ không gọi nó là một workaround nhưng một thực hành DRY tốt. Trong ví dụ của bạn nó có ý nghĩa tất nhiên, giả định một tham chiếu phương pháp sẽ được sử dụng một getter đơn giản.

+0

Tôi thấy, điều này xác nhận sự nghi ngờ của tôi về một đối tượng được tạo ra mỗi khi hàm được truyền. Tuy nhiên tôi vẫn không hiểu lý do tại sao giữ một tham chiếu địa phương trong khi vẫn đi qua các chức năng (và không phải là tài liệu tham khảo địa phương) sẽ không còn tạo ra một rò rỉ bộ nhớ. – Godfather

+0

Tôi nghĩ rằng nó chỉ được tạo khi tên * phương thức * được sử dụng trực tiếp (khi gán hoặc khi được sử dụng làm tham số). Vì vậy, 'func = onHitCB;' sẽ chỉ tạo phương thức đóng một lần, sử dụng tham số 'func' sẽ không làm cho nó được tạo lại. – Kapep

+0

có nhưng tôi không sử dụng func làm tham số, tôi vẫn đang sử dụng onHitCB (nếu bạn xem kỹ 2 ví dụ trên, cả hai đều gọi onHits bằng onHitCB) đó là điều bí ẩn lớn nhất đối với tôi. Thám hiểm tốt nhất của tôi ngay bây giờ sẽ là việc đóng cửa phương pháp được lưu trữ như là một tài liệu tham khảo yếu mà thông thường sẽ được làm sạch bởi các nhà sưu tập rác nhưng không vì một lý do không rõ. Vì nó là một tham chiếu yếu, nó sẽ cần phải được tái tạo ở mọi cuộc gọi nhưng vì tôi giữ một tham chiếu cục bộ, nó vẫn sống đủ lâu để được tái sử dụng trong các cuộc gọi sau này. – Godfather

0

Tôi không chắc mã của bạn là gì trong hàm onHits, nhưng nếu không yêu cầu thêm thời gian để hoàn thành trong một chu kỳ khác. sau đó tôi khuyên bạn làm như thế này:

static public function onHits(target : Shape) : * 
{ 
    // do what you need 

    // return the hitObject; 
    return hitObject; 
} 

public function update() : void 
{ 
    // parse the object direc to to the function. 
    onHitCB (CollisionManager.onHits(myShape)); 
} 

public function onHitCB(hitObject : *) : void 
{ 
    if (hitObject == null) 
     return; 

    // if it not null then do all your calculations. 
    //removed all code to test this problem 
} 
+0

Hiện tại, hàm onHits không làm gì cả. Tôi đã loại bỏ tất cả các mã từ nó nhưng chỉ đơn giản gọi nó bằng cách đi qua một con trỏ Hàm sẽ tạo ra một rò rỉ bộ nhớ. Tôi có thể tránh được vấn đề bằng cách thay đổi thiết kế mã của tôi nhưng chỉ cần chuyển một con trỏ Hàm không gây ra vấn đề này (>. <) – Godfather

1

Để biết thêm thông tin về chính xác những gì và không gây rò rỉ bộ nhớ khi bạn sử dụng các kỹ thuật chức năng, hãy xem http://www.developria.com/2010/12/functional-actionscript-part-1.html. Ngoài ra, hãy lưu ý rằng việc sử dụng các phương pháp tĩnh như thế này thực sự là thực hành không tốt (http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/) và bạn mới bắt đầu gặp phải nhiều vấn đề được gây ra bằng cách sử dụng kỹ thuật này. Có vẻ như bạn đã đủ sớm trong dự án của mình mà bạn không hoàn toàn cam kết với con đường này, vì vậy bạn có thể muốn xem các cách khác để lập trình điều này.

-1

Và đây là lý do tại sao chúng tôi không thực hiện loại điều này trong lập trình kiểu OOP.
Đặt cược tốt nhất của bạn là thực hiện đúng và thêm gọi lại vào lớp CollisionManager.
Lý do nó có thể được GC khi bạn giữ một tham chiếu địa phương là bởi vì hàm không bao giờ mất phạm vi vì var đó là có giữ tham chiếu.
Sau khi một cái gì đó mất phạm vi nó trở nên gần như không thể GC nó.

Hãy thử cách này và xem cách bạn mất phạm vi.

private var somevar:String = 'somevar with a string'; 
public function MyObject() 
{ 
} 

public function update() : void 
{ 
    CollisionManager.onHits(myShape, onHitCB);//empty function 
} 

public function onHitCB(hitObject : *) : void 
{ 
    trace(this.somevar) // scope should be lost at this point and somevar should be null or toss an error. 
} 
+0

Điều này có nghĩa vụ phải hoạt động không? Tôi không chắc chắn những gì bạn có nghĩa là bằng cách mất phạm vi (Chức năng con trỏ giữ một tham chiếu đến dụ họ là một thành viên của để họ có thể được gọi với quyền đối số này). Việc truyền một hàm bằng tham số hoạt động tốt và dấu vết "somevar with a string" giống như nó nên (tôi nghĩ?). Tôi không thấy lý do tại sao chuyển một hàm gọi lại mà phải được gọi khi một va chạm được phát hiện có thể là "thiết kế xấu", mỗi đối tượng có thể và có lẽ sẽ xử lý các xung đột khác nhau. Điều này được sử dụng trong nhiều trường hợp (box2D làm điều này cho nó là động cơ va chạm tôi tin) vì nó linh hoạt hơn. – Godfather

+0

Ví dụ không đúng. Nếu tôi có thời gian, tôi sẽ đăng một bản chỉnh sửa để bạn giải thích rõ hơn những gì tôi đang nói. –