Compare commits
No commits in common. "acc8cab583f093a07f119f01b5bdbea3f3084bef" and "4aa1211d7346ac96351840e67a020c5fcdbbc953" have entirely different histories.
acc8cab583
...
4aa1211d73
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -16,5 +16,3 @@ examples/gc/ns
|
||||||
examples/functions/functions
|
examples/functions/functions
|
||||||
examples/functions/ns
|
examples/functions/ns
|
||||||
ns-old
|
ns-old
|
||||||
examples/strings/strings
|
|
||||||
examples/strings/ns
|
|
||||||
|
|
397
README.md
397
README.md
|
@ -62,8 +62,6 @@ pragma [ clang diagnostic ignored "-Wformat-security" ]
|
||||||
|
|
||||||
Regular expressions are permitted in the names of classes, functions,
|
Regular expressions are permitted in the names of classes, functions,
|
||||||
protocols and protocol methods, overridden superclass methods, and enums.
|
protocols and protocol methods, overridden superclass methods, and enums.
|
||||||
Since the `NSObject` class is necessary for memory management, NSWrap will
|
|
||||||
automatically include it if it is encountered in an input header file.
|
|
||||||
|
|
||||||
When invoked, NSWrap creates a subdirectory with the name of the package
|
When invoked, NSWrap creates a subdirectory with the name of the package
|
||||||
as specified in `nswrap.yaml` or, by default, `ns` if a package name is not
|
as specified in `nswrap.yaml` or, by default, `ns` if a package name is not
|
||||||
|
@ -133,14 +131,14 @@ For example, `NSString` provides the folowing `compare` methods:
|
||||||
These are translated into Go as:
|
These are translated into Go as:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func (o *NSString) Compare(string *NSString) NSComparisonResult { }
|
func (o NSString) Compare(string NSString) NSComparisonResult { }
|
||||||
|
|
||||||
func (o *NSString) CompareOptions(string *NSString, mask NSStringCompareOptions) NSComparisonResult { }
|
func (o NSString) CompareOptions(string NSString, mask NSStringCompareOptions) NSComparisonResult { }
|
||||||
|
|
||||||
func (o *NSString) CompareOptionsRange(string *NSString, mask NSStringCompareOptions,
|
func (o NSString) CompareOptionsRange(string NSString, mask NSStringCompareOptions,
|
||||||
rangeOfReceiverToCompare NSRange) NSComparisonResult { }
|
rangeOfReceiverToCompare NSRange) NSComparisonResult { }
|
||||||
|
|
||||||
func (o *NSString) CompareOptionsRangeLocale(string *NSString, mask NSStringCompareOptions,
|
func (o NSString) CompareOptionsRangeLocale(string NSString, mask NSStringCompareOptions,
|
||||||
rangeOfReceiverToCompare NSRange, locale NSObject) NSComparisonResult { }
|
rangeOfReceiverToCompare NSRange, locale NSObject) NSComparisonResult { }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -158,45 +156,11 @@ fmt.Printf("%s\n",str)
|
||||||
NSWrap creates a `Char` Go type that is equivalent to a C `char`. A pointer to
|
NSWrap creates a `Char` Go type that is equivalent to a C `char`. A pointer to
|
||||||
`Char` in Go code can therefore be used with Objective-C functions and methods
|
`Char` in Go code can therefore be used with Objective-C functions and methods
|
||||||
that take a `char*` parameter.
|
that take a `char*` parameter.
|
||||||
|
|
||||||
When NSWrap binds a method that returns `*Char` (and is in garbage collected mode,
|
|
||||||
the default), it first calls `strdup`
|
|
||||||
on the output of the underlying Objective-C method. Therefore, the returned
|
|
||||||
pointer is manually allocated and will need to be freed later from Go. NSWrap
|
|
||||||
creates a
|
|
||||||
`(*Char).Free()` method for use when these pointers are no longer needed.
|
|
||||||
This copying is necessary because the Objective-C runtime will sometimes
|
|
||||||
return pointers to internal objects that are impossible to manage from the
|
|
||||||
Go side. NSWrap aims to cause any internal objects to be deallocated as soon
|
|
||||||
as possible so they do not cause memory leaks. This means that any returned
|
|
||||||
C strings need to be copied and memory managed manually from the Go side.
|
|
||||||
|
|
||||||
NSWrap provides the helper functions `CharWithGoString` and `CharWithBytes`
|
NSWrap provides the helper functions `CharWithGoString` and `CharWithBytes`
|
||||||
that take, respectively, Go strings and Go byte arrays (`[]byte`) and return
|
that take, respectively, Go strings and Go byte arrays (`[]byte`) and return
|
||||||
`*Char` in Go. As demonstrated above, NSWrap also provides `String()`
|
`*Char` in Go. As demonstrated above, NSWrap also provides a `String()`
|
||||||
methods so that the `*Char` and `*NSString` types implement the `Stringer`
|
methods so that the `*Char` and `NSString` types implement the `Stringer`
|
||||||
Go interface and therefore can be sent directly to functions like `fmt.Printf`.
|
Go interface.
|
||||||
The `String()` method on `*NSString` creates a temporary `*Char` internally
|
|
||||||
but frees it for you before returning. Since methods returning
|
|
||||||
`*Char` return a pointer that needs to be manually freed, it is important
|
|
||||||
to use these properly in order to avoid leaks:
|
|
||||||
|
|
||||||
```go
|
|
||||||
nst := ns.NSStringWithGoString("one string")
|
|
||||||
|
|
||||||
// NO: the next line leaks a *Char (UTF8String)
|
|
||||||
//mygostring := nst.UTF8String().String()
|
|
||||||
|
|
||||||
// OK: NSWrap creates a temporary *Char and frees it for you:
|
|
||||||
mygostring := nst.String()
|
|
||||||
|
|
||||||
// ALSO OK: manually free your own temporary *Char:
|
|
||||||
mytmpchar := nst.UTF8String()
|
|
||||||
mygostring = mytmpchar.String()
|
|
||||||
mytmpchar.Free()
|
|
||||||
```
|
|
||||||
In most cases it will be more convenient to convert directly to Go strings instead
|
|
||||||
of `*Char`.
|
|
||||||
|
|
||||||
## Working With NSObject and its Descendants
|
## Working With NSObject and its Descendants
|
||||||
|
|
||||||
|
@ -207,23 +171,14 @@ follows:
|
||||||
type Id struct {
|
type Id struct {
|
||||||
ptr unsafe.Pointer
|
ptr unsafe.Pointer
|
||||||
}
|
}
|
||||||
func (o *Id) Ptr() unsafe.Pointer { if o == nil { return nil }; return o.ptr }
|
func (o Id) Ptr() unsafe.Pointer { return o.ptr }
|
||||||
|
|
||||||
type NSObject interface {
|
type NSObject interface {
|
||||||
Ptr() unsafe.Pointer
|
Ptr() unsafe.Pointer
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Other object types in Go are structs that directly or indirectly embed `Id`
|
Other object types in Go are structs that directly or indirectly embed `Id`
|
||||||
and therefore contain an `unsafe.Pointer` to an Objective-C object, and
|
and therefore implement `NSObject`.
|
||||||
implement `NSObject` by inheriting the `Ptr()` method.
|
|
||||||
|
|
||||||
Because of this implementation, you will note that every Objective-C object
|
|
||||||
is represented by at least two pointers -- an underlying pointer to the
|
|
||||||
Objective-C object
|
|
||||||
in CGo memory (allocated by the Objective-C runtime), as well as a pointer
|
|
||||||
allocated by the Go runtime to an `Id` type, or to another type that directly
|
|
||||||
or indirectly embeds `Id`. This "dual pointer" approach is necessary to ensure
|
|
||||||
that memory management can be made to work correctly (see below for details).
|
|
||||||
|
|
||||||
* The NSObject Interface
|
* The NSObject Interface
|
||||||
|
|
||||||
|
@ -235,7 +190,7 @@ embeds `Id` to be used with generic Objective-C functions. For example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
o1 := ns.NSStringWithGoString("my string")
|
o1 := ns.NSStringWithGoString("my string")
|
||||||
s1 := ns.NSSetWithObjects(o1)
|
s1 := ns.NSSetWithOBjects(o1)
|
||||||
a := ns.NSMutableArrayWithObjects(o1,s1)
|
a := ns.NSMutableArrayWithObjects(o1,s1)
|
||||||
```
|
```
|
||||||
Since `NSString` and `NSSet` in Go both implement the `NSObject` interface,
|
Since `NSString` and `NSSet` in Go both implement the `NSObject` interface,
|
||||||
|
@ -249,73 +204,70 @@ implements the required delegate protocol.
|
||||||
|
|
||||||
* Inheritance
|
* Inheritance
|
||||||
|
|
||||||
Objective-C allows single inheritance. NSWrap automatically adds
|
Objective-C only provides single inheritance. In Go, this is modeled using
|
||||||
inherited methods to classes that are includled in your binding.
|
embedding. Top level objects that inherit from `NSObject` in Objective-C
|
||||||
|
|
||||||
Types created by NSWrap also "embed" their parent class. For example, top
|
|
||||||
level objects that inherit from `NSObject` in Objective-C
|
|
||||||
embed the Go type `Id` and therefore implement the `NSObject` Go interface.
|
embed the Go type `Id` and therefore implement the `NSObject` Go interface.
|
||||||
Other objects embed their direct superclass. For example:
|
Other objects embed their superclass. For example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type NSArray struct { Id }
|
type NSArray struct { Id }
|
||||||
func (o *NSArray) Ptr() unsafe.Pointer { if o == nil { return nil }; return o.ptr }
|
func (o NSArray) Ptr() unsafe.Pointer { return o.ptr }
|
||||||
func (o *Id) NSArray() *NSArray {
|
func (o Id) NSArray() NSArray {
|
||||||
return (*NSArray)(unsafe.Pointer(o))
|
ret := NSArray{}
|
||||||
|
ret.ptr = o.ptr
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
type NSMutableArray struct { NSArray }
|
type NSMutableArray struct { NSArray }
|
||||||
func (o *NSMutableArray) Ptr() unsafe.Pointer { if o == nil { return nil }; return o.ptr }
|
func (o NSMutableArray) Ptr() unsafe.Pointer { return o.ptr }
|
||||||
func (o *Id) NSMutableArray() *NSMutableArray {
|
func (o Id) NSMutableArray() NSMutableArray {...}
|
||||||
return (*NSMutableArray)(unsafe.Pointer(o))
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Observe:
|
Observe:
|
||||||
```go
|
```go
|
||||||
b := ns.NSButtonAlloc() // NSButton > NSControl > NSView > NSResponder > NSObject
|
b := ns.NSButtonAlloc() // NSButton > NSControl > NSView > NSResponder > NSObject
|
||||||
b.InitWithFrame(ns.NSMakeRect(100,100,200,200))
|
b.InitWithFrame(ns.NSMakeRect(100,100,200,200)) // Method of NSView
|
||||||
b.SetTitle(nst("PUSH"))
|
b.SetTitle(nst("PUSH")) // Method of NSButton
|
||||||
vw := win.ContentView()
|
vw := win.ContentView()
|
||||||
vw.AddSubview(&b.NSView) // Pass the button's embedded NSView
|
vw.AddSubview(b.NSView) // Pass the button's embedded NSView
|
||||||
```
|
```
|
||||||
In Go, `NSButtonAlloc` returns a Go object of type `ns.NSButton`. However,
|
In Go, `NSButtonAlloc` returns a Go object of type `ns.NSButton`. However,
|
||||||
the `initWithFrame` method is defined in AppKit for `NSView`. NSWrap will find this
|
there is no `InitWithFrame` method for receivers of this type. This is
|
||||||
method and add it to the Go `NSButton` type when creating your wrapper because
|
not necessary because `NSButton` embeds `NSControl` which in turn embeds
|
||||||
`NSButton` inherits from `NSControl` which inherits from `NSView`.
|
`NSView`. The `InitWithFrame` method only needs to be implemented for `NSView`
|
||||||
|
receivers. Go will automatically find the indirectly embedded `NSView` and
|
||||||
|
call the right method.
|
||||||
|
|
||||||
As of this
|
Note that, since `InitWithFrame()` is defined only for `NSView` and returns
|
||||||
writing, on MacOS 10.13.6, NSWrap binds 115 instance methods for `NSObject`,
|
an `NSView` type,
|
||||||
so things like `Hash()`, `IsEqualTo()`, `ClassName()`, `RespondsToSelector()`
|
the following will not work. Look out for this if you like to chain your
|
||||||
|
`Alloc` and `Init` methods and are getting type errors:
|
||||||
|
|
||||||
|
```go
|
||||||
|
//DO NOT DO THIS -- InitWithFrame returns NSView, not NSButton
|
||||||
|
b := ns.NSButtonAlloc().InitWithFrame(ns.MakeRect(100,100,200,200))
|
||||||
|
```
|
||||||
|
|
||||||
|
Go has no trouble finding embedded methods for your `NSButton` and will
|
||||||
|
happily search up the chain through `NSControl`, `NSView`, `NSResponder` and
|
||||||
|
`NSObject` and all of their associated protocols and categories. As of this
|
||||||
|
writing, on MacOS 10.13.6, NSWrap binds 90 instance methods for `NSObject`,
|
||||||
|
so things like `Hash()`, `IsEqualTo()`, `ClassName()`, `RespondsToSelector`
|
||||||
and many many others are available and can be called on any object directly
|
and many many others are available and can be called on any object directly
|
||||||
from Go.
|
from Go.
|
||||||
|
|
||||||
All objects implement the `NSObject` interface, but from time to time you
|
Go does not perform the same type
|
||||||
will encounter a method that takes a parameter of a different type that may
|
magic when you use variables as function or method parameters.
|
||||||
not exactly match the type you have. For example, if you want to pass your
|
If you want to pass your `NSButton` as a parameter to a method that accepts
|
||||||
`NSButton` as a parameter to a method that accepts an `NSView` type, you need
|
an `NSView` type, you need to explicitly pass the embedded `NSView`
|
||||||
to explicitly pass its embedded `NSView` (`&b.NSView` in the example above).
|
(`b.NSView` in the example above).
|
||||||
This approach is safer than "converting" the button to an `NSView` (see below)
|
|
||||||
because it will only work on objects that directly or indirectly embed an
|
|
||||||
`NSView` Go type.
|
|
||||||
|
|
||||||
NSWrap creates a method for `Id` allowing objects to be converted
|
NSWrap creates a method for `Id` allowing objects to be converted
|
||||||
at run-time to any other class. You will need this for Enumerators and
|
at run-time to any other class. You will need this for Enumerators, which
|
||||||
functions like `NSArray`'s `GetObjects`, for example,
|
always return `Id`. See below under Enumerators for an example, but make
|
||||||
which always return `*Id`. Make
|
|
||||||
sure you know (or test) what type your objects are before converting them.
|
sure you know (or test) what type your objects are before converting them.
|
||||||
You can implement a version of a Go type switch this way:
|
You can
|
||||||
|
implement a somewhat less convenient version of a Go type switch this way.
|
||||||
```go
|
|
||||||
switch {
|
|
||||||
case o.IsKindOfClass(ns.NSStringClass()):
|
|
||||||
// do something with o.NSString()
|
|
||||||
case o.IsKindOfClass(ns.NSSetClass()):
|
|
||||||
// do something with o.NSSet()
|
|
||||||
default:
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Because `Id` can be converted to any type, and every object in the Foundation
|
Because `Id` can be converted to any type, and every object in the Foundation
|
||||||
classes inherits from `Id`, it is possible to send any message to any
|
classes inherits from `Id`, it is possible to send any message to any
|
||||||
|
@ -362,12 +314,11 @@ functions.
|
||||||
Pointers to pointers are sometimes passed to Objective-C methods or functions
|
Pointers to pointers are sometimes passed to Objective-C methods or functions
|
||||||
as a way of receiving output from those functions, especially because
|
as a way of receiving output from those functions, especially because
|
||||||
Objective-C does not allow for multiple return values. In those cases, after
|
Objective-C does not allow for multiple return values. In those cases, after
|
||||||
the CGo call, the method parameter will be treated as an array of
|
the CGo call, the method parameter will be treated as a nil-terminated array of
|
||||||
object pointers that may have been modified by the Objective-C function or
|
object pointers that may have been modified by the Objective-C function or
|
||||||
method. NSWrap will copy the object pointers back into the input Go slice, up
|
method. NSWrap will copy the object pointers back into the input Go slice, up
|
||||||
to its capacity (which will never be changed). The input Go slice is then
|
to its capacity (which will never be changed). The input Go slice is then
|
||||||
truncated to the appropriate length. If there is no output, the length will
|
truncated to the appropriate length.
|
||||||
be set to 0.
|
|
||||||
|
|
||||||
An example in Core Foundation is the `getObjects:andKeys:count` method for
|
An example in Core Foundation is the `getObjects:andKeys:count` method for
|
||||||
`NSDictionary`:
|
`NSDictionary`:
|
||||||
|
@ -378,31 +329,28 @@ An example in Core Foundation is the `getObjects:andKeys:count` method for
|
||||||
ns.NSArrayWithObjects(nst("obj1"),nst("obj2")),
|
ns.NSArrayWithObjects(nst("obj1"),nst("obj2")),
|
||||||
ns.NSArrayWithObjects(nst("key1"),nst("key2")),
|
ns.NSArrayWithObjects(nst("key1"),nst("key2")),
|
||||||
)
|
)
|
||||||
va,ka := make([]*ns.Id,0,5), make([]*ns.Id,0,5) // length 0, capacity 5 slices
|
os,ks := make([]ns.Id,0,5), make([]ns.Id,0,5) // length 0, capacity 5 slices
|
||||||
dict.GetObjects(&va,&ka,5)
|
dict.GetObjects(&os,&ks,5)
|
||||||
// last parameter to GetObjects is the count, must be less than or equal to the input slice capacity
|
// last parameter is the count, must be less than or equal to the input slice capacity
|
||||||
fmt.Printf("Length of va is now %d\n",len(va)) // va and ka slices are now length = 2
|
fmt.Printf("Length of os is now %d\n",len(os)) // os and ks slices are now length = 2
|
||||||
for i,k := range ka {
|
for i,k := range ks {
|
||||||
fmt.Printf("-- %s -> %s\n",k.NSString(),va[i].NSString())
|
fmt.Printf("-- %s -> %s\n",k.NSString(),os[i].NSString())
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
NSWrap will not check the "count" parameter, so the user will always need
|
NSWrap will never check the "count" parameter, so the user will always need
|
||||||
to make sure it is less than or equal to the capacity of the input
|
to make sure it is less than or equal to the capacity of the input
|
||||||
Go slices.
|
Go slices.
|
||||||
|
|
||||||
Using pointers to pointers is necessary in many Core Foundation situations
|
Using pointers to pointers is necessary in many Core Foundation situations
|
||||||
where you need to get an error message out of a function or method, or in other
|
where you need to get an error message out of a function or method.
|
||||||
cases where an Objective-C method wants to provide multiple return values.
|
|
||||||
Here is an example using `[NSString stringWithContentsOfURL...]`:
|
Here is an example using `[NSString stringWithContentsOfURL...]`:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
err := make([]*ns.NSError,1)
|
err := make([]ns.NSError,1)
|
||||||
n1 = ns.NSStringWithContentsOfURLEncoding(ns.NSURLWithGoString("htttypo://example.com"),0,&err)
|
n1 = ns.NSStringWithContentsOfURLEncoding(ns.NSURLWithGoString("htttypo://example.com"),0,&err)
|
||||||
if len(err) > 0 {
|
|
||||||
fmt.Printf("err: %s\n",err[0].LocalizedDescription())
|
fmt.Printf("err: %s\n",err[0].LocalizedDescription())
|
||||||
//err: The file couldn’t be opened because URL type htttypo isn’t supported.
|
//err: The file couldn’t be opened because URL type htttypo isn’t supported.
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Selectors
|
## Selectors
|
||||||
|
@ -422,17 +370,15 @@ appMenu.AddItemWithTitle(
|
||||||
## Enumerators
|
## Enumerators
|
||||||
|
|
||||||
NSWrap provides a `ForIn` method for the `NSEnumerator` type. Call it with a
|
NSWrap provides a `ForIn` method for the `NSEnumerator` type. Call it with a
|
||||||
`func(*ns.Id) bool` parameter that returns `true` to continue and `false` to
|
`func(ns.Id) bool` parameter that returns `true` to continue and `false` to
|
||||||
stop the enumeration.
|
stop the enumeration.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
a := ns.NSArrayWithObjects(o1,o2,o3)
|
a := ns.NSArrayWithObjects(o1,o2,o3)
|
||||||
i := 0
|
a.ObjectEnumerator().ForIn(func (o ns.Id) bool {
|
||||||
a.ObjectEnumerator().ForIn(func (o *ns.Id) bool {
|
|
||||||
switch {
|
switch {
|
||||||
case o.IsKindOfClass(ns.NSStringClass()):
|
case o.IsKindOfClass(ns.NSStringClass()):
|
||||||
fmt.Printf("%d: %s\n", i, o.NSString())
|
fmt.Println(o.NSString().UTF8String())
|
||||||
i++
|
|
||||||
return true // continue enumeration
|
return true // continue enumeration
|
||||||
default:
|
default:
|
||||||
fmt.Println("Unknown class")
|
fmt.Println("Unknown class")
|
||||||
|
@ -446,7 +392,7 @@ identification.
|
||||||
|
|
||||||
## Enum Definitions
|
## Enum Definitions
|
||||||
|
|
||||||
NSWrap translates C `enum` values into Go constants. The enums you want are
|
NSWrap translates C `enum` values into Go constants. The enums you need are
|
||||||
specified in `nswrap.yaml` by regular expression, which, in the case of named
|
specified in `nswrap.yaml` by regular expression, which, in the case of named
|
||||||
enums, must match the name of the `enum` itself, or in the case of anonymous
|
enums, must match the name of the `enum` itself, or in the case of anonymous
|
||||||
enums, must match the name of the constant(s) you are looking for as declared
|
enums, must match the name of the constant(s) you are looking for as declared
|
||||||
|
@ -480,6 +426,70 @@ const _CLOCK_MONOTONIC_RAW = C._CLOCK_MONOTONIC_RAW
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Memory management
|
||||||
|
|
||||||
|
Objective-C objects are always allocated and returned from CGo code, and
|
||||||
|
therefore these pointers are not garbage collected by Go. You can use any
|
||||||
|
of the standard Objective-C memory management techniques for those pointers,
|
||||||
|
which seem to work but have not been extensively tested.
|
||||||
|
|
||||||
|
Since everything inherits methods from `NSObject`, you can call `Retain()`,
|
||||||
|
`Release()` and `Autorelease()` on any object.
|
||||||
|
|
||||||
|
If the autorelease configuration directive is set to "true", all allocation functions
|
||||||
|
created by NSWrap (i.e. those ending in `Alloc`) will call `autorelease` before they
|
||||||
|
return an object. Alternately, objects can be manually sent an autorelease message.
|
||||||
|
If you are not working in an environment (such as an
|
||||||
|
Application Delegate callback) that provides an autorelease pool, you can
|
||||||
|
create your own:
|
||||||
|
|
||||||
|
* Work directly with `NSAutoreleasePool` objects
|
||||||
|
|
||||||
|
```go
|
||||||
|
swamp := ns.NSAutoreleasePoolAlloc().Init()
|
||||||
|
del := ns.AppDelegateAlloc()
|
||||||
|
//del.Autorelease() // if autorelease: true is not set in nswrap.yaml
|
||||||
|
menu := ns.NSMenuAlloc().InitWithTitle(nst("Main"))
|
||||||
|
//menu.Autorelease()
|
||||||
|
str := ns.NSStringWithGoString("these objects will be automatically deallocated when swamp is drained.")
|
||||||
|
...
|
||||||
|
swamp.Drain()
|
||||||
|
```
|
||||||
|
|
||||||
|
* ...or use the `AutoreleasePool()` helper function
|
||||||
|
|
||||||
|
NSWrap provides a helper function that can be passed a `func()` with no
|
||||||
|
parameters or return value. It is conventient to give it an anonymous function
|
||||||
|
and write your code in line, just like you would if you were using an
|
||||||
|
`@autoreleasepool { }` block.
|
||||||
|
|
||||||
|
```go
|
||||||
|
ns.AutoreleasePool(func() {
|
||||||
|
a := MyObjectAlloc().Init()
|
||||||
|
b := MyOtherObjectAlloc().Init()
|
||||||
|
...
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
You will need to make sure `NSAutoreleasePool` is included in the `classes`
|
||||||
|
directive in your configuration file before working with
|
||||||
|
`NSAutoreleasePool` objects or the `AutoreleasePool` helper function.
|
||||||
|
|
||||||
|
* Pitfalls
|
||||||
|
|
||||||
|
Go concurrency does not play well with Objective-C memory management. In particular,
|
||||||
|
an AutoreleasePool needs to be allocated and drained from the same thread, and
|
||||||
|
only objects allocated within that thread will be drained. Objects allocated and
|
||||||
|
autoreleased from a different goroutine in the same thread are at risk of being
|
||||||
|
prematurely drained. Therefore, you should only work with one AutoreleasePool at
|
||||||
|
a time, and only within a thread that is locked to the OS thread
|
||||||
|
(by calling `runtime.LockOSThread()`). If you will be allocating Objective-C objects
|
||||||
|
from multiple goroutines, it is best not to use the `autorelease: true` directive
|
||||||
|
as that will cause all objects to receive an `autorelease` message even if they are
|
||||||
|
created outside the thread where are using your autorelease pool.
|
||||||
|
See `examples/memory` for some basic tests and read the comments to larn what to avoid.
|
||||||
|
|
||||||
## Delegates
|
## Delegates
|
||||||
|
|
||||||
The `delegates` directive in `nswrap.yaml` creates a new Objective-C
|
The `delegates` directive in `nswrap.yaml` creates a new Objective-C
|
||||||
|
@ -521,8 +531,7 @@ registered,
|
||||||
it will be called with all of its parameters converted to their Go type
|
it will be called with all of its parameters converted to their Go type
|
||||||
equivalents. User-defined callbacks are registered by calling a function
|
equivalents. User-defined callbacks are registered by calling a function
|
||||||
with the method name in TitleCase + `Callback`, so in the example above,
|
with the method name in TitleCase + `Callback`, so in the example above,
|
||||||
if your delegate was named `del`, you would call
|
you would call `ns.CentralManagerDidUpdateStateCallback(...)` with the name of
|
||||||
`del.CentralManagerDidUpdateStateCallback(...)` with the name of
|
|
||||||
your callback function to register to receive notifications when your central
|
your callback function to register to receive notifications when your central
|
||||||
manager updates its state.
|
manager updates its state.
|
||||||
|
|
||||||
|
@ -538,16 +547,11 @@ func cb(c ns.CBCentralManager) {
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
del *ns.CBDelegate // use global variables so these don't get garbage collected
|
|
||||||
cm *ns.CBCentralManager
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
...
|
...
|
||||||
del = ns.CBDelegateAlloc()
|
del := ns.CBDelegateAlloc()
|
||||||
del.CentralManagerDidUpdateStateCallback(cb)
|
del.CentralManagerDidUpdateStateCallback(cb)
|
||||||
cm = ns.CBCentralManagerAlloc().InitWithDelegateQueue(del,queue)
|
cm := ns.CBCentralManagerAlloc().InitWithDelegateQueue(del,queue)
|
||||||
```
|
```
|
||||||
|
|
||||||
When you provide user-defined callback functions, you will need to specify
|
When you provide user-defined callback functions, you will need to specify
|
||||||
|
@ -558,12 +562,12 @@ messages will point you in the right direction.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ go build
|
$ go build
|
||||||
./main.go:127:43: cannot use didFinishLaunching (type func(*ns.NSNotification, bool)) as type
|
./main.go:127:43: cannot use didFinishLaunching (type func(ns.NSNotification, bool)) as type
|
||||||
func(*ns.NSNotification) in argument to del.ApplicationDidFinishLaunchingCallback
|
func(ns.NSNotification) in argument to del.ApplicationDidFinishLaunchingCallback
|
||||||
```
|
```
|
||||||
In the above example, the build failed because an extra `bool` parameter was
|
In the above example, the build failed because an extra `bool` parameter was
|
||||||
included in the callback function. The compiler is telling you that the right
|
included in the callback function. The compiler is telling you that the right
|
||||||
type for the callback is `func(*ns.NSNotification)` with no return value.
|
type for the callback is `func(ns.NSNotification)` with no return value.
|
||||||
|
|
||||||
## Working with AppKit
|
## Working with AppKit
|
||||||
|
|
||||||
|
@ -572,7 +576,8 @@ Delegate. This allows you to build a Cocoa application entirely in Go.
|
||||||
|
|
||||||
Because AppKit uses thread local storage, you will need to make sure all
|
Because AppKit uses thread local storage, you will need to make sure all
|
||||||
calls into it are done from the main OS thread. This can be a challenge in
|
calls into it are done from the main OS thread. This can be a challenge in
|
||||||
Go and you will want to make use of `runtime.LockOSThread()`.
|
Go even though runtime.LockOSThread() is supposed to provide
|
||||||
|
this functionality.
|
||||||
|
|
||||||
This is actually a full working Cocoa application:
|
This is actually a full working Cocoa application:
|
||||||
|
|
||||||
|
@ -613,30 +618,24 @@ import (
|
||||||
"git.wow.st/gmp/nswrap/examples/app/ns" // point to your own NSWrap output directory
|
"git.wow.st/gmp/nswrap/examples/app/ns" // point to your own NSWrap output directory
|
||||||
)
|
)
|
||||||
|
|
||||||
func didFinishLaunching(n *ns.NSNotification) {
|
func didFinishLaunching(n ns.NSNotification) {
|
||||||
fmt.Println("Go: did finish launching!")
|
fmt.Println("Go: did finish launching!")
|
||||||
}
|
}
|
||||||
|
|
||||||
func shouldTerminate(s *ns.NSApplication) ns.BOOL {
|
func shouldTerminate(s ns.NSApplication) ns.BOOL {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
a *ns.NSApplication // global vars so these are not garbage collected
|
|
||||||
del *ns.AppDelegate
|
|
||||||
win *ns.NSWindow
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
a = ns.NSApplicationSharedApplication()
|
a := ns.NSApplicationSharedApplication()
|
||||||
a.SetActivationPolicy(ns.NSApplicationActivationPolicyRegular)
|
a.SetActivationPolicy(ns.NSApplicationActivationPolicyRegular)
|
||||||
del = ns.AppDelegateAlloc()
|
del := ns.AppDelegateAlloc()
|
||||||
del.ApplicationDidFinishLaunchingCallback(didFinishLaunching)
|
del.ApplicationDidFinishLaunchingCallback(didFinishLaunching)
|
||||||
del.ApplicationShouldTerminateAfterLastWindowClosedCallback(shouldTerminate)
|
del.ApplicationShouldTerminateAfterLastWindowClosedCallback(shouldTerminate)
|
||||||
a.SetDelegate(del)
|
a.SetDelegate(del)
|
||||||
|
|
||||||
win = ns.NSWindowAlloc().InitWithContentRectStyleMask(
|
win := ns.NSWindowAlloc().InitWithContentRectStyleMask(
|
||||||
ns.NSMakeRect(200,200,600,600),
|
ns.NSMakeRect(200,200,600,600),
|
||||||
ns.NSWindowStyleMaskTitled | ns.NSWindowStyleMaskClosable,
|
ns.NSWindowStyleMaskTitled | ns.NSWindowStyleMaskClosable,
|
||||||
ns.NSBackingStoreBuffered,
|
ns.NSBackingStoreBuffered,
|
||||||
|
@ -648,7 +647,7 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Pretty simple right? Not really, NSWrap just generated over 39,000 lines of
|
Pretty simple right? Not really, NSWrap just generated almost 15,000 lines of
|
||||||
code. See `examples/app` for a slightly more complex example with working
|
code. See `examples/app` for a slightly more complex example with working
|
||||||
menus, visual format-based auto layout, and a custom button class.
|
menus, visual format-based auto layout, and a custom button class.
|
||||||
|
|
||||||
|
@ -668,7 +667,7 @@ subclasses:
|
||||||
yourClass: # the superclass to inherit from
|
yourClass: # the superclass to inherit from
|
||||||
- init.* # what methods to override
|
- init.* # what methods to override
|
||||||
- -(void)hi_there:(int)x # Objective-C prototype of your new method(s)
|
- -(void)hi_there:(int)x # Objective-C prototype of your new method(s)
|
||||||
# \--the initial hyphen indicates that this is an instance method
|
# |--this hyphen indicates that this is an instance method
|
||||||
```
|
```
|
||||||
|
|
||||||
In the example above, your new class will be named `myClass` in Objective-C
|
In the example above, your new class will be named `myClass` in Objective-C
|
||||||
|
@ -729,109 +728,9 @@ Later on you can add your new button to a view and tell Cocoa where to lay
|
||||||
it out. It's all a little verbose, but that's because for some reason you
|
it out. It's all a little verbose, but that's because for some reason you
|
||||||
decided to write Objective-C code in Go.
|
decided to write Objective-C code in Go.
|
||||||
|
|
||||||
## Memory management
|
|
||||||
|
|
||||||
As mentioned above, NSWrap is designed for there to be at least one Go pointer
|
|
||||||
associated with each underlying Objective-C object pointer.
|
|
||||||
Since Objective-C memory
|
|
||||||
is always allocated by the Objective-C runtime, it is not possible for the Go
|
|
||||||
runtime to have visibility into these memory regions or to directly manage memory
|
|
||||||
used by the CGo code. However, Go will keep track of the associated Go pointer
|
|
||||||
that was created the first time the corresponding Objective-C object was passed
|
|
||||||
over to the Go side and an `Id` or other NSWrap struct type was allocated.
|
|
||||||
Because of this,
|
|
||||||
it is possible to hook into the Go garbage collection system in an attempt to
|
|
||||||
manage Objective-C memory strictly from the Go side. When there are no remaining Go
|
|
||||||
pointers to an NSWrap `Id` struct, it will be deallocated by the Go garbage collector
|
|
||||||
and a finalizer will be called that `release`es the corresponding Objective-C
|
|
||||||
object.
|
|
||||||
|
|
||||||
The memory management rules work as follows:
|
|
||||||
|
|
||||||
* Objects in Go are represented by pointers to types that implement the `NSObject`
|
|
||||||
interface
|
|
||||||
* NSObject has one method, `Ptr()`, which returns an `unsafe.Pointer` to an
|
|
||||||
Objective-C object.
|
|
||||||
* All methods that return objects to Go call `retain` except for `new`, `init`,
|
|
||||||
`alloc`, `copy` and `mutableCopy`, which already return retained objects from the
|
|
||||||
Objective-C runtime.
|
|
||||||
* Go wrappers for Objective-C methods call `runtime.SetFinalizer()`, which calls
|
|
||||||
`release` when the associated Go struct is garbage collected.
|
|
||||||
* All Objective-C methods are run inside an `@autoreleasepool {}` block to prevent
|
|
||||||
internal memory leaks within the Objective-C libraries and frameworks.
|
|
||||||
* Objects sent to you in callback functions are not memory managed by Go
|
|
||||||
and must be manually
|
|
||||||
managed using `Retain()` and `Release()` methods if you need to take ownership of them.
|
|
||||||
A rule of thumb is that if you assign such an object to a persistent Go variable for
|
|
||||||
use outside of the callback, call `Retain()`.
|
|
||||||
|
|
||||||
Because of the linkage with the Go garbage collector described above, there should be
|
|
||||||
no need for any memory management code to be written from the Go side, except in the
|
|
||||||
case mentioned above where your Go delegate receives objects that need to be kept
|
|
||||||
around outside of the callback.
|
|
||||||
|
|
||||||
Since everything in Objective C inherits methods from `NSObject`, you can call
|
|
||||||
`Retain()`, `Release()` and `Autorelease()` on any object. You can technically bind
|
|
||||||
the `NSAutoreleasePool` class and create and drain instances of it from the Go side,
|
|
||||||
but this is not recommended in the default, garbage collected mode and can run into
|
|
||||||
problems because the Go runtime is inherently multithreaded. See `examples/memory`
|
|
||||||
for an example of manual memory management, which should be possible to do reliably
|
|
||||||
but I'm not sure why you would go through the trouble.
|
|
||||||
|
|
||||||
NSWrap is doing a number of things behind the scenes to make garbage collection work.
|
|
||||||
As mentioned,
|
|
||||||
all Objective-C methods are called within an `@autorelease {}` block. This is
|
|
||||||
necessary because some foundation classes (notably `NSString`) create internal
|
|
||||||
objects that are `autoreleased` but never returned to the caller. These objects can
|
|
||||||
never be deallocated unless the method in question was called within an autorelease
|
|
||||||
pool.
|
|
||||||
|
|
||||||
NSWrap assumes you
|
|
||||||
are going to take ownership of every Objective-C object returned by a method, either
|
|
||||||
directly as a return value or through a pointer to a pointer given as a parameter.
|
|
||||||
Therefore, NSWrap calls `retain` on all of these objects before going back to the
|
|
||||||
Go side, unless the object is either `nil` or equivalent to the input object.
|
|
||||||
NSWrap also will not call `retain` on the return values of `init`, `new`, `copy`,
|
|
||||||
`mutableCopy` or `alloc` methods. If you do not want ownership of the object,
|
|
||||||
simply assign it to a local varable and the garbage collector will take care of
|
|
||||||
releasing it.
|
|
||||||
|
|
||||||
In
|
|
||||||
order for this to work on a pointer to a pointer parameter, NSWrap treats the
|
|
||||||
input
|
|
||||||
parameter as an array with a length specified by either a `range` parameter (of
|
|
||||||
type `NSRange`) or a `count` parameter of an integer type. If there is neither a
|
|
||||||
`range` or `count` parameter, NSWrap assumes the array is length 1.
|
|
||||||
|
|
||||||
As an example, in Objective-C, if you were to take an object out of an `NSArray`
|
|
||||||
and the array was later deallocated, there is no guarantee that the object you
|
|
||||||
obtained is still around unless you called `retain` on it. This is not necessary
|
|
||||||
with NSWrap, which automatically retains objects returned by methods like
|
|
||||||
`objectAtIndex:` and `getObjects:range` and manages them with the Go garbage
|
|
||||||
collector.
|
|
||||||
|
|
||||||
The methods described above work for methods that return Objective-C
|
|
||||||
objects, which can be `retain`ed, but not with methods that return other types of
|
|
||||||
pointers such as C strings. NSWrap has a special case for C strings (`*Char`
|
|
||||||
in Go), calling
|
|
||||||
`strdup` on the return value within the `@autoreleasepool` block. This
|
|
||||||
ensures that the string is preserved even if it points to a termporary
|
|
||||||
autoreleased
|
|
||||||
object. Since this behavior results in newly allocated memory, these pointers will
|
|
||||||
need to be freed from Go later on. Since these are pointers to C memory,
|
|
||||||
it is not possible to set a finalizer on these pointers for garbage collection
|
|
||||||
by Go.
|
|
||||||
|
|
||||||
Note that the Go garbage collector is lazy and will not activate unless your
|
|
||||||
application is running low on heap space. That means in practice that Objective-C
|
|
||||||
objects are going to stick around a lot longer than they might in a pure
|
|
||||||
Objective-C application. If this is an issue, simply run the Go GC manually with
|
|
||||||
`runtime.GC()`.
|
|
||||||
|
|
||||||
|
|
||||||
# Limitations
|
# Limitations
|
||||||
|
|
||||||
## Blocks and Function Pointers
|
## Blocks
|
||||||
|
|
||||||
NSWrap does not support methods or functions that take C functions or blocks
|
NSWrap does not support methods or functions that take C functions or blocks
|
||||||
as parameters or return values.
|
as parameters or return values.
|
||||||
|
@ -843,7 +742,7 @@ and got carried away.
|
||||||
|
|
||||||
# Acknowledgements
|
# Acknowledgements
|
||||||
|
|
||||||
This work was inspired by Maxim Kupriianov's excellent
|
This work was inspired by Maxim's excellent
|
||||||
[c-for-go](https://github.com/xlab/c-for-go). Much of the
|
[c-for-go](https://github.com/xlab/c-for-go). Much of the
|
||||||
infrastructure was lifted from Elliot Chance's equally excellent
|
infrastructure was lifted from Elliot Chance's equally excellent
|
||||||
[c2go](https://github.com/elliotchance/c2go). Kiyoshi Murata's
|
[c2go](https://github.com/elliotchance/c2go). Kiyoshi Murata's
|
||||||
|
|
|
@ -21,10 +21,6 @@ func pb2() {
|
||||||
a.Terminate(a)
|
a.Terminate(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func db() {
|
|
||||||
fmt.Println("button deallocated")
|
|
||||||
}
|
|
||||||
|
|
||||||
func didFinishLaunching(n *ns.NSNotification) {
|
func didFinishLaunching(n *ns.NSNotification) {
|
||||||
fmt.Println("Go: did finish launching")
|
fmt.Println("Go: did finish launching")
|
||||||
fmt.Printf("Notification: %s\n", n.Name().UTF8String())
|
fmt.Printf("Notification: %s\n", n.Name().UTF8String())
|
||||||
|
@ -72,14 +68,12 @@ func didFinishLaunching(n *ns.NSNotification) {
|
||||||
|
|
||||||
b1.Init()
|
b1.Init()
|
||||||
b1.PressedCallback(pb1)
|
b1.PressedCallback(pb1)
|
||||||
b1.DeallocCallback(db)
|
|
||||||
b1.SetAction(ns.Selector("pressed"))
|
b1.SetAction(ns.Selector("pressed"))
|
||||||
b1.SetTarget(b1)
|
b1.SetTarget(b1)
|
||||||
b1.SetTitle(nst("PUSH"))
|
b1.SetTitle(nst("PUSH"))
|
||||||
|
|
||||||
b2.Init()
|
b2.Init()
|
||||||
b2.PressedCallback(pb2)
|
b2.PressedCallback(pb2)
|
||||||
b2.DeallocCallback(db)
|
|
||||||
b2.SetTarget(b2)
|
b2.SetTarget(b2)
|
||||||
b2.SetAction(ns.Selector("pressed"))
|
b2.SetAction(ns.Selector("pressed"))
|
||||||
b2.SetTitle(nst("QUIT"))
|
b2.SetTitle(nst("QUIT"))
|
||||||
|
@ -109,7 +103,6 @@ func didFinishLaunching(n *ns.NSNotification) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func shouldTerminateAfterLastWindowClosed(s *ns.NSApplication) ns.BOOL {
|
func shouldTerminateAfterLastWindowClosed(s *ns.NSApplication) ns.BOOL {
|
||||||
fmt.Println("Go: should terminate after last window closed")
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +117,6 @@ func didBecomeActive(n *ns.NSNotification) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
a *ns.NSApplication
|
a *ns.NSApplication
|
||||||
del *ns.AppDelegate
|
|
||||||
win *ns.NSWindow
|
win *ns.NSWindow
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -135,8 +127,7 @@ func app() {
|
||||||
a.SetActivationPolicy(ns.NSApplicationActivationPolicyRegular)
|
a.SetActivationPolicy(ns.NSApplicationActivationPolicyRegular)
|
||||||
|
|
||||||
// Set up an AppDelegate
|
// Set up an AppDelegate
|
||||||
// assign it to a global variable so it doesn't get garbage collected
|
del := ns.AppDelegateAlloc()
|
||||||
del = ns.AppDelegateAlloc()
|
|
||||||
del.ApplicationDidFinishLaunchingCallback(didFinishLaunching)
|
del.ApplicationDidFinishLaunchingCallback(didFinishLaunching)
|
||||||
del.ApplicationShouldTerminateAfterLastWindowClosedCallback(shouldTerminateAfterLastWindowClosed)
|
del.ApplicationShouldTerminateAfterLastWindowClosedCallback(shouldTerminateAfterLastWindowClosed)
|
||||||
del.ApplicationWillTerminateCallback(willTerminate)
|
del.ApplicationWillTerminateCallback(willTerminate)
|
||||||
|
@ -158,6 +149,6 @@ func main() {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Run our app in an autorelease pool just for fun
|
// Run our app in an autorelease pool just for fun
|
||||||
go app()
|
go ns.Autoreleasepool(app)
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,8 @@ subclasses:
|
||||||
GButton:
|
GButton:
|
||||||
NSButton:
|
NSButton:
|
||||||
- -(void)pressed
|
- -(void)pressed
|
||||||
- dealloc
|
|
||||||
|
|
||||||
frameworks: [ Foundation, AppKit ]
|
frameworks: [ Foundation, AppKit ]
|
||||||
pragma: [ clang diagnostic ignored "-Wformat-security" ]
|
pragma: [ clang diagnostic ignored "-Wformat-security" ]
|
||||||
|
#autorelease: true
|
||||||
|
gogc: true
|
||||||
|
|
|
@ -100,12 +100,12 @@ func hr(d *ns.NSData) int {
|
||||||
func discoverCharacteristics(p *ns.CBPeripheral, s *ns.CBService, e *ns.NSError) {
|
func discoverCharacteristics(p *ns.CBPeripheral, s *ns.CBService, e *ns.NSError) {
|
||||||
fmt.Printf("Did discover characteristics\n")
|
fmt.Printf("Did discover characteristics\n")
|
||||||
uuid := s.UUID()
|
uuid := s.UUID()
|
||||||
fmt.Printf("----%s\n", uuid.UUIDString())
|
fmt.Printf("----%s\n", uuid.UUIDString().UTF8String())
|
||||||
if uuid.IsEqualTo(hrm_uuid) {
|
if uuid.IsEqualTo(hrm_uuid) {
|
||||||
s.Characteristics().ObjectEnumerator().ForIn(func(o *ns.Id) bool {
|
s.Characteristics().ObjectEnumerator().ForIn(func(o *ns.Id) bool {
|
||||||
chr := o.CBCharacteristic()
|
chr := o.CBCharacteristic()
|
||||||
chuuid := chr.UUID()
|
chuuid := chr.UUID()
|
||||||
fmt.Printf("------%s\n", chuuid.UUIDString())
|
fmt.Printf("------%s\n", chuuid.UUIDString().UTF8String())
|
||||||
if chuuid.IsEqualTo(hrv_uuid) {
|
if chuuid.IsEqualTo(hrv_uuid) {
|
||||||
p.SetNotifyValue(1, chr)
|
p.SetNotifyValue(1, chr)
|
||||||
v := chr.Value()
|
v := chr.Value()
|
||||||
|
|
|
@ -39,3 +39,4 @@ delegates:
|
||||||
- peripheralDidUpdateValueForCharacteristic
|
- peripheralDidUpdateValueForCharacteristic
|
||||||
|
|
||||||
pragma: [ clang diagnostic ignored "-Wformat-security" ]
|
pragma: [ clang diagnostic ignored "-Wformat-security" ]
|
||||||
|
gogc: true
|
||||||
|
|
|
@ -4,7 +4,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
|
|
||||||
"git.wow.st/gmp/nswrap/examples/foundation/ns"
|
"git.wow.st/gmp/nswrap/examples/foundation/ns"
|
||||||
)
|
)
|
||||||
|
@ -44,10 +43,8 @@ func main() {
|
||||||
fmt.Printf("i1 = %@\n", i1)
|
fmt.Printf("i1 = %@\n", i1)
|
||||||
fmt.Printf("i1.Ptr() = %p\n", i1.Ptr())
|
fmt.Printf("i1.Ptr() = %p\n", i1.Ptr())
|
||||||
fmt.Printf("\nNSArray.ObjectEnumerator().ForIn():\n")
|
fmt.Printf("\nNSArray.ObjectEnumerator().ForIn():\n")
|
||||||
x := 0
|
|
||||||
a.ObjectEnumerator().ForIn(func(o *ns.Id) bool {
|
a.ObjectEnumerator().ForIn(func(o *ns.Id) bool {
|
||||||
fmt.Printf("%d: %s\n",x,o.NSString())
|
fmt.Println(o.NSString())
|
||||||
x++
|
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
fmt.Printf("\nNSSetWithObjectsCount():\n")
|
fmt.Printf("\nNSSetWithObjectsCount():\n")
|
||||||
|
@ -72,7 +69,6 @@ func main() {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
fmt.Printf("a = %p a.NSArray = %p\n",a,&a.NSArray)
|
|
||||||
fmt.Printf("\nNSArrayWithObjects() (length 1)\n")
|
fmt.Printf("\nNSArrayWithObjects() (length 1)\n")
|
||||||
a2 = ns.NSArrayWithObjects(n1)
|
a2 = ns.NSArrayWithObjects(n1)
|
||||||
a2.ObjectEnumerator().ForIn(func(o *ns.Id) bool {
|
a2.ObjectEnumerator().ForIn(func(o *ns.Id) bool {
|
||||||
|
@ -106,26 +102,19 @@ func main() {
|
||||||
ns.NSArrayWithObjects(nst("obj1"), nst("obj2")),
|
ns.NSArrayWithObjects(nst("obj1"), nst("obj2")),
|
||||||
ns.NSArrayWithObjects(nst("key1"), nst("key2")),
|
ns.NSArrayWithObjects(nst("key1"), nst("key2")),
|
||||||
)
|
)
|
||||||
oarr := make([]*ns.Id, 0, 5)
|
os := make([]*ns.Id, 0, 5)
|
||||||
fmt.Printf("Length of oarr is %d\n", len(oarr))
|
fmt.Printf("Length of os is %d\n", len(os))
|
||||||
karr := make([]*ns.Id, 0, 5)
|
ks := make([]*ns.Id, 0, 5)
|
||||||
fmt.Printf("\nGetObjects()\n")
|
fmt.Printf("\nGetObjects()\n")
|
||||||
d.GetObjects(&oarr, &karr, 4)
|
d.GetObjects(&os, &ks, 4)
|
||||||
fmt.Printf("Length of oarr is now %d\n", len(oarr))
|
fmt.Printf("Length of os is now %d\n", len(os))
|
||||||
for i, k := range karr {
|
for i, k := range ks {
|
||||||
fmt.Printf("-- %s -> %s\n", k.NSString(), oarr[i].NSString())
|
fmt.Printf("-- %s -> %s\n", k.NSString(), os[i].NSString())
|
||||||
}
|
}
|
||||||
fmt.Printf("\nNSStringWithContentsOfURLEncoding()\n")
|
fmt.Printf("\nNSStringWithContentsOfURLEncoding()\n")
|
||||||
err := make([]*ns.NSError, 1)
|
err := make([]*ns.NSError, 1)
|
||||||
n1 = ns.NSStringWithContentsOfURLEncoding(ns.NSURLWithGoString("http://captive.apple.com"), ns.NSUTF8StringEncoding, &err)
|
n1 = ns.NSStringWithContentsOfURLEncoding(ns.NSURLWithGoString("htttypo://example.com"), 0, &err)
|
||||||
if len(err) == 0 {
|
|
||||||
fmt.Printf("n1 = %s\n",n1)
|
|
||||||
}
|
|
||||||
n1 = ns.NSStringWithContentsOfURLEncoding(ns.NSURLWithGoString("htttypo://example.com"), ns.NSUTF8StringEncoding, &err)
|
|
||||||
if len(err) > 0 {
|
|
||||||
fmt.Printf("err[0] = %p -> %p\n",err[0],err[0].Ptr())
|
|
||||||
fmt.Printf("err: %s\n", err[0].LocalizedDescription())
|
fmt.Printf("err: %s\n", err[0].LocalizedDescription())
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("\nNSStringWithFormat()\n")
|
fmt.Printf("\nNSStringWithFormat()\n")
|
||||||
str := ns.NSStringWithFormat(nst("(%@) (%@)\n(%@)\n"), n2, n3, s1)
|
str := ns.NSStringWithFormat(nst("(%@) (%@)\n(%@)\n"), n2, n3, s1)
|
||||||
|
@ -140,24 +129,4 @@ func main() {
|
||||||
fmt.Printf("--%s\n",o.NSString())
|
fmt.Printf("--%s\n",o.NSString())
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
dir,e := os.Getwd()
|
|
||||||
if e != nil {
|
|
||||||
fmt.Printf("Failed to get current working directory. %s\n",err)
|
|
||||||
os.Exit(-1)
|
|
||||||
}
|
|
||||||
path := nst(dir)
|
|
||||||
filter := ns.NSArrayWithObjects(nst("ast"),nst("yaml"))
|
|
||||||
ost := make([]*ns.NSString,0,1)
|
|
||||||
oar:= make([]*ns.NSArray,0,1)
|
|
||||||
fmt.Printf("\nCompletePathIntoString()\n")
|
|
||||||
i := path.CompletePathIntoString(&ost,0,&oar,filter)
|
|
||||||
fmt.Printf("%d matches\n",i)
|
|
||||||
if i > 0 {
|
|
||||||
fmt.Printf("ost = %s\n",ost[0])
|
|
||||||
fmt.Printf("oar =\n")
|
|
||||||
oar[0].ObjectEnumerator().ForIn(func(o *ns.Id) bool {
|
|
||||||
fmt.Printf("--%s\n",o.NSString())
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ classes:
|
||||||
- NSFileManager
|
- NSFileManager
|
||||||
- NSObject
|
- NSObject
|
||||||
functions: [ NSMakeRange ]
|
functions: [ NSMakeRange ]
|
||||||
enums: [ P_ALL, CF.*, .*StringEncoding ]
|
enums: [ P_ALL, CF.* ]
|
||||||
frameworks: [ Foundation ]
|
frameworks: [ Foundation ]
|
||||||
pragma: [ clang diagnostic ignored "-Wformat-security" ]
|
pragma: [ clang diagnostic ignored "-Wformat-security" ]
|
||||||
vaargs: 32
|
vaargs: 32
|
||||||
|
|
|
@ -103,7 +103,7 @@ func memtest3() {
|
||||||
// check that our string was retained.
|
// check that our string was retained.
|
||||||
|
|
||||||
s1 := arr.ObjectAtIndex(0)
|
s1 := arr.ObjectAtIndex(0)
|
||||||
gstr := s1.NSString().String()
|
gstr := s1.NSString().UTF8String().String()
|
||||||
_ = gstr
|
_ = gstr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,6 @@ func memtest4() {
|
||||||
_ = c1
|
_ = c1
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
time.Sleep(time.Second/50)
|
time.Sleep(time.Second/50)
|
||||||
c1.Free() // you need to manually free UTF8Strings
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,78 +145,22 @@ func memtest5() {
|
||||||
u := sub.UTF8String()
|
u := sub.UTF8String()
|
||||||
u2 := sub2.UTF8String()
|
u2 := sub2.UTF8String()
|
||||||
u3 := sub3.UTF8String()
|
u3 := sub3.UTF8String()
|
||||||
time.Sleep(time.Second/50)
|
|
||||||
runtime.GC()
|
|
||||||
i++
|
|
||||||
u.Free()
|
|
||||||
u2.Free()
|
|
||||||
u3.Free()
|
|
||||||
_ = u
|
_ = u
|
||||||
_ = u2
|
_ = u2
|
||||||
_ = u3
|
_ = u3
|
||||||
|
time.Sleep(time.Second/50)
|
||||||
|
runtime.GC()
|
||||||
|
i++
|
||||||
//fmt.Printf("loop completed\n")
|
//fmt.Printf("loop completed\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func tmpdict(i int) *ns.NSString {
|
|
||||||
o1 := ns.NSStringWithGoString(fmt.Sprintf("temp string 1-%d",i))
|
|
||||||
o2 := ns.NSStringWithGoString(fmt.Sprintf("temp string 2-%d",i))
|
|
||||||
k1 := ns.NSStringWithGoString(fmt.Sprintf("temp key 1-%d",i))
|
|
||||||
k2 := ns.NSStringWithGoString(fmt.Sprintf("temp key 2-%d",i))
|
|
||||||
dict := ns.NSDictionaryWithObjectsAndKeys(o1,k1,o2,k2)
|
|
||||||
ret := dict.ValueForKey(k1)
|
|
||||||
//fmt.Printf("tmpdict(): string = %s\n",ret.NSString())
|
|
||||||
|
|
||||||
defer runtime.GC() // o1, o2, k1, k2, and dict can be released after we return
|
|
||||||
return ret.NSString() // should be retained by NSDictionary.ValueForKey()
|
|
||||||
}
|
|
||||||
|
|
||||||
func tmparr(i int) *ns.NSString {
|
|
||||||
o1 := ns.NSStringWithGoString(fmt.Sprintf("temp string 3-%d",i))
|
|
||||||
o2 := ns.NSStringWithGoString(fmt.Sprintf("temp string 4-%d",i))
|
|
||||||
arr := ns.NSArrayWithObjects(o1,o2)
|
|
||||||
os := make([]*ns.Id,0,2)
|
|
||||||
arr.GetObjects(&os, ns.NSMakeRange(0,2))
|
|
||||||
|
|
||||||
defer runtime.GC() // collect o1, o2 and arr
|
|
||||||
return os[1].NSString() // should have been retained by NSArray.GetObjects()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func memtest6() {
|
|
||||||
fmt.Println("memtest6 started")
|
|
||||||
i := 0
|
|
||||||
for {
|
|
||||||
s1 := tmpdict(i)
|
|
||||||
s2 := tmparr(i)
|
|
||||||
time.Sleep(time.Second / 5)
|
|
||||||
u1 := s1.String() // make sure s1 and s2 are still available
|
|
||||||
u2 := s2.String()
|
|
||||||
e1 := fmt.Sprintf("temp string 1-%d",i)
|
|
||||||
if u1 != e1 {
|
|
||||||
fmt.Printf("tmpdict() error: %s != %s\n",u1,e1)
|
|
||||||
}
|
|
||||||
e2 := fmt.Sprintf("temp string 4-%d",i)
|
|
||||||
if u2 != e2 {
|
|
||||||
fmt.Printf("tmparr() error: %s != %s\n",u2,e2)
|
|
||||||
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Printf("MultiThreaded? %t\n", ns.NSThreadIsMultiThreaded())
|
|
||||||
th := ns.NSThreadNew()
|
|
||||||
th.Start()
|
|
||||||
fmt.Printf("MultiThreaded? %t\n", ns.NSThreadIsMultiThreaded())
|
|
||||||
go memtest1()
|
go memtest1()
|
||||||
go memtest2()
|
go memtest2()
|
||||||
go memtest3()
|
go memtest3()
|
||||||
go memtest4()
|
go memtest4()
|
||||||
go memtest5()
|
go memtest5()
|
||||||
go memtest6()
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
// print a progress indicator
|
// print a progress indicator
|
||||||
|
|
|
@ -5,8 +5,7 @@ classes:
|
||||||
- NSArray
|
- NSArray
|
||||||
- NSMutableArray
|
- NSMutableArray
|
||||||
- NSString
|
- NSString
|
||||||
- NSDictionary
|
- NSObject
|
||||||
- NSThread
|
|
||||||
subclasses:
|
subclasses:
|
||||||
MyClass:
|
MyClass:
|
||||||
NSObject:
|
NSObject:
|
||||||
|
@ -17,4 +16,4 @@ functions:
|
||||||
frameworks:
|
frameworks:
|
||||||
- Foundation
|
- Foundation
|
||||||
pragma: [ clang diagnostic ignored "-Wformat-security" ]
|
pragma: [ clang diagnostic ignored "-Wformat-security" ]
|
||||||
#nogc: true
|
gogc: true
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// An example of manual memory management (nogc directive in nswrap.yaml)
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "C"
|
import "C"
|
||||||
|
@ -39,6 +38,8 @@ func memtest1() {
|
||||||
for {
|
for {
|
||||||
pool := ns.NSAutoreleasePoolAlloc().Init()
|
pool := ns.NSAutoreleasePoolAlloc().Init()
|
||||||
o1 := ns.MyClassAlloc()
|
o1 := ns.MyClassAlloc()
|
||||||
|
//If autorelease: true is set in nswrap.yaml, the manual calls to
|
||||||
|
//autorelease are not necessary.
|
||||||
o1.Autorelease()
|
o1.Autorelease()
|
||||||
o1.DeallocCallback(dealloc)
|
o1.DeallocCallback(dealloc)
|
||||||
o1.ReleaseCallback(release)
|
o1.ReleaseCallback(release)
|
||||||
|
@ -132,9 +133,6 @@ func memtest4() {
|
||||||
go memtest1()
|
go memtest1()
|
||||||
go memtest2()
|
go memtest2()
|
||||||
go memtest3()
|
go memtest3()
|
||||||
go memtest1()
|
|
||||||
go memtest2()
|
|
||||||
go memtest3()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func memtest4a() {
|
func memtest4a() {
|
||||||
|
@ -198,6 +196,9 @@ func main() {
|
||||||
//Within an autorelease pool, do not do anything that can result in a
|
//Within an autorelease pool, do not do anything that can result in a
|
||||||
//switch to a different thread.
|
//switch to a different thread.
|
||||||
|
|
||||||
|
//go memtest1()
|
||||||
|
//go memtest2()
|
||||||
|
//go memtest3()
|
||||||
go memtest4()
|
go memtest4()
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,4 +14,3 @@ subclasses:
|
||||||
frameworks:
|
frameworks:
|
||||||
- Foundation
|
- Foundation
|
||||||
pragma: [ clang diagnostic ignored "-Wformat-security" ]
|
pragma: [ clang diagnostic ignored "-Wformat-security" ]
|
||||||
nogc: true
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package: ClassOne
|
package: ClassOne
|
||||||
inputfiles: [ ClassOne/simple.h ]
|
inputfiles: [ ClassOne/simple.h ]
|
||||||
classes:
|
classes:
|
||||||
- NSObject
|
|
||||||
- ClassOne
|
- ClassOne
|
||||||
- ClassTwo
|
- ClassTwo
|
||||||
subclasses:
|
subclasses:
|
||||||
|
@ -9,7 +8,8 @@ subclasses:
|
||||||
ClassTwo:
|
ClassTwo:
|
||||||
- geti1
|
- geti1
|
||||||
- -(void)myMethod1:(int)a
|
- -(void)myMethod1:(int)a
|
||||||
- -(const NSObject* _Nonnull)myMethod2:(int)a options:(NSDictionary<NSString*,NSObject*> *)opt
|
- +(const NSObject* _Nonnull)myMethod2:(int)a options:(NSDictionary<NSString*,NSObject*> *)opt
|
||||||
|
|
||||||
imports: [ simple.h ]
|
imports: [ simple.h ]
|
||||||
frameworks: [ Foundation ]
|
frameworks: [ Foundation ]
|
||||||
|
#arc: true
|
||||||
|
|
|
@ -1,157 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"git.wow.st/gmp/nswrap/examples/strings/ns"
|
|
||||||
)
|
|
||||||
|
|
||||||
func incr() func(bool) (int, float64) {
|
|
||||||
i := 0
|
|
||||||
b := 0.0
|
|
||||||
var mx sync.Mutex
|
|
||||||
return func(bad bool) (int, float64) {
|
|
||||||
mx.Lock()
|
|
||||||
if bad {
|
|
||||||
b++
|
|
||||||
defer mx.Unlock()
|
|
||||||
} else {
|
|
||||||
defer func() { i++; mx.Unlock() }()
|
|
||||||
}
|
|
||||||
if b == 0 {
|
|
||||||
return i, 0.0
|
|
||||||
} else {
|
|
||||||
return i, (b/float64(i)) * 100
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type tracker struct {
|
|
||||||
add, drop func(*ns.Id)
|
|
||||||
check func()
|
|
||||||
i func(bool) (int, float64)
|
|
||||||
}
|
|
||||||
|
|
||||||
type record struct {
|
|
||||||
ptr unsafe.Pointer
|
|
||||||
goPtr *ns.Id
|
|
||||||
when time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTracker() (func(*ns.Id), func(*ns.Id), func()) {
|
|
||||||
addch := make(chan *ns.Id)
|
|
||||||
dropch := make(chan *ns.Id)
|
|
||||||
data := []record{}
|
|
||||||
var mux sync.Mutex
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case x := <-addch:
|
|
||||||
mux.Lock()
|
|
||||||
data = append(data,record{
|
|
||||||
x.Ptr(),
|
|
||||||
x,
|
|
||||||
time.Now(),
|
|
||||||
})
|
|
||||||
mux.Unlock()
|
|
||||||
case x := <-dropch:
|
|
||||||
mux.Lock()
|
|
||||||
data = append(data,record{
|
|
||||||
nil,
|
|
||||||
x,
|
|
||||||
time.Now(),
|
|
||||||
})
|
|
||||||
mux.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
add := func(x *ns.Id) {
|
|
||||||
addch<- x
|
|
||||||
}
|
|
||||||
drop := func(x *ns.Id) {
|
|
||||||
dropch<- x
|
|
||||||
}
|
|
||||||
check := func() {
|
|
||||||
live := map[unsafe.Pointer]*ns.Id{}
|
|
||||||
bad := false
|
|
||||||
mux.Lock()
|
|
||||||
for _,r := range data {
|
|
||||||
if r.ptr != nil {
|
|
||||||
if live[r.ptr] != nil {
|
|
||||||
fmt.Printf("COLLISION: %p & %p -> %p\n", r.goPtr, live[r.ptr], r.ptr)
|
|
||||||
bad = true
|
|
||||||
}
|
|
||||||
live[r.ptr] = r.goPtr
|
|
||||||
} else {
|
|
||||||
delete(live,r.ptr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Printf("Checked %d records -- ",len(data))
|
|
||||||
if bad {
|
|
||||||
fmt.Printf("failed\n")
|
|
||||||
} else {
|
|
||||||
fmt.Printf("ok\n")
|
|
||||||
}
|
|
||||||
mux.Unlock()
|
|
||||||
}
|
|
||||||
return add,drop,check
|
|
||||||
}
|
|
||||||
|
|
||||||
func mkstrings(t tracker) {
|
|
||||||
for {
|
|
||||||
//fmt.Printf("main thread: %t\n",ns.NSThreadIsMainThread())
|
|
||||||
x,b := t.i(false)
|
|
||||||
str := fmt.Sprintf("string %d",x)
|
|
||||||
s := ns.NSStringWithGoString(str)
|
|
||||||
//t.add(&s.Id)
|
|
||||||
for j := 0; j < 10; j++ {
|
|
||||||
sout := s.String()
|
|
||||||
if str != sout {
|
|
||||||
_,b = t.i(true)
|
|
||||||
fmt.Printf("%3.2f%% -- %d: '%s' '%s'\n", b, x, str, sout)
|
|
||||||
}
|
|
||||||
time.Sleep(time.Second/1000)
|
|
||||||
}
|
|
||||||
if x % 1000 == 0 {
|
|
||||||
fmt.Printf("%3.2f%% -- %s\n", b, time.Now().Format("03:04:05.000"))
|
|
||||||
}
|
|
||||||
//t.drop(&s.Id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
runtime.GOMAXPROCS(4)
|
|
||||||
fmt.Printf("Starting\n")
|
|
||||||
//ns.NSThreadNew().Start()
|
|
||||||
fmt.Printf("multithreaded: %t\n", ns.NSThreadIsMultiThreaded())
|
|
||||||
//pool := ns.NSAutoreleasePoolAlloc()
|
|
||||||
add, drop, check := newTracker()
|
|
||||||
i := tracker{add, drop, check, incr()}
|
|
||||||
go mkstrings(i)
|
|
||||||
go mkstrings(i)
|
|
||||||
go mkstrings(i)
|
|
||||||
go mkstrings(i)
|
|
||||||
go mkstrings(i)
|
|
||||||
go mkstrings(i)
|
|
||||||
go mkstrings(i)
|
|
||||||
go mkstrings(i)
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
runtime.GC()
|
|
||||||
time.Sleep(time.Second/100)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
time.Sleep(time.Second * 600)
|
|
||||||
i.check()
|
|
||||||
|
|
||||||
//pool.Drain()
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
inputfiles:
|
|
||||||
- /System/Library/Frameworks/Foundation.framework/Headers/Foundation.h
|
|
||||||
classes:
|
|
||||||
- NSString
|
|
||||||
- NSThread
|
|
||||||
frameworks:
|
|
||||||
- Foundation
|
|
||||||
pragma: [ clang diagnostic ignored "-Wformat-security" ]
|
|
||||||
#nogc: true
|
|
24
main.go
24
main.go
|
@ -19,12 +19,6 @@ import (
|
||||||
var Debug = false
|
var Debug = false
|
||||||
var Profile = false
|
var Profile = false
|
||||||
|
|
||||||
//automatically add interfaces if they are found in the input interface
|
|
||||||
//declarations
|
|
||||||
var autoadd = []string{
|
|
||||||
"NSObject",
|
|
||||||
}
|
|
||||||
|
|
||||||
type conf struct {
|
type conf struct {
|
||||||
Positions bool
|
Positions bool
|
||||||
Package string
|
Package string
|
||||||
|
@ -45,7 +39,7 @@ type conf struct {
|
||||||
//Arc flag for debugging only, builds will break
|
//Arc flag for debugging only, builds will break
|
||||||
Arc bool
|
Arc bool
|
||||||
Autorelease bool
|
Autorelease bool
|
||||||
Nogc bool
|
Gogc bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var Config conf
|
var Config conf
|
||||||
|
@ -257,20 +251,19 @@ func Start() (err error) {
|
||||||
if Config.Positions {
|
if Config.Positions {
|
||||||
ast.TrackPositions = true
|
ast.TrackPositions = true
|
||||||
}
|
}
|
||||||
wrap.Gogc = true
|
if Config.Gogc && Config.Autorelease {
|
||||||
|
fmt.Printf("Cannot use Autorelease and Gogc directives at the same time\n")
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
if Config.Arc {
|
if Config.Arc {
|
||||||
wrap.Arc = true
|
wrap.Arc = true
|
||||||
wrap.Gogc = false
|
|
||||||
}
|
}
|
||||||
if Config.Autorelease {
|
if Config.Autorelease {
|
||||||
wrap.Autorelease = true
|
wrap.Autorelease = true
|
||||||
wrap.Gogc = false
|
|
||||||
}
|
}
|
||||||
if Config.Nogc {
|
if Config.Gogc {
|
||||||
wrap.Gogc = false
|
wrap.Gogc = true
|
||||||
}
|
}
|
||||||
|
|
||||||
//NOTE: converting in parallel is slower on my system
|
//NOTE: converting in parallel is slower on my system
|
||||||
//nodes := convertLinesToNodesParallel(lines)
|
//nodes := convertLinesToNodesParallel(lines)
|
||||||
nodes := convertLinesToNodes(lines)
|
nodes := convertLinesToNodes(lines)
|
||||||
|
@ -304,9 +297,6 @@ func Start() (err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if matches(x.Name, autoadd) {
|
|
||||||
Config.Classes = append(Config.Classes,x.Name)
|
|
||||||
}
|
|
||||||
case *ast.ObjCCategoryDecl:
|
case *ast.ObjCCategoryDecl:
|
||||||
w.AddCategory(x)
|
w.AddCategory(x)
|
||||||
case *ast.TypedefDecl:
|
case *ast.TypedefDecl:
|
||||||
|
|
|
@ -7,8 +7,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Gogc bool
|
|
||||||
|
|
||||||
//super is a map recording which class is the parent of each other class
|
//super is a map recording which class is the parent of each other class
|
||||||
var super map[string]string
|
var super map[string]string
|
||||||
|
|
||||||
|
@ -444,7 +442,7 @@ func GoToC(sname, name string, pnames, snames []string, rtype *Type, ptypes []*T
|
||||||
case TypedefShouldWrap(ptgt) && !pt.Variadic && !fun:
|
case TypedefShouldWrap(ptgt) && !pt.Variadic && !fun:
|
||||||
p = pn + ".Ptr()"
|
p = pn + ".Ptr()"
|
||||||
case snames[i] != "":
|
case snames[i] != "":
|
||||||
p = "(*unsafe.Pointer)(unsafe.Pointer(&" + snames[i] + "[0]))"
|
p = "unsafe.Pointer(&" + snames[i] + "[0])"
|
||||||
case pt.Variadic:
|
case pt.Variadic:
|
||||||
p = "unsafe.Pointer(&" + p + ")"
|
p = "unsafe.Pointer(&" + p + ")"
|
||||||
case pt.IsPointer() && !fun:
|
case pt.IsPointer() && !fun:
|
||||||
|
@ -480,13 +478,6 @@ func GoToC(sname, name string, pnames, snames []string, rtype *Type, ptypes []*T
|
||||||
if IsGoInterface(ptgt) {
|
if IsGoInterface(ptgt) {
|
||||||
ptgt = "Id"
|
ptgt = "Id"
|
||||||
}
|
}
|
||||||
dogc := ""
|
|
||||||
if Gogc {
|
|
||||||
dogc = fmt.Sprintf(`
|
|
||||||
runtime.SetFinalizer((*%s)[i], func(o *%s) {
|
|
||||||
o.Release()
|
|
||||||
})`, pnames[i], ptgt)
|
|
||||||
}
|
|
||||||
ret.WriteString(fmt.Sprintf(`
|
ret.WriteString(fmt.Sprintf(`
|
||||||
(*%s) = (*%s)[:cap(*%s)]
|
(*%s) = (*%s)[:cap(*%s)]
|
||||||
for i := 0; i < len(*%s); i++ {
|
for i := 0; i < len(*%s); i++ {
|
||||||
|
@ -495,10 +486,10 @@ func GoToC(sname, name string, pnames, snames []string, rtype *Type, ptypes []*T
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if (*%s)[i] == nil {
|
if (*%s)[i] == nil {
|
||||||
(*%s)[i] = &%s{}%s
|
(*%s)[i] = &%s{}
|
||||||
}
|
}
|
||||||
(*%s)[i].ptr = %s[i]
|
(*%s)[i].ptr = %s[i]
|
||||||
}`, pnames[i], pnames[i], pnames[i], pnames[i], sname, pnames[i], pnames[i], pnames[i], pnames[i], ptgt, dogc, pnames[i], sname))
|
}`, pnames[i], pnames[i], pnames[i], pnames[i], sname, pnames[i], pnames[i], pnames[i], pnames[i], ptgt, pnames[i], sname))
|
||||||
}
|
}
|
||||||
if rt != "void" {
|
if rt != "void" {
|
||||||
cmp := ""
|
cmp := ""
|
||||||
|
|
|
@ -217,7 +217,9 @@ type NSObject interface {
|
||||||
type NSString struct { Id }
|
type NSString struct { Id }
|
||||||
func (o *NSString) Ptr() unsafe.Pointer { if o == nil { return nil }; return o.ptr }
|
func (o *NSString) Ptr() unsafe.Pointer { if o == nil { return nil }; return o.ptr }
|
||||||
func (o *Id) NSString() *NSString {
|
func (o *Id) NSString() *NSString {
|
||||||
return (*NSString)(unsafe.Pointer(o))
|
ret := &NSString{}
|
||||||
|
ret.ptr = o.ptr
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
str = "int(void)"
|
str = "int(void)"
|
||||||
|
@ -274,7 +276,7 @@ func (o *Id) NSString() *NSString {
|
||||||
snames := []string{"", "", "", ""}
|
snames := []string{"", "", "", ""}
|
||||||
|
|
||||||
chk_gotoc := func(expected string) {
|
chk_gotoc := func(expected string) {
|
||||||
chk(GoToC("myFun", "myFun", pnames, snames, rtype, ptypes, false, false, false), expected)
|
chk(GoToC("myFun", pnames, snames, rtype, ptypes, false, false, false), expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
chk_gotoc("")
|
chk_gotoc("")
|
||||||
|
@ -296,16 +298,12 @@ func (o *Id) NSString() *NSString {
|
||||||
chk_gotoc(
|
chk_gotoc(
|
||||||
`ret := &NSString{}
|
`ret := &NSString{}
|
||||||
ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), (C.int)(p3), unsafe.Pointer(p4)))
|
ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), (C.int)(p3), unsafe.Pointer(p4)))
|
||||||
if ret.ptr == nil { return ret }
|
|
||||||
if ret.ptr == o.ptr { return (*NSString)(unsafe.Pointer(o)) }
|
|
||||||
return ret`)
|
return ret`)
|
||||||
|
|
||||||
rtype = nsop
|
rtype = nsop
|
||||||
chk_gotoc(
|
chk_gotoc(
|
||||||
`ret := &Id{}
|
`ret := &Id{}
|
||||||
ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), (C.int)(p3), unsafe.Pointer(p4)))
|
ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), (C.int)(p3), unsafe.Pointer(p4)))
|
||||||
if ret.ptr == nil { return ret }
|
|
||||||
if ret.ptr == o.ptr { return (*Id)(unsafe.Pointer(o)) }
|
|
||||||
return ret`)
|
return ret`)
|
||||||
|
|
||||||
ptypes[1].Variadic = true
|
ptypes[1].Variadic = true
|
||||||
|
@ -313,8 +311,6 @@ func (o *Id) NSString() *NSString {
|
||||||
chk_gotoc(
|
chk_gotoc(
|
||||||
`ret := &Id{}
|
`ret := &Id{}
|
||||||
ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), unsafe.Pointer(&p2), (C.int)(p3), unsafe.Pointer(p4)))
|
ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), unsafe.Pointer(&p2), (C.int)(p3), unsafe.Pointer(p4)))
|
||||||
if ret.ptr == nil { return ret }
|
|
||||||
if ret.ptr == o.ptr { return (*Id)(unsafe.Pointer(o)) }
|
|
||||||
return ret`)
|
return ret`)
|
||||||
|
|
||||||
ptypes[1].Variadic = false
|
ptypes[1].Variadic = false
|
||||||
|
@ -322,7 +318,7 @@ func (o *Id) NSString() *NSString {
|
||||||
ptypes[1] = nsopp
|
ptypes[1] = nsopp
|
||||||
chk_gotoc(
|
chk_gotoc(
|
||||||
`ret := &Id{}
|
`ret := &Id{}
|
||||||
ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), (*unsafe.Pointer)(unsafe.Pointer(&p2p[0])), (C.int)(p3), unsafe.Pointer(p4)))
|
ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), unsafe.Pointer(&p2p[0]), (C.int)(p3), unsafe.Pointer(p4)))
|
||||||
(*p2) = (*p2)[:cap(*p2)]
|
(*p2) = (*p2)[:cap(*p2)]
|
||||||
for i := 0; i < len(*p2); i++ {
|
for i := 0; i < len(*p2); i++ {
|
||||||
if p2p[i] == nil {
|
if p2p[i] == nil {
|
||||||
|
@ -334,8 +330,6 @@ func (o *Id) NSString() *NSString {
|
||||||
}
|
}
|
||||||
(*p2)[i].ptr = p2p[i]
|
(*p2)[i].ptr = p2p[i]
|
||||||
}
|
}
|
||||||
if ret.ptr == nil { return ret }
|
|
||||||
if ret.ptr == o.ptr { return (*Id)(unsafe.Pointer(o)) }
|
|
||||||
return ret`)
|
return ret`)
|
||||||
snames[1] = ""
|
snames[1] = ""
|
||||||
snames[2] = "p3p"
|
snames[2] = "p3p"
|
||||||
|
@ -343,7 +337,7 @@ func (o *Id) NSString() *NSString {
|
||||||
ptypes[2] = nstpp
|
ptypes[2] = nstpp
|
||||||
chk_gotoc(
|
chk_gotoc(
|
||||||
`ret := &Id{}
|
`ret := &Id{}
|
||||||
ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), (*unsafe.Pointer)(unsafe.Pointer(&p3p[0])), unsafe.Pointer(p4)))
|
ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), unsafe.Pointer(&p3p[0]), unsafe.Pointer(p4)))
|
||||||
(*p3) = (*p3)[:cap(*p3)]
|
(*p3) = (*p3)[:cap(*p3)]
|
||||||
for i := 0; i < len(*p3); i++ {
|
for i := 0; i < len(*p3); i++ {
|
||||||
if p3p[i] == nil {
|
if p3p[i] == nil {
|
||||||
|
@ -355,13 +349,11 @@ func (o *Id) NSString() *NSString {
|
||||||
}
|
}
|
||||||
(*p3)[i].ptr = p3p[i]
|
(*p3)[i].ptr = p3p[i]
|
||||||
}
|
}
|
||||||
if ret.ptr == nil { return ret }
|
|
||||||
if ret.ptr == o.ptr { return (*Id)(unsafe.Pointer(o)) }
|
|
||||||
return ret`)
|
return ret`)
|
||||||
|
|
||||||
chk(GoToC("myFun", "myFun", pnames, snames, rtype, ptypes, true, false, false),
|
chk(GoToC("myFun", pnames, snames, rtype, ptypes, true, false, false),
|
||||||
`ret := &Id{}
|
`ret := &Id{}
|
||||||
ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), (*unsafe.Pointer)(unsafe.Pointer(&p3p[0])), p4))
|
ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), unsafe.Pointer(&p3p[0]), p4))
|
||||||
(*p3) = (*p3)[:cap(*p3)]
|
(*p3) = (*p3)[:cap(*p3)]
|
||||||
for i := 0; i < len(*p3); i++ {
|
for i := 0; i < len(*p3); i++ {
|
||||||
if p3p[i] == nil {
|
if p3p[i] == nil {
|
||||||
|
@ -373,7 +365,5 @@ func (o *Id) NSString() *NSString {
|
||||||
}
|
}
|
||||||
(*p3)[i].ptr = p3p[i]
|
(*p3)[i].ptr = p3p[i]
|
||||||
}
|
}
|
||||||
if ret.ptr == nil { return ret }
|
|
||||||
if ret.ptr == o.ptr { return (*Id)(unsafe.Pointer(o)) }
|
|
||||||
return ret`)
|
return ret`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func dbg(f string, xs ...interface{}) {
|
func dbg(f string, xs ...interface{}) {
|
||||||
if Debug {
|
if Debug && false {
|
||||||
fmt.Printf(f, xs...)
|
fmt.Printf(f, xs...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
130
wrap/main.go
130
wrap/main.go
|
@ -70,7 +70,7 @@ func NewWrapper(debug bool) *Wrapper {
|
||||||
}
|
}
|
||||||
ret.goImports["unsafe"] = true
|
ret.goImports["unsafe"] = true
|
||||||
if Gogc {
|
if Gogc {
|
||||||
types.Gogc = true
|
ret.goImports["runtime"] = true
|
||||||
}
|
}
|
||||||
ret.goTypes.WriteString(`
|
ret.goTypes.WriteString(`
|
||||||
type Id struct {
|
type Id struct {
|
||||||
|
@ -152,8 +152,6 @@ type Method struct {
|
||||||
Unavailable bool
|
Unavailable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShouldFinalize returns true on a method that returns an object that should
|
|
||||||
// have a GC finalizer.
|
|
||||||
func (m *Method) ShouldFinalize() bool {
|
func (m *Method) ShouldFinalize() bool {
|
||||||
grtype := m.Type.GoType()
|
grtype := m.Type.GoType()
|
||||||
return Gogc && grtype != "NSAutoreleasePool" &&
|
return Gogc && grtype != "NSAutoreleasePool" &&
|
||||||
|
@ -162,8 +160,6 @@ func (m *Method) ShouldFinalize() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsRetained returns true if a given instance method returns a retained object.
|
// IsRetained returns true if a given instance method returns a retained object.
|
||||||
// NSWrap will not send a 'retain' message to these objects before returning
|
|
||||||
// them to Go.
|
|
||||||
func IsRetained(name string) bool {
|
func IsRetained(name string) bool {
|
||||||
return (
|
return (
|
||||||
(len(name) >= 3 && name[:3] == "new") ||
|
(len(name) >= 3 && name[:3] == "new") ||
|
||||||
|
@ -304,14 +300,10 @@ func (w Wrapper) cparamlist(m *Method) (string, string, string) {
|
||||||
}
|
}
|
||||||
for _, p := range m.Parameters {
|
for _, p := range m.Parameters {
|
||||||
var tp string
|
var tp string
|
||||||
gt := p.Type.GoType()
|
wp := types.ShouldWrap(p.Type.GoType())
|
||||||
wp := types.ShouldWrap(gt)
|
if wp || p.Type.IsPointer() || p.Type.Variadic {
|
||||||
switch {
|
|
||||||
case len(gt) > 2 && gt[:1] == "*" && types.PtrShouldWrap(gt[1:]):
|
|
||||||
tp = "void**"
|
|
||||||
case wp || p.Type.IsPointer() || p.Type.Variadic:
|
|
||||||
tp = "void*"
|
tp = "void*"
|
||||||
default:
|
} else {
|
||||||
tp = p.Type.CType()
|
tp = p.Type.CType()
|
||||||
}
|
}
|
||||||
ns = append(ns, p.Vname)
|
ns = append(ns, p.Vname)
|
||||||
|
@ -327,28 +319,21 @@ func (w Wrapper) objcparamlist(m *Method) string {
|
||||||
}
|
}
|
||||||
first := true
|
first := true
|
||||||
ret := []string{}
|
ret := []string{}
|
||||||
pname := ""
|
|
||||||
for _, p := range m.Parameters {
|
for _, p := range m.Parameters {
|
||||||
gt := p.Type.GoType()
|
if first && !p.Type.Variadic {
|
||||||
if first {
|
ret = append(ret, m.Name+":"+p.Vname)
|
||||||
first = false
|
first = false
|
||||||
pname = m.Name
|
|
||||||
} else {
|
} else {
|
||||||
pname = p.Pname
|
if p.Type.Variadic {
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case len(gt) > 2 && gt[:1] == "*" && types.PtrShouldWrap(gt[1:]):
|
|
||||||
ret = append(ret, pname+":("+p.Type.Node.CType()+")"+p.Vname)
|
|
||||||
case !p.Type.Variadic:
|
|
||||||
ret = append(ret, pname+":"+p.Vname)
|
|
||||||
first = false
|
|
||||||
case p.Type.Variadic:
|
|
||||||
str := []string{p.Pname + ", arr[0]"}
|
str := []string{p.Pname + ", arr[0]"}
|
||||||
for i := 1; i < w.Vaargs; i++ {
|
for i := 1; i < w.Vaargs; i++ {
|
||||||
str = append(str, "arr["+strconv.Itoa(i)+"]")
|
str = append(str, "arr["+strconv.Itoa(i)+"]")
|
||||||
}
|
}
|
||||||
str = append(str, "nil")
|
str = append(str, "nil")
|
||||||
ret = append(ret, strings.Join(str, ", "))
|
ret = append(ret, strings.Join(str, ", "))
|
||||||
|
} else {
|
||||||
|
ret = append(ret, p.Pname+":"+p.Vname)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return strings.Join(ret, " ")
|
return strings.Join(ret, " ")
|
||||||
|
@ -362,13 +347,11 @@ var goreserved map[string]bool = map[string]bool{
|
||||||
"len": true,
|
"len": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wrapper) gpntp(m *Method) ([]string, []string, []string, []*types.Type, string) {
|
func (w *Wrapper) gpntp(m *Method) ([]string, []string, []*types.Type, string) {
|
||||||
ns := []string{}
|
ns := []string{}
|
||||||
pnames := []string{}
|
|
||||||
tps := []*types.Type{}
|
tps := []*types.Type{}
|
||||||
if !m.ClassMethod {
|
if !m.ClassMethod {
|
||||||
ns = append(ns, "o")
|
ns = append(ns, "o")
|
||||||
pnames = append(pnames, m.Name)
|
|
||||||
tps = append(tps, types.NewTypeFromString(m.Class+"*", ""))
|
tps = append(tps, types.NewTypeFromString(m.Class+"*", ""))
|
||||||
}
|
}
|
||||||
for i, p := range m.Parameters {
|
for i, p := range m.Parameters {
|
||||||
|
@ -380,7 +363,6 @@ func (w *Wrapper) gpntp(m *Method) ([]string, []string, []string, []*types.Type,
|
||||||
gname = fmt.Sprintf("p%d",i)
|
gname = fmt.Sprintf("p%d",i)
|
||||||
}
|
}
|
||||||
ns = append(ns, gname)
|
ns = append(ns, gname)
|
||||||
pnames = append(pnames, p.Pname)
|
|
||||||
tps = append(tps, p.Type)
|
tps = append(tps, p.Type)
|
||||||
}
|
}
|
||||||
w.processTypes(tps)
|
w.processTypes(tps)
|
||||||
|
@ -412,7 +394,7 @@ func (w *Wrapper) gpntp(m *Method) ([]string, []string, []string, []*types.Type,
|
||||||
}
|
}
|
||||||
ret = append(ret, ns[i]+" "+gt)
|
ret = append(ret, ns[i]+" "+gt)
|
||||||
}
|
}
|
||||||
return ns, pnames, snames, tps, strings.Join(ret, ", ")
|
return ns, snames, tps, strings.Join(ret, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
type Interface struct {
|
type Interface struct {
|
||||||
|
@ -901,17 +883,11 @@ func (c *Char) Free() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wrapper) StringHelpers() {
|
func (w *Wrapper) StringHelpers() {
|
||||||
ufree := ""
|
w.goHelpers.WriteString(`
|
||||||
if Gogc {
|
|
||||||
ufree = "utf8.Free()\n\t"
|
|
||||||
}
|
|
||||||
w.goHelpers.WriteString(fmt.Sprintf(`
|
|
||||||
func (o *NSString) String() string {
|
func (o *NSString) String() string {
|
||||||
utf8 := o.UTF8String()
|
return o.UTF8String().String()
|
||||||
ret := utf8.String()
|
|
||||||
%sreturn ret
|
|
||||||
}
|
}
|
||||||
`,ufree))
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wrapper) EnumeratorHelpers() {
|
func (w *Wrapper) EnumeratorHelpers() {
|
||||||
|
@ -1067,7 +1043,7 @@ func (w *Wrapper) _processMethod(m *Method, fun bool) {
|
||||||
} else {
|
} else {
|
||||||
cmtype = m.Type.CTypeAttrib()
|
cmtype = m.Type.CTypeAttrib()
|
||||||
}
|
}
|
||||||
ns, pnames, snames, tps, gplist := w.gpntp(m)
|
ns, snames, tps, gplist := w.gpntp(m)
|
||||||
if gname == grtype { // avoid name conflicts between methods and types
|
if gname == grtype { // avoid name conflicts between methods and types
|
||||||
gname = "Get" + gname
|
gname = "Get" + gname
|
||||||
}
|
}
|
||||||
|
@ -1155,8 +1131,7 @@ func %s%s(%s) %s {
|
||||||
}`, cret, cobj, w.objcparamlist(m)))
|
}`, cret, cobj, w.objcparamlist(m)))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
//if Gogc && !m.isVoid() {
|
if Gogc && !m.isVoid() {
|
||||||
if Gogc {
|
|
||||||
rtn := ""
|
rtn := ""
|
||||||
if types.PtrShouldWrap(m.Type.GoType()) {
|
if types.PtrShouldWrap(m.Type.GoType()) {
|
||||||
switch {
|
switch {
|
||||||
|
@ -1164,7 +1139,7 @@ func %s%s(%s) %s {
|
||||||
if grtype != "*NSAutoreleasePool" && constructor {
|
if grtype != "*NSAutoreleasePool" && constructor {
|
||||||
// retain objects returned by class constructor methods
|
// retain objects returned by class constructor methods
|
||||||
rtn = `
|
rtn = `
|
||||||
if(ret != nil) { [ret retain]; }`
|
[ret retain];`
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not retain new, alloc, init and copy methods
|
// do not retain new, alloc, init and copy methods
|
||||||
|
@ -1174,48 +1149,19 @@ func %s%s(%s) %s {
|
||||||
// by default, for instance methods, retain
|
// by default, for instance methods, retain
|
||||||
// if returning a new object
|
// if returning a new object
|
||||||
rtn = `
|
rtn = `
|
||||||
if (ret != nil && ret != o) { [ret retain]; }`
|
if (o != ret) { [ret retain]; }`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rtns := []string{}
|
var ar1, ar2 string
|
||||||
// for pointers to pointers, assume length 1 unless there is a
|
if constructor || true {
|
||||||
// parameter named "range" or "count".
|
ar1 = "@autoreleasepool {\n\t\t"
|
||||||
rlength := "i<1"
|
ar2 = "\n }"
|
||||||
for i,n := range pnames {
|
|
||||||
vn := strings.ReplaceAll(ns[i],"_","")
|
|
||||||
if n == "range" {
|
|
||||||
rlength = "i<" + vn + ".length"
|
|
||||||
}
|
|
||||||
if n == "count" {
|
|
||||||
rlength = "i<" + vn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i,n := range ns {
|
|
||||||
if snames[i] == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
rtns = append(rtns,fmt.Sprintf(`
|
|
||||||
for(int i=0;%s;i++) {
|
|
||||||
if(%s[i] == 0) { break; }
|
|
||||||
[(id)%s[i] retain];
|
|
||||||
}
|
|
||||||
`, rlength, n, n))
|
|
||||||
}
|
|
||||||
var retdecl, reteq, retretn, dup1, dup2 string
|
|
||||||
if !m.isVoid() {
|
|
||||||
retdecl = fmt.Sprintf("%s ret;\n\t", m.Type.CTypeAttrib())
|
|
||||||
reteq = "ret = "
|
|
||||||
retretn = "\n\treturn ret;\n"
|
|
||||||
if m.Type.CType() == "char*" {
|
|
||||||
dup1 = "strdup("
|
|
||||||
dup2 = ")"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
w.cCode.WriteString(fmt.Sprintf(
|
w.cCode.WriteString(fmt.Sprintf(
|
||||||
` %s@autoreleasepool {
|
` %s ret;
|
||||||
%s%s[%s %s]%s;%s%s
|
%sret = [%s %s];%s%s
|
||||||
}%s
|
return ret;
|
||||||
}`, retdecl, reteq, dup1, cobj, w.objcparamlist(m), dup2, rtn, strings.Join(rtns,"\n\t"), retretn))
|
}`, m.Type.CTypeAttrib(), ar1, cobj, w.objcparamlist(m), rtn, ar2))
|
||||||
} else {
|
} else {
|
||||||
w.cCode.WriteString(fmt.Sprintf(` %s[%s %s];
|
w.cCode.WriteString(fmt.Sprintf(` %s[%s %s];
|
||||||
}`, cret, cobj, w.objcparamlist(m)))
|
}`, cret, cobj, w.objcparamlist(m)))
|
||||||
|
@ -1236,7 +1182,6 @@ func %s%s(%s) %s {
|
||||||
dbg2 = fmt.Sprintf(`fmt.Printf("GC finalizer (%s): release %%p -> %%p\n", o, o.ptr)
|
dbg2 = fmt.Sprintf(`fmt.Printf("GC finalizer (%s): release %%p -> %%p\n", o, o.ptr)
|
||||||
`, cls)
|
`, cls)
|
||||||
}
|
}
|
||||||
w.goImports["runtime"] = true
|
|
||||||
w.goCode.WriteString(fmt.Sprintf(`
|
w.goCode.WriteString(fmt.Sprintf(`
|
||||||
func (o *%s) GC() {
|
func (o *%s) GC() {
|
||||||
if o.ptr == nil { return }
|
if o.ptr == nil { return }
|
||||||
|
@ -1971,7 +1916,7 @@ func %s%s(%s)%s {
|
||||||
gocode.WriteString(fmt.Sprintf(`
|
gocode.WriteString(fmt.Sprintf(`
|
||||||
func (o *%s) Super%s(%s) %s {
|
func (o *%s) Super%s(%s) %s {
|
||||||
`, gname, gnames[i], strings.Join(earglist[1:], ", "), grtype))
|
`, gname, gnames[i], strings.Join(earglist[1:], ", "), grtype))
|
||||||
ns, _, snames, tps, _ := w.gpntp(m)
|
ns, snames, tps, _ := w.gpntp(m)
|
||||||
lparm := len(tps) - 1
|
lparm := len(tps) - 1
|
||||||
if len(tps) > 0 && tps[lparm].Variadic {
|
if len(tps) > 0 && tps[lparm].Variadic {
|
||||||
vn := ns[lparm]
|
vn := ns[lparm]
|
||||||
|
@ -2212,31 +2157,14 @@ func (w *Wrapper) Wrap(toproc []string) {
|
||||||
for k := range w.goImports {
|
for k := range w.goImports {
|
||||||
imports = append(imports,"\t\"" + k + "\"")
|
imports = append(imports,"\t\"" + k + "\"")
|
||||||
}
|
}
|
||||||
startThread := ""
|
|
||||||
goInit := ""
|
|
||||||
if w.Interfaces["NSThread"] != nil {
|
|
||||||
startThread = `
|
|
||||||
void
|
|
||||||
NSWrap_init() {
|
|
||||||
[[NSThread new] start]; // put the runtime into multi-threaded mode
|
|
||||||
}
|
|
||||||
`
|
|
||||||
goInit = `
|
|
||||||
func init() {
|
|
||||||
C.NSWrap_init()
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
of.WriteString(fmt.Sprintf(`
|
of.WriteString(fmt.Sprintf(`
|
||||||
%s
|
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
%s
|
%s
|
||||||
)
|
)
|
||||||
%s
|
`,strings.Join(imports,"\n")))
|
||||||
`,startThread,strings.Join(imports,"\n"),goInit))
|
|
||||||
of.WriteString(w.goTypes.String())
|
of.WriteString(w.goTypes.String())
|
||||||
of.WriteString(w.goConst.String())
|
of.WriteString(w.goConst.String())
|
||||||
of.WriteString(w.goHelpers.String())
|
of.WriteString(w.goHelpers.String())
|
||||||
|
|
Loading…
Reference in New Issue
Block a user