Skip to content

Commit 0aa602d

Browse files
authored
autoreleasepool support (#50)
Implements `Autorelease(func())` to run the body with an active autoreleasepool. Existing calls to `NSAutoreleasePool` have been replaced and wrapped with this block helper. Fixes #12
1 parent 05f9475 commit 0aa602d

File tree

6 files changed

+83
-49
lines changed

6 files changed

+83
-49
lines changed

core/NSAutoreleasePool.go

Lines changed: 0 additions & 13 deletions
This file was deleted.

objc/autoreleasepool.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package objc
2+
3+
/*
4+
#cgo LDFLAGS: -lobjc -framework Foundation
5+
extern void objc_autoreleasePoolPop(void* pool);
6+
extern void* objc_autoreleasePoolPush(void);
7+
*/
8+
import "C"
9+
import "runtime"
10+
11+
func Autorelease(body func()) {
12+
// ObjC autoreleasepools are thread-local, so we need the body to be locked to
13+
// the same OS thread.
14+
runtime.LockOSThread()
15+
defer runtime.UnlockOSThread()
16+
pool := C.objc_autoreleasePoolPush()
17+
defer C.objc_autoreleasePoolPop(pool)
18+
body()
19+
}

objc/autoreleasepool_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package objc
2+
3+
import (
4+
"testing"
5+
)
6+
7+
type AutoreleaseDealloc struct {
8+
Object `objc:"AutoreleaseDealloc : NSObject"`
9+
}
10+
11+
func TestAutorelease(t *testing.T) {
12+
c := NewClassFromStruct(AutoreleaseDealloc{})
13+
deallocCount := 0
14+
c.AddMethod("dealloc", func(o Object) {
15+
deallocCount++
16+
o.SendSuper("dealloc")
17+
})
18+
RegisterClass(c)
19+
20+
Autorelease(func() {
21+
dc := GetClass("AutoreleaseDealloc").Alloc().Init()
22+
dc.Autorelease()
23+
})
24+
if deallocCount != 1 {
25+
t.Errorf("expected dealloc to be called once, but got: %d", deallocCount)
26+
}
27+
}

objc/iboutlet_test.go

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -46,37 +46,35 @@ func NSStringFromString(str string) Object {
4646
func TestIBOutletKeyValueCodingImpl(t *testing.T) {
4747
registerIBOutletTestClass()
4848

49-
pool := GetClass("NSAutoreleasePool").Send("alloc").Send("init")
50-
defer pool.Send("release")
49+
Autorelease(func() {
50+
ibo := GetClass("IBOutletTester").Send("alloc").Send("init")
51+
ibo.Send("setValue:forKey:", ibo, NSStringFromString("Myself").Autorelease())
5152

52-
ibo := GetClass("IBOutletTester").Send("alloc").Send("init")
53-
ibo.Send("setValue:forKey:", ibo, NSStringFromString("Myself").Autorelease())
54-
55-
if ibo.Send("myselfIsNil").Bool() {
56-
t.Fatal("nil iboutlet, value not properly set for key")
57-
}
53+
if ibo.Send("myselfIsNil").Bool() {
54+
t.Fatal("nil iboutlet, value not properly set for key")
55+
}
5856

59-
if !ibo.Send("myselfIsMyself").Bool() {
60-
t.Fatal("value not set, or incorrectly set.")
61-
}
57+
if !ibo.Send("myselfIsMyself").Bool() {
58+
t.Fatal("value not set, or incorrectly set.")
59+
}
60+
})
6261
}
6362

6463
func TestIBOutletSetter(t *testing.T) {
6564
registerIBOutletTestClass()
6665

67-
pool := GetClass("NSAutoreleasePool").Send("alloc").Send("init")
68-
defer pool.Send("release")
66+
Autorelease(func() {
67+
ibo := GetClass("IBOutletTester").Send("alloc").Send("init")
68+
ibo.Send("setMyself:", ibo)
6969

70-
ibo := GetClass("IBOutletTester").Send("alloc").Send("init")
71-
ibo.Send("setMyself:", ibo)
72-
73-
if ibo.Send("myselfIsNil").Bool() {
74-
t.Fatal("nil iboutlet, value not properly set for key")
75-
}
70+
if ibo.Send("myselfIsNil").Bool() {
71+
t.Fatal("nil iboutlet, value not properly set for key")
72+
}
7673

77-
if !ibo.Send("myselfIsMyself").Bool() {
78-
t.Fatal("value not set, or incorrectly set.")
79-
}
74+
if !ibo.Send("myselfIsMyself").Bool() {
75+
t.Fatal("value not set, or incorrectly set.")
76+
}
77+
})
8078
}
8179

8280
type Shadow struct {

objc/msg_test.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ import (
1414
)
1515

1616
func TestMain(m *testing.M) {
17-
pool := GetClass("NSAutoreleasePool").Alloc().Init()
18-
defer pool.Release()
19-
20-
os.Exit(m.Run())
17+
// default to 1 to still exit with an error if a bug leads to not updating the
18+
// status
19+
status := 1
20+
Autorelease(func() {
21+
status = m.Run()
22+
})
23+
os.Exit(status)
2124
}
2225

2326
func BenchmarkSendMsg(b *testing.B) {

objc/object.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -177,14 +177,14 @@ func (obj object) CString() string {
177177

178178
func (obj object) String() string {
179179
// TODO: some kind of recover to catch when this doesnt work
180-
181-
pool := GetClass("NSAutoreleasePool").Alloc().Init()
182-
defer pool.Release()
183-
184-
bytes := obj.Send("description").Send("UTF8String")
185-
if bytes.Pointer() == 0 {
186-
return "(nil)"
187-
}
188-
189-
return bytes.CString()
180+
var r string
181+
Autorelease(func() {
182+
bytes := obj.Send("description").Send("UTF8String")
183+
if bytes.Pointer() == 0 {
184+
r = "(nil)"
185+
} else {
186+
r = bytes.CString()
187+
}
188+
})
189+
return r
190190
}

0 commit comments

Comments
 (0)