204 lines
5.2 KiB
Go
204 lines
5.2 KiB
Go
// An example of manual memory management (nogc directive in nswrap.yaml)
|
|
package main
|
|
|
|
import "C"
|
|
import (
|
|
"fmt"
|
|
"runtime"
|
|
"time"
|
|
|
|
"git.wow.st/gmp/nswrap/examples/memory/ns"
|
|
)
|
|
|
|
func dealloc(self ns.MyClass, super ns.MyClassSupermethods) {
|
|
//[super dealloc] is called for you automatically, so no Supermethods
|
|
//struct is provided here.
|
|
fmt.Println("--dealloc called")
|
|
}
|
|
|
|
func release(self ns.MyClass, super ns.MyClassSupermethods) {
|
|
fmt.Println("--release called")
|
|
super.Release() // comment out for leak
|
|
}
|
|
|
|
//Basic memory allocation test using a manual Autorelease pool. Also utilizes
|
|
//a custom object that overrides dealloc and release methods. Make sure you
|
|
//call [super release] or you will have a leak (see above). [super dealloc] is
|
|
//called for youautomatically as it is basically always required.
|
|
func memtest1() {
|
|
|
|
//because time.Sleep() is called within each loop, it is necessary
|
|
//to lock this goroutine to a thread. Otherwise, Sleep can return
|
|
//and continue execution on a different thread, causing the risk that
|
|
//the next call to NSAutoreleasePool.Drain() seg faults because it is
|
|
//not in the same thread where the NSAutoreleasePool was allocated.
|
|
|
|
runtime.LockOSThread()
|
|
|
|
fmt.Println("memtest1: started")
|
|
for {
|
|
pool := ns.NSAutoreleasePoolAlloc().Init()
|
|
o1 := ns.MyClassAlloc()
|
|
o1.Autorelease()
|
|
o1.DeallocCallback(dealloc)
|
|
o1.ReleaseCallback(release)
|
|
o2 := ns.NSObjectAlloc()
|
|
o2.Autorelease()
|
|
o3 := ns.NSMutableArrayAlloc()
|
|
o3.Autorelease()
|
|
o4 := ns.NSStringAlloc()
|
|
o4.Autorelease()
|
|
_ = o1
|
|
_ = o2
|
|
_ = o3
|
|
_ = o4
|
|
pool.Drain()
|
|
time.Sleep(time.Second / 2)
|
|
}
|
|
fmt.Println("memtest1: done")
|
|
}
|
|
|
|
//Test the ns.Autoreleasepool() function. Also confirm that we do not need
|
|
//to release or manually autorelease objects returned by constructor methods
|
|
//(i.e. not created with *Alloc()).
|
|
func memtest2() {
|
|
runtime.LockOSThread()
|
|
fmt.Println("memtest2: started")
|
|
i := 0
|
|
for {
|
|
ns.Autoreleasepool(func() {
|
|
o1 := ns.NSObjectAlloc()
|
|
o1.Autorelease()
|
|
//Does not leak strings within an autorelease pool
|
|
s1 := ns.NSStringWithGoString(fmt.Sprintf("string-%d", i))
|
|
_ = s1
|
|
//o1.Retain() // uncomment for leak
|
|
i++
|
|
})
|
|
time.Sleep(time.Second / 3)
|
|
}
|
|
fmt.Println("memtest2: done")
|
|
}
|
|
|
|
//Test nested Autoreleasepool invocations -- confirms that objects in the
|
|
//outer pool are not deallocated by the inner pool.
|
|
func memtest3() {
|
|
|
|
runtime.LockOSThread() // comment out for crash
|
|
|
|
fmt.Println("memtest3: started")
|
|
i := 0
|
|
for {
|
|
ns.Autoreleasepool(func() {
|
|
arr := ns.NSMutableArrayAlloc().Init()
|
|
arr.Autorelease()
|
|
arr.AddObject(ns.NSStringWithGoString(fmt.Sprintf("my string %d", i)))
|
|
s1 := ns.NSStringWithGoString(fmt.Sprintf("my other string %d", i))
|
|
fmt.Printf("%s\n", arr.ObjectAtIndex(0).NSString())
|
|
_ = s1
|
|
|
|
for x := 0; x < 3; x++ {
|
|
ns.Autoreleasepool(func() {
|
|
str := arr.ObjectAtIndex(0).NSString()
|
|
fmt.Printf("%d->%s\n", x, str) // does not leak in an autorelease pool
|
|
time.Sleep(time.Second / 5)
|
|
})
|
|
}
|
|
time.Sleep(time.Second / 2)
|
|
i++
|
|
})
|
|
}
|
|
fmt.Println("memtest3: done")
|
|
}
|
|
|
|
//Test of manual memory management.
|
|
func memtest4() {
|
|
go memtest4a()
|
|
go memtest4a()
|
|
go memtest4a()
|
|
go memtest4b()
|
|
go memtest4b()
|
|
go memtest4b()
|
|
go memtest4c()
|
|
go memtest4c()
|
|
go memtest4c()
|
|
// running multiple separate threads with autorelease pools...
|
|
go memtest1()
|
|
go memtest2()
|
|
go memtest3()
|
|
go memtest1()
|
|
go memtest2()
|
|
go memtest3()
|
|
go memtest1()
|
|
go memtest2()
|
|
go memtest3()
|
|
go memtest1()
|
|
go memtest2()
|
|
go memtest3()
|
|
}
|
|
|
|
func memtest4a() {
|
|
for {
|
|
o1 := ns.NSObjectAlloc()
|
|
o1.Init()
|
|
time.Sleep(time.Second / 50)
|
|
o1.Release()
|
|
}
|
|
}
|
|
func memtest4b() {
|
|
i := 0
|
|
for {
|
|
o1 := ns.NSObjectAlloc() // need to Release
|
|
|
|
// These object constructors will always leak. In the case of an
|
|
// immutable string, it is not an issue unless a large number of different
|
|
// strings are going to be made.
|
|
//s1 := ns.NSStringWithGoString(fmt.Sprintf("a string %d",i)) // uncomment for leak
|
|
s1 := ns.NSStringWithGoString("a string")
|
|
i++
|
|
arr := ns.NSArrayAlloc().InitWithObjects(s1) // need to Release arr
|
|
s2 := arr.ObjectAtIndex(0).NSString()
|
|
|
|
// If you try to convert an NSString to UTF8String, CString (*Char),
|
|
// or GoString, Objective-C runtime creates an autoreleased
|
|
// NSTaggedPointerCStringContainer internally and there is no way to
|
|
// release it unless you called your method from within an autorelease
|
|
// pool. The following two calls cause a leak.
|
|
//u := s1.UTF8String() // uncomment for leak
|
|
//fmt.Println(s1) // uncomment for leak
|
|
|
|
time.Sleep(time.Second / 50)
|
|
|
|
o1.Release()
|
|
arr.Release()
|
|
|
|
//s1.Release() // does not prevent the leak caused by
|
|
// NSStringWithGoString: s1 is autoreleased by the Objective-C
|
|
// runtime, these methods will always leak without autorelease
|
|
// pools.
|
|
|
|
_ = o1
|
|
_ = s2
|
|
}
|
|
}
|
|
func memtest4c() {
|
|
for {
|
|
o1 := ns.NSArrayAlloc()
|
|
o2 := ns.NSStringAlloc()
|
|
|
|
time.Sleep(time.Second / 50)
|
|
o1.Release()
|
|
o2.Release()
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
//Uncomment more than one test at a time for a crash.
|
|
//Note: You may not run autorelease pools from multiple goroutines.
|
|
//Within an autorelease pool, do not do anything that can result in a
|
|
//switch to a different thread.
|
|
|
|
go memtest4()
|
|
select {}
|
|
}
|