| # Ref count testing |
| |
| How to write a reliable ref counting test: |
| |
| - Rather than trying to read the object ref count, use `ReferenceTracker`. |
| Alternatively, arc_test and ref_count_test use a `int32_t* counter` pattern, |
| but the idea is the same. Rather than checking the actual ref counts, these |
| patterns just watch for when the destructor is called. |
| - Move creation of any ObjC objects, blocks, or lambda functions into a separate |
| function, to avoid accidental capturing of variables inside closures, or other |
| unexpected variable lifetimes. |
| - Mark such functions with `@pragma('vm:never-inline')`. |
| - An alternative to serparate functions, to force drop Dart references to an |
| object, is to make the object variable nullable, and then set it to null. |
| - Use `expect(object, isNotNull)` to ensure that `object` stays alive until |
| then. |
| - Use `doGC()` to force run GC. If you're on a platform that doesn't support |
| `doGC()` (ie `canDoGC` is false), use `flutterDoGC()` (though it's slower, |
| not 100% reliable, and breaks autorelease pools, so should be avoided). |
| - If you're working with blocks, you'll likely need to |
| `await Future<void>.delayed(Duration.zero)` to allow the disposal message to |
| be sent back to the owner isolate so that the registered closure is cleaned |
| up. After that you'll need to call `doGC()` again to make sure the closure is |
| destroyed. |
| - You can use `blockHasRegisteredClosure` to test that the closure is destroyed. |
| - Got nested blocks? Repeat that process until everything is destroyed. |
| - Objects still living too long, despite all that? ObjC might be doing an |
| autorelease somewhere. If your test doesn't have an explicit autorelease pool, |
| it will be added to a pool that outlives the test (likely thread scoped). To |
| fix this, wrap your test in `objc_autoreleasePoolPush/Pop`. Make sure not to |
| do any async/await stuff in between the push and pop. |