Compare commits
	
		
			2 Commits
		
	
	
		
			4aa1211d73
			...
			acc8cab583
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| acc8cab583 | |||
| b5773b5525 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							|  | @ -16,3 +16,5 @@ 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,6 +62,8 @@ 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 | ||||||
|  | @ -131,14 +133,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 { } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
|  | @ -156,11 +158,45 @@ 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 a `String()` | `*Char` in Go. As demonstrated above, NSWrap also provides `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. | Go interface and therefore can be sent directly to functions like `fmt.Printf`. | ||||||
|  | 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 | ||||||
| 
 | 
 | ||||||
|  | @ -171,14 +207,23 @@ follows: | ||||||
| type Id struct { | type Id struct { | ||||||
| 	ptr unsafe.Pointer | 	ptr unsafe.Pointer | ||||||
| } | } | ||||||
| func (o Id) Ptr() unsafe.Pointer { return o.ptr } | func (o *Id) Ptr() unsafe.Pointer { if o == nil { return nil }; 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 implement `NSObject`. | and therefore contain an `unsafe.Pointer` to an Objective-C object, and | ||||||
|  | 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 | ||||||
| 
 | 
 | ||||||
|  | @ -190,7 +235,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, | ||||||
|  | @ -204,70 +249,73 @@ implements the required delegate protocol. | ||||||
| 
 | 
 | ||||||
| * Inheritance | * Inheritance | ||||||
| 
 | 
 | ||||||
| Objective-C only provides single inheritance. In Go, this is modeled using | Objective-C allows single inheritance. NSWrap automatically adds | ||||||
| embedding. Top level objects that inherit from `NSObject` in Objective-C | inherited methods to classes that are includled in your binding. | ||||||
|  | 
 | ||||||
|  | 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 superclass. For example: | Other objects embed their direct superclass. For example: | ||||||
| 
 | 
 | ||||||
| ```go | ```go | ||||||
| type NSArray struct { Id } | type NSArray struct { Id } | ||||||
| func (o NSArray) Ptr() unsafe.Pointer { return o.ptr } | func (o *NSArray) Ptr() unsafe.Pointer { if o == nil { return nil }; return o.ptr } | ||||||
| func (o Id) NSArray() NSArray { | func (o *Id) NSArray() *NSArray { | ||||||
| 	ret := NSArray{} | 	return (*NSArray)(unsafe.Pointer(o)) | ||||||
| 	ret.ptr = o.ptr |  | ||||||
| 	return ret |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type NSMutableArray struct { NSArray } | type NSMutableArray struct { NSArray } | ||||||
| func (o NSMutableArray) Ptr() unsafe.Pointer { return o.ptr } | func (o *NSMutableArray) Ptr() unsafe.Pointer { if o == nil { return nil }; 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)) // Method of NSView | b.InitWithFrame(ns.NSMakeRect(100,100,200,200)) | ||||||
| b.SetTitle(nst("PUSH"))                         // Method of NSButton | b.SetTitle(nst("PUSH")) | ||||||
| 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, | ||||||
| there is no `InitWithFrame` method for receivers of this type. This is | the `initWithFrame` method is defined in AppKit for `NSView`. NSWrap will find this | ||||||
| not necessary because `NSButton` embeds `NSControl` which in turn embeds | method and add it to the Go `NSButton` type when creating your wrapper because | ||||||
| `NSView`. The `InitWithFrame` method only needs to be implemented for `NSView` | `NSButton` inherits from `NSControl` which inherits from `NSView`. | ||||||
| receivers. Go will automatically find the indirectly embedded `NSView` and |  | ||||||
| call the right method. |  | ||||||
| 
 | 
 | ||||||
| Note that, since `InitWithFrame()` is defined only for `NSView` and returns | As of this | ||||||
| an `NSView` type, | writing, on MacOS 10.13.6, NSWrap binds 115 instance methods for `NSObject`, | ||||||
| the following will not work. Look out for this if you like to chain your | so things like `Hash()`, `IsEqualTo()`, `ClassName()`, `RespondsToSelector()` | ||||||
| `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. | ||||||
| 
 | 
 | ||||||
| Go does not perform the same type | All objects implement the `NSObject` interface, but from time to time you | ||||||
| magic when you use variables as function or method parameters. | will encounter a method that takes a parameter of a different type that may | ||||||
| If you want to pass your `NSButton` as a parameter to a method that accepts | not exactly match the type you have. For example, if you want to pass your | ||||||
| an `NSView` type, you need to explicitly pass the embedded `NSView` | `NSButton` as a parameter to a method that accepts an `NSView` type, you need | ||||||
| (`b.NSView` in the example above). | to explicitly pass its embedded `NSView` (`&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, which | at run-time to any other class. You will need this for Enumerators and | ||||||
| always return `Id`. See below under Enumerators for an example, but make | functions like `NSArray`'s `GetObjects`, for example, | ||||||
|  | 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 | You can implement a version of a Go type switch this way: | ||||||
| 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 | ||||||
|  | @ -314,11 +362,12 @@ 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 a nil-terminated array of | the CGo call, the method parameter will be treated as an 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. | truncated to the appropriate length. If there is no output, the length will | ||||||
|  | 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`: | ||||||
|  | @ -329,28 +378,31 @@ 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")), | ||||||
|         ) |         ) | ||||||
|         os,ks := make([]ns.Id,0,5), make([]ns.Id,0,5)  // length 0, capacity 5 slices |         va,ka := make([]*ns.Id,0,5), make([]*ns.Id,0,5)  // length 0, capacity 5 slices | ||||||
|         dict.GetObjects(&os,&ks,5) |         dict.GetObjects(&va,&ka,5) | ||||||
| 	// last parameter is the count, must be less than or equal to the input slice capacity | 	// last parameter to GetObjects is the count, must be less than or equal to the input slice capacity | ||||||
|         fmt.Printf("Length of os is now %d\n",len(os)) // os and ks slices are now length = 2 |         fmt.Printf("Length of va is now %d\n",len(va)) // va and ka slices are now length = 2 | ||||||
|         for i,k := range ks { |         for i,k := range ka { | ||||||
|                 fmt.Printf("-- %s -> %s\n",k.NSString(),os[i].NSString()) |                 fmt.Printf("-- %s -> %s\n",k.NSString(),va[i].NSString()) | ||||||
|         } |         } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| NSWrap will never check the "count" parameter, so the user will always need | NSWrap will not 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. | where you need to get an error message out of a function or method, or in other | ||||||
|  | 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 | ||||||
|  | @ -370,15 +422,17 @@ 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) | ||||||
| a.ObjectEnumerator().ForIn(func (o ns.Id) bool { | i := 0 | ||||||
|  | a.ObjectEnumerator().ForIn(func (o *ns.Id) bool { | ||||||
| 	switch { | 	switch { | ||||||
| 	case o.IsKindOfClass(ns.NSStringClass()): | 	case o.IsKindOfClass(ns.NSStringClass()): | ||||||
| 		fmt.Println(o.NSString().UTF8String()) | 		fmt.Printf("%d: %s\n", i, o.NSString()) | ||||||
|  | 		i++ | ||||||
| 		return true  // continue enumeration | 		return true  // continue enumeration | ||||||
| 	default: | 	default: | ||||||
| 		fmt.Println("Unknown class") | 		fmt.Println("Unknown class") | ||||||
|  | @ -392,7 +446,7 @@ identification. | ||||||
| 
 | 
 | ||||||
| ## Enum Definitions | ## Enum Definitions | ||||||
| 
 | 
 | ||||||
| NSWrap translates C `enum` values into Go constants. The enums you need are | NSWrap translates C `enum` values into Go constants. The enums you want 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 | ||||||
|  | @ -426,70 +480,6 @@ 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 | ||||||
|  | @ -531,7 +521,8 @@ 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, | ||||||
| you would call `ns.CentralManagerDidUpdateStateCallback(...)` with the name of | if your delegate was named `del`, you would call | ||||||
|  | `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. | ||||||
| 
 | 
 | ||||||
|  | @ -547,11 +538,16 @@ 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 | ||||||
|  | @ -562,12 +558,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 | ||||||
| 
 | 
 | ||||||
|  | @ -576,8 +572,7 @@ 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 even though runtime.LockOSThread() is supposed to provide | Go and you will want to make use of `runtime.LockOSThread()`. | ||||||
| this functionality. |  | ||||||
| 
 | 
 | ||||||
| This is actually a full working Cocoa application: | This is actually a full working Cocoa application: | ||||||
| 
 | 
 | ||||||
|  | @ -618,24 +613,30 @@ 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, | ||||||
|  | @ -647,7 +648,7 @@ func main() { | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Pretty simple right? Not really, NSWrap just generated almost 15,000 lines of | Pretty simple right? Not really, NSWrap just generated over 39,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. | ||||||
| 
 | 
 | ||||||
|  | @ -667,7 +668,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) | ||||||
| #       |--this hyphen indicates that this is an instance method | #       \--the initial 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 | ||||||
|  | @ -728,9 +729,109 @@ 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 | ## Blocks and Function Pointers | ||||||
| 
 | 
 | ||||||
| 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. | ||||||
|  | @ -742,7 +843,7 @@ and got carried away. | ||||||
| 
 | 
 | ||||||
| # Acknowledgements | # Acknowledgements | ||||||
| 
 | 
 | ||||||
| This work was inspired by Maxim's excellent | This work was inspired by Maxim Kupriianov'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,6 +21,10 @@ 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()) | ||||||
|  | @ -68,12 +72,14 @@ 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")) | ||||||
|  | @ -103,6 +109,7 @@ 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 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -117,6 +124,7 @@ func didBecomeActive(n *ns.NSNotification) { | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	a   *ns.NSApplication | 	a   *ns.NSApplication | ||||||
|  | 	del *ns.AppDelegate | ||||||
| 	win *ns.NSWindow | 	win *ns.NSWindow | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -127,7 +135,8 @@ func app() { | ||||||
| 	a.SetActivationPolicy(ns.NSApplicationActivationPolicyRegular) | 	a.SetActivationPolicy(ns.NSApplicationActivationPolicyRegular) | ||||||
| 
 | 
 | ||||||
| 	// Set up an AppDelegate
 | 	// Set up an AppDelegate
 | ||||||
| 	del := ns.AppDelegateAlloc() | 	// assign it to a global variable so it doesn't get garbage collected
 | ||||||
|  | 	del = ns.AppDelegateAlloc() | ||||||
| 	del.ApplicationDidFinishLaunchingCallback(didFinishLaunching) | 	del.ApplicationDidFinishLaunchingCallback(didFinishLaunching) | ||||||
| 	del.ApplicationShouldTerminateAfterLastWindowClosedCallback(shouldTerminateAfterLastWindowClosed) | 	del.ApplicationShouldTerminateAfterLastWindowClosedCallback(shouldTerminateAfterLastWindowClosed) | ||||||
| 	del.ApplicationWillTerminateCallback(willTerminate) | 	del.ApplicationWillTerminateCallback(willTerminate) | ||||||
|  | @ -149,6 +158,6 @@ func main() { | ||||||
| 	}() | 	}() | ||||||
| 
 | 
 | ||||||
| 	// Run our app in an autorelease pool just for fun
 | 	// Run our app in an autorelease pool just for fun
 | ||||||
| 	go ns.Autoreleasepool(app) | 	go app() | ||||||
| 	select {} | 	select {} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -41,8 +41,7 @@ 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().UTF8String()) | 	fmt.Printf("----%s\n", uuid.UUIDString()) | ||||||
| 	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().UTF8String()) | 			fmt.Printf("------%s\n", chuuid.UUIDString()) | ||||||
| 			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,4 +39,3 @@ delegates: | ||||||
|             - peripheralDidUpdateValueForCharacteristic |             - peripheralDidUpdateValueForCharacteristic | ||||||
| 
 | 
 | ||||||
| pragma: [ clang diagnostic ignored "-Wformat-security" ] | pragma: [ clang diagnostic ignored "-Wformat-security" ] | ||||||
| gogc: true |  | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"os" | ||||||
| 
 | 
 | ||||||
| 	"git.wow.st/gmp/nswrap/examples/foundation/ns" | 	"git.wow.st/gmp/nswrap/examples/foundation/ns" | ||||||
| ) | ) | ||||||
|  | @ -43,8 +44,10 @@ 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.Println(o.NSString()) | 		fmt.Printf("%d: %s\n",x,o.NSString()) | ||||||
|  | 		x++ | ||||||
| 		return true | 		return true | ||||||
| 	}) | 	}) | ||||||
| 	fmt.Printf("\nNSSetWithObjectsCount():\n") | 	fmt.Printf("\nNSSetWithObjectsCount():\n") | ||||||
|  | @ -69,6 +72,7 @@ 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 { | ||||||
|  | @ -102,19 +106,26 @@ 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")), | ||||||
| 	) | 	) | ||||||
| 	os := make([]*ns.Id, 0, 5) | 	oarr := make([]*ns.Id, 0, 5) | ||||||
| 	fmt.Printf("Length of os is %d\n", len(os)) | 	fmt.Printf("Length of oarr is %d\n", len(oarr)) | ||||||
| 	ks := make([]*ns.Id, 0, 5) | 	karr := make([]*ns.Id, 0, 5) | ||||||
| 	fmt.Printf("\nGetObjects()\n") | 	fmt.Printf("\nGetObjects()\n") | ||||||
| 	d.GetObjects(&os, &ks, 4) | 	d.GetObjects(&oarr, &karr, 4) | ||||||
| 	fmt.Printf("Length of os is now %d\n", len(os)) | 	fmt.Printf("Length of oarr is now %d\n", len(oarr)) | ||||||
| 	for i, k := range ks { | 	for i, k := range karr { | ||||||
| 		fmt.Printf("-- %s -> %s\n", k.NSString(), os[i].NSString()) | 		fmt.Printf("-- %s -> %s\n", k.NSString(), oarr[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("htttypo://example.com"), 0, &err) | 	n1 = ns.NSStringWithContentsOfURLEncoding(ns.NSURLWithGoString("http://captive.apple.com"), ns.NSUTF8StringEncoding, &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) | ||||||
|  | @ -129,4 +140,24 @@ 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.* ] | enums: [ P_ALL, CF.*, .*StringEncoding ] | ||||||
| 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().UTF8String().String() | 		gstr := s1.NSString().String() | ||||||
| 		_ = gstr | 		_ = gstr | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -122,6 +122,7 @@ 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
 | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -145,22 +146,78 @@ func memtest5() { | ||||||
| 		u := sub.UTF8String() | 		u := sub.UTF8String() | ||||||
| 		u2 := sub2.UTF8String() | 		u2 := sub2.UTF8String() | ||||||
| 		u3 := sub3.UTF8String() | 		u3 := sub3.UTF8String() | ||||||
| 		_ = u |  | ||||||
| 		_ = u2 |  | ||||||
| 		_ = u3 |  | ||||||
| 		time.Sleep(time.Second/50) | 		time.Sleep(time.Second/50) | ||||||
| 		runtime.GC() | 		runtime.GC() | ||||||
| 		i++ | 		i++ | ||||||
|  | 		u.Free() | ||||||
|  | 		u2.Free() | ||||||
|  | 		u3.Free() | ||||||
|  | 		_ = u | ||||||
|  | 		_ = u2 | ||||||
|  | 		_ = u3 | ||||||
| 		//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,7 +5,8 @@ classes: | ||||||
|     - NSArray |     - NSArray | ||||||
|     - NSMutableArray |     - NSMutableArray | ||||||
|     - NSString |     - NSString | ||||||
|     - NSObject |     - NSDictionary | ||||||
|  |     - NSThread | ||||||
| subclasses: | subclasses: | ||||||
|     MyClass: |     MyClass: | ||||||
|         NSObject: |         NSObject: | ||||||
|  | @ -16,4 +17,4 @@ functions: | ||||||
| frameworks: | frameworks: | ||||||
|     - Foundation |     - Foundation | ||||||
| pragma: [ clang diagnostic ignored "-Wformat-security" ] | pragma: [ clang diagnostic ignored "-Wformat-security" ] | ||||||
| gogc: true | #nogc: true | ||||||
|  |  | ||||||
|  | @ -1,3 +1,4 @@ | ||||||
|  | // An example of manual memory management (nogc directive in nswrap.yaml)
 | ||||||
| package main | package main | ||||||
| 
 | 
 | ||||||
| import "C" | import "C" | ||||||
|  | @ -38,8 +39,6 @@ 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) | ||||||
|  | @ -133,6 +132,9 @@ func memtest4() { | ||||||
| 	go memtest1() | 	go memtest1() | ||||||
| 	go memtest2() | 	go memtest2() | ||||||
| 	go memtest3() | 	go memtest3() | ||||||
|  | 	go memtest1() | ||||||
|  | 	go memtest2() | ||||||
|  | 	go memtest3() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func memtest4a() { | func memtest4a() { | ||||||
|  | @ -196,9 +198,6 @@ 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,3 +14,4 @@ subclasses: | ||||||
| frameworks: | frameworks: | ||||||
|     - Foundation |     - Foundation | ||||||
| pragma: [ clang diagnostic ignored "-Wformat-security" ] | pragma: [ clang diagnostic ignored "-Wformat-security" ] | ||||||
|  | nogc: true | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| package: ClassOne | package: ClassOne | ||||||
| inputfiles: [ ClassOne/simple.h ] | inputfiles: [ ClassOne/simple.h ] | ||||||
| classes: | classes: | ||||||
|  |     - NSObject | ||||||
|     - ClassOne |     - ClassOne | ||||||
|     - ClassTwo |     - ClassTwo | ||||||
| subclasses: | subclasses: | ||||||
|  | @ -8,8 +9,7 @@ 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 |  | ||||||
|  |  | ||||||
							
								
								
									
										157
									
								
								examples/strings/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								examples/strings/main.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,157 @@ | ||||||
|  | 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) | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								examples/strings/nswrap.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								examples/strings/nswrap.yaml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | 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,6 +19,12 @@ 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 | ||||||
|  | @ -39,7 +45,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 | ||||||
| 	Gogc        bool | 	Nogc        bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var Config conf | var Config conf | ||||||
|  | @ -251,19 +257,20 @@ func Start() (err error) { | ||||||
| 	if Config.Positions { | 	if Config.Positions { | ||||||
| 		ast.TrackPositions = true | 		ast.TrackPositions = true | ||||||
| 	} | 	} | ||||||
| 	if Config.Gogc && Config.Autorelease { | 	wrap.Gogc = true | ||||||
| 		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.Gogc { | 	if Config.Nogc { | ||||||
| 		wrap.Gogc = true | 		wrap.Gogc = false | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
| 	//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) | ||||||
|  | @ -297,6 +304,9 @@ 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,6 +7,8 @@ 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 | ||||||
| 
 | 
 | ||||||
|  | @ -442,7 +444,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(&" + snames[i] + "[0])" | 			p = "(*unsafe.Pointer)(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: | ||||||
|  | @ -478,6 +480,13 @@ 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++ { | ||||||
|  | @ -486,10 +495,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)[i] = &%s{}%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, pnames[i], sname)) | 	}`, pnames[i], pnames[i], pnames[i], pnames[i], sname, pnames[i], pnames[i], pnames[i], pnames[i], ptgt, dogc, pnames[i], sname)) | ||||||
| 	} | 	} | ||||||
| 	if rt != "void" { | 	if rt != "void" { | ||||||
| 		cmp := "" | 		cmp := "" | ||||||
|  |  | ||||||
|  | @ -217,9 +217,7 @@ 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 { | ||||||
| 	ret := &NSString{} | 	return (*NSString)(unsafe.Pointer(o)) | ||||||
| 	ret.ptr = o.ptr |  | ||||||
| 	return ret |  | ||||||
| } | } | ||||||
| `) | `) | ||||||
| 	str = "int(void)" | 	str = "int(void)" | ||||||
|  | @ -276,7 +274,7 @@ func (o *Id) NSString() *NSString { | ||||||
| 	snames := []string{"", "", "", ""} | 	snames := []string{"", "", "", ""} | ||||||
| 
 | 
 | ||||||
| 	chk_gotoc := func(expected string) { | 	chk_gotoc := func(expected string) { | ||||||
| 		chk(GoToC("myFun", pnames, snames, rtype, ptypes, false, false, false), expected) | 		chk(GoToC("myFun", "myFun", pnames, snames, rtype, ptypes, false, false, false), expected) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	chk_gotoc("") | 	chk_gotoc("") | ||||||
|  | @ -298,12 +296,16 @@ 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 | ||||||
|  | @ -311,6 +313,8 @@ 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 | ||||||
|  | @ -318,7 +322,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(&p2p[0]), (C.int)(p3), unsafe.Pointer(p4))) | 	ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), (*unsafe.Pointer)(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 { | ||||||
|  | @ -330,6 +334,8 @@ 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" | ||||||
|  | @ -337,7 +343,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(&p3p[0]), unsafe.Pointer(p4))) | 	ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), (*unsafe.Pointer)(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 { | ||||||
|  | @ -349,11 +355,13 @@ 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", pnames, snames, rtype, ptypes, true, false, false), | 	chk(GoToC("myFun", "myFun", pnames, snames, rtype, ptypes, true, false, false), | ||||||
| 		`ret := &Id{} | 		`ret := &Id{} | ||||||
| 	ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), unsafe.Pointer(&p3p[0]), p4)) | 	ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), (*unsafe.Pointer)(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 { | ||||||
|  | @ -365,5 +373,7 @@ 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 && false { | 	if Debug { | ||||||
| 		fmt.Printf(f, xs...) | 		fmt.Printf(f, xs...) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										132
									
								
								wrap/main.go
									
									
									
									
									
								
							
							
						
						
									
										132
									
								
								wrap/main.go
									
									
									
									
									
								
							|  | @ -70,7 +70,7 @@ func NewWrapper(debug bool) *Wrapper { | ||||||
| 	} | 	} | ||||||
| 	ret.goImports["unsafe"] = true | 	ret.goImports["unsafe"] = true | ||||||
| 	if Gogc { | 	if Gogc { | ||||||
| 		ret.goImports["runtime"] = true | 		types.Gogc = true | ||||||
| 	} | 	} | ||||||
| 	ret.goTypes.WriteString(` | 	ret.goTypes.WriteString(` | ||||||
| type Id struct { | type Id struct { | ||||||
|  | @ -152,6 +152,8 @@ 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" && | ||||||
|  | @ -160,6 +162,8 @@ 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") || | ||||||
|  | @ -300,10 +304,14 @@ 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 | ||||||
| 		wp := types.ShouldWrap(p.Type.GoType()) | 		gt := p.Type.GoType() | ||||||
| 		if wp || p.Type.IsPointer() || p.Type.Variadic { | 		wp := types.ShouldWrap(gt) | ||||||
|  | 		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*" | ||||||
| 		} else { | 		default: | ||||||
| 			tp = p.Type.CType() | 			tp = p.Type.CType() | ||||||
| 		} | 		} | ||||||
| 		ns = append(ns, p.Vname) | 		ns = append(ns, p.Vname) | ||||||
|  | @ -319,21 +327,28 @@ 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 { | ||||||
| 		if first && !p.Type.Variadic { | 		gt := p.Type.GoType() | ||||||
| 			ret = append(ret, m.Name+":"+p.Vname) | 		if first { | ||||||
| 			first = false | 			first = false | ||||||
|  | 			pname = m.Name | ||||||
| 		} else { | 		} else { | ||||||
| 			if p.Type.Variadic { | 			pname = p.Pname | ||||||
|  | 		} | ||||||
|  | 		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, " ") | ||||||
|  | @ -347,11 +362,13 @@ var goreserved map[string]bool = map[string]bool{ | ||||||
| 	"len":   true, | 	"len":   true, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (w *Wrapper) gpntp(m *Method) ([]string, []string, []*types.Type, string) { | func (w *Wrapper) gpntp(m *Method) ([]string, []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 { | ||||||
|  | @ -363,6 +380,7 @@ func (w *Wrapper) gpntp(m *Method) ([]string, []string, []*types.Type, string) { | ||||||
| 			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) | ||||||
|  | @ -394,7 +412,7 @@ func (w *Wrapper) gpntp(m *Method) ([]string, []string, []*types.Type, string) { | ||||||
| 		} | 		} | ||||||
| 		ret = append(ret, ns[i]+" "+gt) | 		ret = append(ret, ns[i]+" "+gt) | ||||||
| 	} | 	} | ||||||
| 	return ns, snames, tps, strings.Join(ret, ", ") | 	return ns, pnames, snames, tps, strings.Join(ret, ", ") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Interface struct { | type Interface struct { | ||||||
|  | @ -883,11 +901,17 @@ func (c *Char) Free() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (w *Wrapper) StringHelpers() { | func (w *Wrapper) StringHelpers() { | ||||||
| 	w.goHelpers.WriteString(` | 	ufree := "" | ||||||
| func (o *NSString) String() string { | 	if Gogc { | ||||||
| 	return o.UTF8String().String() | 		ufree = "utf8.Free()\n\t" | ||||||
| 	} | 	} | ||||||
| `) | 	w.goHelpers.WriteString(fmt.Sprintf(` | ||||||
|  | func (o *NSString) String() string { | ||||||
|  | 	utf8 := o.UTF8String() | ||||||
|  | 	ret := utf8.String() | ||||||
|  | 	%sreturn ret | ||||||
|  | } | ||||||
|  | `,ufree)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (w *Wrapper) EnumeratorHelpers() { | func (w *Wrapper) EnumeratorHelpers() { | ||||||
|  | @ -1043,7 +1067,7 @@ func (w *Wrapper) _processMethod(m *Method, fun bool) { | ||||||
| 	} else { | 	} else { | ||||||
| 		cmtype = m.Type.CTypeAttrib() | 		cmtype = m.Type.CTypeAttrib() | ||||||
| 	} | 	} | ||||||
| 	ns, snames, tps, gplist := w.gpntp(m) | 	ns, pnames, 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 | ||||||
| 	} | 	} | ||||||
|  | @ -1131,7 +1155,8 @@ 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 { | ||||||
|  | @ -1139,7 +1164,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 = ` | ||||||
| 		[ret retain];` | 		if(ret != nil) { [ret retain]; }` | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| 				// do not retain new, alloc, init and copy methods
 | 				// do not retain new, alloc, init and copy methods
 | ||||||
|  | @ -1149,19 +1174,48 @@ 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 (o != ret) { [ret retain]; }` | 		if (ret != nil && ret != o) { [ret retain]; }` | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			var ar1, ar2 string | 			rtns := []string{} | ||||||
| 			if constructor || true { | 		// for pointers to pointers, assume length 1 unless there is a
 | ||||||
| 				ar1 = "@autoreleasepool {\n\t\t" | 		// parameter named "range" or "count".
 | ||||||
| 				ar2 = "\n	}" | 			rlength := "i<1" | ||||||
|  | 			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 ret; | `	%s@autoreleasepool { | ||||||
| 	%sret = [%s %s];%s%s | 		%s%s[%s %s]%s;%s%s | ||||||
| 	return ret; | 	}%s | ||||||
| }`, m.Type.CTypeAttrib(), ar1, cobj, w.objcparamlist(m), rtn, ar2)) | }`, retdecl, reteq, dup1, cobj, w.objcparamlist(m), dup2, rtn, strings.Join(rtns,"\n\t"), retretn)) | ||||||
| 		} 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))) | ||||||
|  | @ -1182,6 +1236,7 @@ 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 } | ||||||
|  | @ -1916,7 +1971,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] | ||||||
|  | @ -2157,14 +2212,31 @@ 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 | ||||||
| ) | ) | ||||||
| `,strings.Join(imports,"\n"))) | %s | ||||||
|  | `,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