Skip to content

Commit

Permalink
autoreleasepool support (#50)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mgood authored Oct 21, 2021
1 parent 05f9475 commit 0aa602d
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 49 deletions.
13 changes: 0 additions & 13 deletions core/NSAutoreleasePool.go

This file was deleted.

19 changes: 19 additions & 0 deletions objc/autoreleasepool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package objc

/*
#cgo LDFLAGS: -lobjc -framework Foundation
extern void objc_autoreleasePoolPop(void* pool);
extern void* objc_autoreleasePoolPush(void);
*/
import "C"
import "runtime"

func Autorelease(body func()) {
// ObjC autoreleasepools are thread-local, so we need the body to be locked to
// the same OS thread.
runtime.LockOSThread()
defer runtime.UnlockOSThread()
pool := C.objc_autoreleasePoolPush()
defer C.objc_autoreleasePoolPop(pool)
body()
}
27 changes: 27 additions & 0 deletions objc/autoreleasepool_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package objc

import (
"testing"
)

type AutoreleaseDealloc struct {
Object `objc:"AutoreleaseDealloc : NSObject"`
}

func TestAutorelease(t *testing.T) {
c := NewClassFromStruct(AutoreleaseDealloc{})
deallocCount := 0
c.AddMethod("dealloc", func(o Object) {
deallocCount++
o.SendSuper("dealloc")
})
RegisterClass(c)

Autorelease(func() {
dc := GetClass("AutoreleaseDealloc").Alloc().Init()
dc.Autorelease()
})
if deallocCount != 1 {
t.Errorf("expected dealloc to be called once, but got: %d", deallocCount)
}
}
42 changes: 20 additions & 22 deletions objc/iboutlet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,37 +46,35 @@ func NSStringFromString(str string) Object {
func TestIBOutletKeyValueCodingImpl(t *testing.T) {
registerIBOutletTestClass()

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

ibo := GetClass("IBOutletTester").Send("alloc").Send("init")
ibo.Send("setValue:forKey:", ibo, NSStringFromString("Myself").Autorelease())

if ibo.Send("myselfIsNil").Bool() {
t.Fatal("nil iboutlet, value not properly set for key")
}
if ibo.Send("myselfIsNil").Bool() {
t.Fatal("nil iboutlet, value not properly set for key")
}

if !ibo.Send("myselfIsMyself").Bool() {
t.Fatal("value not set, or incorrectly set.")
}
if !ibo.Send("myselfIsMyself").Bool() {
t.Fatal("value not set, or incorrectly set.")
}
})
}

func TestIBOutletSetter(t *testing.T) {
registerIBOutletTestClass()

pool := GetClass("NSAutoreleasePool").Send("alloc").Send("init")
defer pool.Send("release")
Autorelease(func() {
ibo := GetClass("IBOutletTester").Send("alloc").Send("init")
ibo.Send("setMyself:", ibo)

ibo := GetClass("IBOutletTester").Send("alloc").Send("init")
ibo.Send("setMyself:", ibo)

if ibo.Send("myselfIsNil").Bool() {
t.Fatal("nil iboutlet, value not properly set for key")
}
if ibo.Send("myselfIsNil").Bool() {
t.Fatal("nil iboutlet, value not properly set for key")
}

if !ibo.Send("myselfIsMyself").Bool() {
t.Fatal("value not set, or incorrectly set.")
}
if !ibo.Send("myselfIsMyself").Bool() {
t.Fatal("value not set, or incorrectly set.")
}
})
}

type Shadow struct {
Expand Down
11 changes: 7 additions & 4 deletions objc/msg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ import (
)

func TestMain(m *testing.M) {
pool := GetClass("NSAutoreleasePool").Alloc().Init()
defer pool.Release()

os.Exit(m.Run())
// default to 1 to still exit with an error if a bug leads to not updating the
// status
status := 1
Autorelease(func() {
status = m.Run()
})
os.Exit(status)
}

func BenchmarkSendMsg(b *testing.B) {
Expand Down
20 changes: 10 additions & 10 deletions objc/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,14 +177,14 @@ func (obj object) CString() string {

func (obj object) String() string {
// TODO: some kind of recover to catch when this doesnt work

pool := GetClass("NSAutoreleasePool").Alloc().Init()
defer pool.Release()

bytes := obj.Send("description").Send("UTF8String")
if bytes.Pointer() == 0 {
return "(nil)"
}

return bytes.CString()
var r string
Autorelease(func() {
bytes := obj.Send("description").Send("UTF8String")
if bytes.Pointer() == 0 {
r = "(nil)"
} else {
r = bytes.CString()
}
})
return r
}

0 comments on commit 0aa602d

Please sign in to comment.