Skip to content

Commit 71056e1

Browse files
committed
Dev: fix memory leak in @swiftblock
1 parent 9f5f626 commit 71056e1

File tree

3 files changed

+80
-6
lines changed

3 files changed

+80
-6
lines changed

compiler/src/main/java/com/readdle/codegen/SwiftBlockDescriptor.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ File generateCode() throws IOException {
151151
sig));
152152

153153
swiftWriter.emitEmptyLine();
154-
swiftWriter.emitStatement(String.format("public lazy var block: %s = {", simpleTypeName));
154+
swiftWriter.emitStatement(String.format("public var block: %s {", simpleTypeName));
155+
swiftWriter.emitStatement("return {");
155156

156157
for (SwiftParamDescriptor param : params) {
157158
swiftWriter.emitStatement(String.format("let java_%s: JNIArgumentProtocol", param.name));
@@ -243,6 +244,7 @@ File generateCode() throws IOException {
243244
swiftWriter.emitStatement("}");
244245
}
245246

247+
swiftWriter.emitStatement("}");
246248
swiftWriter.emitStatement("}");
247249

248250
swiftWriter.emitEmptyLine();
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.readdle.swiftjava.sample;
2+
3+
import org.junit.Assert;
4+
5+
import java.lang.ref.WeakReference;
6+
7+
public class MemoryLeakVerifier {
8+
9+
private static final int MAX_GC_ITERATIONS = 50;
10+
private static final int GC_SLEEP_TIME = 100;
11+
12+
private final WeakReference reference;
13+
14+
public MemoryLeakVerifier(Object object) {
15+
this.reference = new WeakReference(object);
16+
}
17+
18+
public Object getObject() {
19+
return reference.get();
20+
}
21+
22+
/**
23+
* Attempts to perform a full garbage collection so that all weak references will be removed. Usually only
24+
* a single GC is required, but there have been situations where some unused memory is not cleared up on the
25+
* first pass. This method performs a full garbage collection and then validates that the weak reference
26+
* now has been cleared. If it hasn't then the thread will sleep for 50 milliseconds and then retry up to
27+
* 10 more times. If after this the object still has not been collected then the assertion will fail.
28+
* <p>
29+
* Based upon the method described in: http://www.javaworld.com/javaworld/javatips/jw-javatip130.html
30+
*/
31+
public void assertGarbageCollected(String name) {
32+
Runtime runtime = Runtime.getRuntime();
33+
for (int i = 0; i < MAX_GC_ITERATIONS; i++) {
34+
runtime.runFinalization();
35+
runtime.gc();
36+
if (getObject() == null)
37+
break;
38+
39+
// Pause for a while and then go back around the loop to try again...
40+
try {
41+
Thread.sleep(GC_SLEEP_TIME);
42+
} catch (InterruptedException e) {
43+
// Ignore any interrupts and just try again...
44+
}
45+
}
46+
Assert.assertNull(name + ": object should not exist after " + MAX_GC_ITERATIONS + " collections", getObject());
47+
}
48+
}

sample/src/androidTest/java/com/readdle/swiftjava/sample/SampleReferenceTest.java

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
package com.readdle.swiftjava.sample;
22

3+
import android.support.annotation.NonNull;
4+
import android.support.annotation.Nullable;
5+
import android.support.test.runner.AndroidJUnit4;
6+
import android.test.suitebuilder.annotation.LargeTest;
7+
38
import com.readdle.codegen.anotation.JavaSwift;
49
import com.readdle.codegen.anotation.SwiftError;
510
import com.readdle.swiftjava.sample.asbtracthierarhy.AbstractType;
@@ -10,11 +15,6 @@
1015
import org.junit.Test;
1116
import org.junit.runner.RunWith;
1217

13-
import android.support.annotation.NonNull;
14-
import android.support.annotation.Nullable;
15-
import android.support.test.runner.AndroidJUnit4;
16-
import android.test.suitebuilder.annotation.LargeTest;
17-
1818
import java.util.ArrayList;
1919
import java.util.List;
2020
import java.util.UUID;
@@ -163,6 +163,30 @@ public String call(@Nullable Exception error, @Nullable String string) {
163163
Assert.assertTrue(result.equals("123"));
164164
}
165165

166+
@Test
167+
public void testBlockMemoryLeak() {
168+
SampleReference.CompletionBlock block = new SampleReference.CompletionBlock() {
169+
@Nullable
170+
@Override
171+
public String call(@Nullable Exception error, @Nullable String string) {
172+
if (error == null) {
173+
return string;
174+
}
175+
else {
176+
return null;
177+
}
178+
}
179+
};
180+
181+
MemoryLeakVerifier memoryLeakVerifier = new MemoryLeakVerifier(block);
182+
183+
String result = sampleReference.funcWithBlock(block);
184+
Assert.assertNotNull(result);
185+
186+
block = null;
187+
memoryLeakVerifier.assertGarbageCollected("SampleReference.CompletionBlock");
188+
}
189+
166190
@Test
167191
public void testAbstractTypes() {
168192
AbstractType child = sampleReference.getAbstractType();

0 commit comments

Comments
 (0)