objc

package
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 20, 2025 License: Apache-2.0 Imports: 11 Imported by: 3

Documentation

Rendered for darwin/amd64

Overview

Package objc is a low-level pure Go objective-c runtime. This package is easy to use incorrectly, so it is best to use a wrapper that provides the functionality you need in a safer way.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func InvokeBlock added in v0.9.0

func InvokeBlock[T any](block Block, args ...any) (result T, err error)

InvokeBlock is a convenience method for calling the implementation of a block. The block implementation must return 1 value.

Example
package main

import (
	"fmt"

	"github.com/ebitengine/purego/objc"
)

func main() {
	type vector struct {
		X, Y, Z float64
	}

	block := objc.NewBlock(
		func(block objc.Block, v1, v2 *vector) *vector {
			return &vector{
				X: v1.Y*v2.Z - v1.Z*v2.Y,
				Y: v1.Z*v2.X - v1.X*v2.Z,
				Z: v1.X*v2.Y - v1.Y*v2.X,
			}
		},
	)
	defer block.Release()

	result, err := objc.InvokeBlock[*vector](
		block,
		&vector{X: 0.1, Y: 2.3, Z: 4.5},
		&vector{X: 6.7, Y: 8.9, Z: 0.1},
	)

	fmt.Println(*result, err)
}
Output:

{-39.82 30.14 -14.52} <nil>

func Send added in v0.2.0

func Send[T any](id ID, sel SEL, args ...any) T

Send is a convenience method for sending messages to objects that can return any type. This function takes a SEL instead of a string since RegisterName grabs the global Objective-C lock. It is best to cache the result of RegisterName.

Example
package main

import (
	"fmt"

	"github.com/ebitengine/purego/objc"
)

func main() {
	type NSRange struct {
		Location, Range uint
	}
	class_NSString := objc.GetClass("NSString")
	sel_stringWithUTF8String := objc.RegisterName("stringWithUTF8String:")

	fullString := objc.ID(class_NSString).Send(sel_stringWithUTF8String, "Hello, World!\x00")
	subString := objc.ID(class_NSString).Send(sel_stringWithUTF8String, "lo, Wor\x00")

	r := objc.Send[NSRange](fullString, objc.RegisterName("rangeOfString:"), subString)
	fmt.Println(r)
}
Output:

{3 7}

func SendSuper added in v0.2.0

func SendSuper[T any](id ID, sel SEL, args ...any) T

SendSuper is a convenience method for sending message to object's super that can return any type. This function takes a SEL instead of a string since RegisterName grabs the global Objective-C lock. It is best to cache the result of RegisterName.

Example
package main

import (
	"fmt"

	"github.com/ebitengine/purego/objc"
)

func main() {
	super, err := objc.RegisterClass(
		"SuperObject2",
		objc.GetClass("NSObject"),
		nil,
		nil,
		[]objc.MethodDef{
			{
				Cmd: objc.RegisterName("doSomething"),
				Fn: func(self objc.ID, _cmd objc.SEL) int {
					return 16
				},
			},
		},
	)
	if err != nil {
		panic(err)
	}

	child, err := objc.RegisterClass(
		"ChildObject2",
		super,
		nil,
		nil,
		[]objc.MethodDef{
			{
				Cmd: objc.RegisterName("doSomething"),
				Fn: func(self objc.ID, _cmd objc.SEL) int {
					return 24
				},
			},
		},
	)
	if err != nil {
		panic(err)
	}

	res := objc.SendSuper[int](objc.ID(child).Send(objc.RegisterName("new")), objc.RegisterName("doSomething"))
	fmt.Println(res)
}
Output:

16

Types

type Block added in v0.9.0

type Block ID

Block is an opaque pointer to an Objective-C object containing a function with its associated closure.

func NewBlock added in v0.9.0

func NewBlock(fn any) Block

NewBlock takes a Go function that takes a Block as its first argument. It returns an Block that can be called by Objective-C code. The function panics if an error occurs. Use Block.Release to free this block when it is no longer in use.

Example
package main

import (
	"fmt"

	"github.com/ebitengine/purego"
	"github.com/ebitengine/purego/objc"
)

func main() {
	_, err := purego.Dlopen("/System/Library/Frameworks/Foundation.framework/Foundation", purego.RTLD_GLOBAL|purego.RTLD_NOW)
	if err != nil {
		panic(err)
	}

	var count = 0
	block := objc.NewBlock(
		func(block objc.Block, line objc.ID, stop *bool) {
			count++
			fmt.Printf("LINE %d: %s\n", count, objc.Send[string](line, objc.RegisterName("UTF8String")))
			*stop = count == 3
		},
	)
	defer block.Release()

	lines := objc.ID(objc.GetClass("NSString")).Send(objc.RegisterName("stringWithUTF8String:"), "Alpha\nBeta\nGamma\nDelta\nEpsilon")
	defer lines.Send(objc.RegisterName("release"))

	lines.Send(objc.RegisterName("enumerateLinesUsingBlock:"), block)
}
Output:

LINE 1: Alpha
LINE 2: Beta
LINE 3: Gamma

func (Block) Copy added in v0.9.0

func (b Block) Copy() Block

Copy creates a copy of a block on the Objective-C heap (or increments the reference count if already on the heap). Use Block.Release to free the copy when it is no longer in use.

func (Block) Invoke added in v0.9.0

func (b Block) Invoke(args ...any)

Invoke calls the implementation of a block.

func (Block) Release added in v0.9.0

func (b Block) Release()

Release decrements the Block's reference count, and if it is the last reference, frees it.

type Class

type Class uintptr

Class is an opaque type that represents an Objective-C class.

func GetClass

func GetClass(name string) Class

GetClass returns the Class object for the named class, or nil if the class is not registered with the Objective-C runtime.

func RegisterClass added in v0.2.0

func RegisterClass(name string, superClass Class, protocols []*Protocol, ivars []FieldDef, methods []MethodDef) (Class, error)

RegisterClass takes the name of the class to create, the superclass, a list of protocols this class implements, a list of fields this class has and a list of methods. It returns the created class or an error describing what went wrong.

Example
package main

import (
	"fmt"
	"reflect"

	"github.com/ebitengine/purego/objc"
)

func main() {
	var (
		sel_new    = objc.RegisterName("new")
		sel_init   = objc.RegisterName("init")
		sel_setBar = objc.RegisterName("setBar:")
		sel_bar    = objc.RegisterName("bar")

		BarInit = func(id objc.ID, cmd objc.SEL) objc.ID {
			return id.SendSuper(cmd)
		}
	)

	class, err := objc.RegisterClass(
		"BarObject",
		objc.GetClass("NSObject"),
		[]*objc.Protocol{
			objc.GetProtocol("NSDelegateWindow"),
		},
		[]objc.FieldDef{
			{
				Name:      "bar",
				Type:      reflect.TypeOf(int(0)),
				Attribute: objc.ReadWrite,
			},
			{
				Name:      "foo",
				Type:      reflect.TypeOf(false),
				Attribute: objc.ReadWrite,
			},
		},
		[]objc.MethodDef{
			{
				Cmd: sel_init,
				Fn:  BarInit,
			},
		},
	)
	if err != nil {
		panic(err)
	}

	object := objc.ID(class).Send(sel_new)
	object.Send(sel_setBar, 123)
	bar := int(object.Send(sel_bar))
	fmt.Println(bar)
}
Output:

123
Example (Helloworld)
package main

import (
	"fmt"

	"github.com/ebitengine/purego/objc"
)

func main() {
	class, err := objc.RegisterClass(
		"FooObject",
		objc.GetClass("NSObject"),
		nil,
		nil,
		[]objc.MethodDef{
			{
				Cmd: objc.RegisterName("run"),
				Fn: func(self objc.ID, _cmd objc.SEL) {
					fmt.Println("Hello World!")
				},
			},
		},
	)
	if err != nil {
		panic(err)
	}

	object := objc.ID(class).Send(objc.RegisterName("new"))
	object.Send(objc.RegisterName("run"))
}
Output:

Hello World!

func (Class) AddMethod

func (c Class) AddMethod(name SEL, imp IMP, types string) bool

AddMethod adds a new method to a class with a given name and implementation. The types argument is a string containing the mapping of parameters and return type. Since the function must take at least two arguments—self and _cmd, the second and third characters must be “@:” (the first character is the return type).

func (Class) AddProtocol added in v0.2.0

func (c Class) AddProtocol(protocol *Protocol) bool

AddProtocol adds a protocol to a class. Returns true if the protocol was added successfully, otherwise false (for example, the class already conforms to that protocol).

func (Class) InstanceSize added in v0.2.0

func (c Class) InstanceSize() uintptr

InstanceSize returns the size in bytes of instances of the class or 0 if cls is nil

func (Class) InstanceVariable

func (c Class) InstanceVariable(name string) Ivar

InstanceVariable returns an Ivar data structure containing information about the instance variable specified by name.

func (Class) SuperClass

func (c Class) SuperClass() Class

SuperClass returns the superclass of a class. You should usually use NSObject‘s superclass method instead of this function.

type FieldDef added in v0.5.0

type FieldDef struct {
	Name      string
	Type      reflect.Type
	Attribute IvarAttrib
}

FieldDef is a definition of a field to add to an Objective-C class. The name of the field is what will be used to access it through the Ivar. If the type is bool the name cannot start with `is` since a getter will be generated with the name `isBoolName`. The name also cannot contain any spaces. The type is the Go equivalent type of the Ivar. Attribute determines if a getter and or setter method is generated for this field.

type ID

type ID uintptr

ID is an opaque pointer to some Objective-C object

func (ID) Class

func (id ID) Class() Class

Class returns the class of the object.

func (ID) GetIvar added in v0.5.0

func (id ID) GetIvar(ivar Ivar) ID

GetIvar reads the value of an instance variable in an object.

func (ID) Send

func (id ID) Send(sel SEL, args ...any) ID

Send is a convenience method for sending messages to objects. This function takes a SEL instead of a string since RegisterName grabs the global Objective-C lock. It is best to cache the result of RegisterName.

func (ID) SendSuper

func (id ID) SendSuper(sel SEL, args ...any) ID

SendSuper is a convenience method for sending message to object's super. This function takes a SEL instead of a string since RegisterName grabs the global Objective-C lock. It is best to cache the result of RegisterName.

Example
package main

import (
	"fmt"

	"github.com/ebitengine/purego/objc"
)

func main() {
	super, err := objc.RegisterClass(
		"SuperObject",
		objc.GetClass("NSObject"),
		nil,
		nil,
		[]objc.MethodDef{
			{
				Cmd: objc.RegisterName("doSomething"),
				Fn: func(self objc.ID, _cmd objc.SEL) {
					fmt.Println("In Super!")
				},
			},
		},
	)
	if err != nil {
		panic(err)
	}

	child, err := objc.RegisterClass(
		"ChildObject",
		super,
		nil,
		nil,
		[]objc.MethodDef{
			{
				Cmd: objc.RegisterName("doSomething"),
				Fn: func(self objc.ID, _cmd objc.SEL) {
					fmt.Println("In Child")
					self.SendSuper(_cmd)
				},
			},
		},
	)
	if err != nil {
		panic(err)
	}

	objc.ID(child).Send(objc.RegisterName("new")).Send(objc.RegisterName("doSomething"))
}
Output:

In Child
In Super!

func (ID) SetIvar added in v0.5.0

func (id ID) SetIvar(ivar Ivar, value ID)

SetIvar sets the value of an instance variable in an object.

type IMP

type IMP uintptr

IMP is a function pointer that can be called by Objective-C code.

Example
package main

import (
	"fmt"

	"github.com/ebitengine/purego"
	"github.com/ebitengine/purego/objc"
)

func main() {
	imp := objc.NewIMP(func(self objc.ID, _cmd objc.SEL, a3, a4, a5, a6, a7, a8, a9 int) {
		fmt.Println("IMP:", self, _cmd, a3, a4, a5, a6, a7, a8, a9)
	})

	purego.SyscallN(uintptr(imp), 105, 567, 9, 2, 3, ^uintptr(4), 4, 8, 9)
}
Output:

IMP: 105 567 9 2 3 -5 4 8 9

func NewIMP

func NewIMP(fn any) IMP

NewIMP takes a Go function that takes (ID, SEL) as its first two arguments. It returns an IMP function pointer that can be called by Objective-C code. The function panics if an error occurs. The function pointer is never deallocated.

type Ivar

type Ivar uintptr

Ivar an opaque type that represents an instance variable.

func (Ivar) Name added in v0.5.0

func (i Ivar) Name() string

func (Ivar) Offset

func (i Ivar) Offset() uintptr

Offset returns the offset of an instance variable that can be used to assign and read the Ivar's value.

For instance variables of type ID or other object types, call Ivar and SetIvar instead of using this offset to access the instance variable data directly.

type IvarAttrib added in v0.5.0

type IvarAttrib int

IvarAttrib is the attribute that an ivar has. It affects if and which methods are automatically generated when creating a class with RegisterClass. See Apple Docs for an understanding of these attributes. The fields are still accessible using objc.GetIvar and objc.SetIvar regardless of the value of IvarAttrib.

Take for example this Objective-C code:

@property (readwrite) float value;

In Go, the functions can be accessed as followed:

var value = purego.Send[float32](id, purego.RegisterName("value"))
id.Send(purego.RegisterName("setValue:"), 3.46)
const (
	ReadOnly IvarAttrib = 1 << iota
	ReadWrite
)

type MethodDef added in v0.5.0

type MethodDef struct {
	Cmd SEL
	Fn  any
}

MethodDef represents the Go function and the selector that ObjC uses to access that function.

type MethodDescription added in v0.9.0

type MethodDescription struct {
	// contains filtered or unexported fields
}

MethodDescription holds the name and type definition of a method.

func (MethodDescription) Name added in v0.9.0

func (m MethodDescription) Name() string

Name returns the name of this method.

func (MethodDescription) Types added in v0.9.0

func (m MethodDescription) Types() string

Types returns the OBJC runtime encoded type description.

type Property added in v0.9.0

type Property uintptr

Property is an opaque type for Objective-C property metadata.

func (Property) Attributes added in v0.9.0

func (p Property) Attributes() string

Attributes returns a comma separated list of PropertyAttribute

func (Property) Name added in v0.9.0

func (p Property) Name() string

Name returns the name of this property.

type PropertyAttribute added in v0.9.0

type PropertyAttribute struct {
	Name, Value *byte
}

PropertyAttribute contains the null-terminated Name and Value pair of a Properties internal description.

type Protocol added in v0.2.0

type Protocol [0]func()

Protocol is a type that declares methods that can be implemented by any class.

func AllocateProtocol added in v0.9.0

func AllocateProtocol(name string) *Protocol

AllocateProtocol creates a new protocol in the OBJC runtime or nil if the protocol already exists.

Example
package main

import (
	"fmt"
	"log"

	"github.com/ebitengine/purego/objc"
)

func main() {
	var p *objc.Protocol
	if p = objc.AllocateProtocol("MyCustomProtocol"); p != nil {
		p.AddMethodDescription(objc.RegisterName("isFoo"), "B16@0:8", true, true)
		var adoptedProtocol *objc.Protocol
		adoptedProtocol = objc.GetProtocol("NSObject")
		if adoptedProtocol == nil {
			log.Fatalln("protocol 'NSObject' does not exist")
		}
		p.AddProtocol(adoptedProtocol)
		p.AddProperty("accessibilityElement", []objc.PropertyAttribute{
			{Name: &[]byte("T\x00")[0], Value: &[]byte("B\x00")[0]},
			{Name: &[]byte("G\x00")[0], Value: &[]byte("isBar\x00")[0]},
		}, true, true)
		p.Register()
	}

	p = objc.GetProtocol("MyCustomProtocol")

	for _, protocol := range p.CopyProtocolList() {
		fmt.Println(protocol.Name())
	}
	for _, property := range p.CopyPropertyList(true, true) {
		fmt.Println(property.Name(), property.Attributes())
	}
	for _, method := range p.CopyMethodDescriptionList(true, true) {
		fmt.Println(method.Name(), method.Types())
	}

}
Output:

NSObject
accessibilityElement TB,GisBar
isFoo B16@0:8

func GetProtocol added in v0.2.0

func GetProtocol(name string) *Protocol

GetProtocol returns the protocol for the given name or nil if there is no protocol by that name.

func (*Protocol) AddMethodDescription added in v0.9.0

func (p *Protocol) AddMethodDescription(name SEL, types string, isRequiredMethod, isInstanceMethod bool)

AddMethodDescription adds a method to a protocol. This can only be called between AllocateProtocol and Protocol.Register.

func (*Protocol) AddProperty added in v0.9.0

func (p *Protocol) AddProperty(name string, attributes []PropertyAttribute, isRequiredProperty, isInstanceProperty bool)

AddProperty adds a property to the protocol. This can only be called between AllocateProtocol and Protocol.Register.

func (*Protocol) AddProtocol added in v0.9.0

func (p *Protocol) AddProtocol(protocol *Protocol)

AddProtocol marks the protocol as inheriting from another. This can only be called between AllocateProtocol and Protocol.Register.

func (*Protocol) CopyMethodDescriptionList added in v0.9.0

func (p *Protocol) CopyMethodDescriptionList(isRequiredMethod, isInstanceMethod bool) []MethodDescription

CopyMethodDescriptionList returns a list of methods that this protocol has given it isRequiredMethod and isInstanceMethod.

func (*Protocol) CopyPropertyList added in v0.9.0

func (p *Protocol) CopyPropertyList(isRequiredProperty, isInstanceProperty bool) []Property

CopyPropertyList returns a list of properties that this protocol has given it isRequiredProperty and isInstanceProperty.

func (*Protocol) CopyProtocolList added in v0.9.0

func (p *Protocol) CopyProtocolList() []*Protocol

CopyProtocolList returns a list of the protocols that this protocol inherits from.

func (*Protocol) Equals added in v0.5.0

func (p *Protocol) Equals(p2 *Protocol) bool

Equals return true if the two protocols are the same.

func (*Protocol) Name added in v0.9.0

func (p *Protocol) Name() string

Name returns the name of this protocol.

func (*Protocol) Register added in v0.9.0

func (p *Protocol) Register()

Register registers the protocol created using AllocateProtocol with the runtime. This must be done before it is used anywhere and can only be called once.

type SEL

type SEL uintptr

SEL is an opaque type that represents a method selector

func RegisterName

func RegisterName(name string) SEL

RegisterName registers a method with the Objective-C runtime system, maps the method name to a selector, and returns the selector value. This function grabs the global Objective-c lock. It is best the cache the result of this function.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL