$(DDOC $(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(SPEC_S Interfacing to Objective-C,
$(DDOC_BLANKLINE )
$(HEADERNAV_TOC $(HEADERNAV_SUBITEMS classes, Classes,
$(HEADERNAV_ITEM external-class, Declaring an External Class)
$(HEADERNAV_ITEM external-class, Binding to a @property (Accessor Methods))
$(HEADERNAV_ITEM defining-class, Defining a Class)
)
$(HEADERNAV_SUBITEMS protocols, Protocols,
$(HEADERNAV_ITEM declaring-protocol, Declaring a Protocol)
$(HEADERNAV_ITEM optional-methods, Optional Methods)
)
$(HEADERNAV_ITEM instance-variables, Instance Variables)
$(HEADERNAV_ITEM instance-method, Calling an Instance Method)
$(HEADERNAV_SUBITEMS selector-attribute, The @selector
Attribute,
$(HEADERNAV_ITEM compiler-checks, Compiler Checks)
)
$(HEADERNAV_SUBITEMS optional-attribute, The @optional
Attribute,
$(HEADERNAV_ITEM compiler-checks, Compiler Checks)
)
$(HEADERNAV_ITEM objc-linkage, Objective-C Linkage)
$(HEADERNAV_ITEM memory-management, Memory Management)
$(HEADERNAV_SUBITEMS frameworks, Frameworks,
$(HEADERNAV_ITEM framework-paths, Framework Paths)
)
$(HEADERNAV_ITEM usage-example, Full Usage Example)
)
$(DDOC_BLANKLINE )
$(P D supports interfacing with Objective-C. It supports protocols, classes,
subclasses, instance variables, instance methods and class methods.
Platform support might vary between different compilers.
)
$(DDOC_BLANKLINE )
$(P Fully working example is available at
$(LINK2 #usage-example, the bottom).
)
$(DDOC_BLANKLINE )
$(SECTION2 $(LNAME2 classes, Classes))
$(DDOC_BLANKLINE )
$(SECTION3 $(LNAME2 external-class, Declaring an External Class))
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD import) core.attribute : selector;
$(D_KEYWORD extern) (Objective-C)
$(D_KEYWORD extern) $(D_KEYWORD class) NSString
{
$(D_KEYWORD const)($(D_KEYWORD char))* UTF8String() @selector($(D_STRING "UTF8String"));
}
)
$(DDOC_BLANKLINE )
$(P All Objective-C classes that should be accessible from within D need to
be declared with the $(LINK2 #objc-linkage, Objective-C linkage). If the
class is declared as extern
(in addition to extern (Objective-C)
) it
is expected to be defined externally.
)
$(DDOC_BLANKLINE )
$(P The $(LINK2 #selector-attribute, @selector
) attribute indicates which
Objective-C selector should be used when calling this method.
This attribute needs to be attached to all methods with the
Objective-C
linkage.
)
$(DDOC_BLANKLINE )
$(SECTION3 $(LNAME2 external-class, Binding to a @property (Accessor Methods)))
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD import) core.attribute : selector;
$(D_KEYWORD extern) (Objective-C)
$(D_KEYWORD extern) $(D_KEYWORD class) MTLRenderPipelineDescriptor : NSObject
{
NSString label() @selector($(D_STRING "label"));
NSString label(NSString) @selector($(D_STRING "setLabel:"));
}
)
$(DDOC_BLANKLINE )
$(P Whenever needing to bind to Objective-C classes @property
, one must be aware
that it generates both a getter and setter. The method to get its value (getter)
is the same name as the property's name. The method to set its value (setter)
starts with the word "set" and then uses the capitalized property name.
The setter for the property label
is setLabel:
.
)
$(DDOC_BLANKLINE )
$(SECTION3 $(LNAME2 defining-class, Defining a Class))
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD import) core.attribute : selector;
$(D_COMMENT // externally defined
)$(D_KEYWORD extern) (Objective-C)
$(D_KEYWORD extern) $(D_KEYWORD class) NSObject
{
$(D_KEYWORD static) NSObject alloc() @selector($(D_STRING "alloc"));
NSObject init() @selector($(D_STRING "init"));
}
$(D_KEYWORD extern) (Objective-C)
$(D_KEYWORD class) Foo : NSObject
{
$(D_KEYWORD override) $(D_KEYWORD static) Foo alloc() @selector($(D_STRING "alloc"));
$(D_KEYWORD override) Foo init() @selector($(D_STRING "init"));
$(D_KEYWORD final) $(D_KEYWORD int) bar($(D_KEYWORD int) a) @selector($(D_STRING "bar:"))
{
$(D_KEYWORD return) a;
}
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD assert)(Foo.alloc.init.bar(3) == 3);
}
)
$(DDOC_BLANKLINE )
$(P Defining an Objective-C class is exactly the same as declaring an
external class but it should not be declared as extern
.
)
$(DDOC_BLANKLINE )
$(P To match the Objective-C semantics, static
and final
methods are
virtual. static
methods are overridable as well.
)
$(DDOC_BLANKLINE )
$(SECTION2 $(LNAME2 protocols, Protocols))
$(DDOC_BLANKLINE )
$(SECTION3 $(LNAME2 declaring-protocol, Declaring a Protocol))
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD import) core.attribute : selector;
$(D_KEYWORD import) core.stdc.stdio : printf;
$(D_KEYWORD extern) (Objective-C)
$(D_KEYWORD interface) Foo
{
$(D_KEYWORD static) $(D_KEYWORD void) foo() @selector($(D_STRING "foo"));
$(D_KEYWORD void) bar() @selector($(D_STRING "bar"));
}
$(D_KEYWORD extern) (Objective-C)
$(D_KEYWORD class) Bar : Foo
{
$(D_KEYWORD static) $(D_KEYWORD void) foo() @selector($(D_STRING "foo"))
{
printf($(D_STRING "foo\n"));
}
$(D_KEYWORD void) bar() @selector($(D_STRING "bar"))
{
printf($(D_STRING "bar\n"));
}
}
)
$(DDOC_BLANKLINE )
$(P Objective-C protocols are represented as interfaces in D and are
declared using the interface
keyword.
)
$(DDOC_BLANKLINE )
$(P All Objective-C protocols that should be accessible from within D need
to be declared with the $(LINK2 #objc-linkage, Objective-C linkage).
)
$(DDOC_BLANKLINE )
$(P Objective-C protocols support virtual class (static) methods. These
methods must be implemented by the class that implements the protocol
(unless they are $(LINK2 #optional-methods, optional)). To match these
semantics, static
methods are virtual. That also means that static
methods with Objective-C linkage, inside an interface cannot have a body.
)
$(DDOC_BLANKLINE )
$(SECTION3 $(LNAME2 optional-methods, Optional Methods))
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD import) core.attribute : optional, selector;
$(D_KEYWORD import) core.stdc.stdio : printf;
$(D_KEYWORD struct) objc_selector;
$(D_KEYWORD alias) SEL = objc_selector*;
$(D_KEYWORD extern) (C) SEL sel_registerName($(D_KEYWORD in) $(D_KEYWORD char)* str);
$(D_KEYWORD extern) (Objective-C)
$(D_KEYWORD extern) $(D_KEYWORD class) NSObject
{
$(D_KEYWORD static) NSObject alloc() @selector($(D_STRING "alloc"));
NSObject init() @selector($(D_STRING "init"));
}
$(D_KEYWORD extern) (Objective-C)
$(D_KEYWORD interface) Foo
{
$(D_KEYWORD bool) respondsToSelector(SEL sel) @selector($(D_STRING "respondsToSelector:"));
$(D_KEYWORD void) foo() @selector($(D_STRING "foo"));
$(D_COMMENT // this is an optional method
) @optional $(D_KEYWORD void) bar() @selector($(D_STRING "bar"));
}
$(D_KEYWORD extern) (Objective-C)
$(D_KEYWORD class) Bar : NSObject, Foo
{
$(D_KEYWORD override) $(D_KEYWORD static) Bar alloc() @selector($(D_STRING "alloc"));
$(D_KEYWORD override) Bar init() @selector($(D_STRING "init"));
$(D_KEYWORD bool) respondsToSelector(SEL sel) @selector($(D_STRING "respondsToSelector:"));
$(D_KEYWORD void) foo() @selector($(D_STRING "foo"))
{
printf($(D_STRING "foo\n"));
}
}
$(D_KEYWORD void) main()
{
Foo f = Bar.alloc.init;
$(D_COMMENT // check, at runtime, if the instance `f` implements the method `bar`
) $(D_KEYWORD if) (f.respondsToSelector(sel_registerName($(D_STRING "bar"))))
f.bar();
$(D_KEYWORD else)
f.foo();
}
)
$(P Objective-C protocols support optional methods. Optional methods are
not required to be implemented by the class that implements the
protocol. To safely call an optional method, a runtime check should be
performed to make sure the receiver implements the method.
)
$(DDOC_BLANKLINE )
$(P In D, optional methods are represented using the
$(LINK2 #optional-attribute, @optional
) attribute.
)
$(DDOC_BLANKLINE )
$(SECTION2 $(LNAME2 instance-variables, Instance Variables))
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD import) core.attribute : selector;
$(D_COMMENT // externally defined
)$(D_KEYWORD extern) (Objective-C)
$(D_KEYWORD extern) $(D_KEYWORD class) NSObject
{
$(D_KEYWORD static) NSObject alloc() @selector($(D_STRING "alloc"));
NSObject init() @selector($(D_STRING "init"));
}
$(D_KEYWORD extern) (Objective-C)
$(D_KEYWORD class) Foo : NSObject
{
$(D_KEYWORD int) bar_;
$(D_KEYWORD override) $(D_KEYWORD static) Foo alloc() @selector($(D_STRING "alloc"));
$(D_KEYWORD override) Foo init() @selector($(D_STRING "init"));
$(D_KEYWORD int) bar() @selector($(D_STRING "bar"))
{
$(D_KEYWORD return) bar_;
}
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD auto) foo = Foo.alloc.init;
foo.bar_ = 3;
$(D_KEYWORD assert)(foo.bar == 3);
}
)
$(DDOC_BLANKLINE )
$(P Declaring an instance variable looks exactly the same as for a regular
D class.
)
$(DDOC_BLANKLINE )
$(P To solve the fragile base class problem, instance variables in
Objective-C has a dynamic offset. That means that the base class can
change (add or remove instance variables) without the subclasses needing
to recompile or relink. Thanks to this feature it's not necessary to
declare instance variables when creating bindings to Objective-C classes.
)
$(DDOC_BLANKLINE )
$(SECTION2 $(LNAME2 instance-method, Calling an Instance Method))
$(DDOC_BLANKLINE )
$(P Calling an Objective-C instance method uses the same syntax as calling
regular D methods:
)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD const)($(D_KEYWORD char))* result = object.UTF8String();
)
$(DDOC_BLANKLINE )
$(P When the compiler sees a call to a method with Objective-C linkage it
will generate a call similar to how an Objective-C compiler would call
the method.
)
$(DDOC_BLANKLINE )
$(SECTION2 $(LNAME2 selector-attribute, The @selector
Attribute))
$(DDOC_BLANKLINE )
$(P The @selector
attribute is a compiler recognized
$(LINK2 attribute.html#uda, UDA). It is used to tell the compiler which
selector to use when calling an Objective-C method.
)
$(DDOC_BLANKLINE )
$(P Selectors in Objective-C can contain the colon character, which is not valid in D
identifiers. D supports method overloading while Objective-C
achieves something similar by using different selectors. For these two
reasons it is better to be able to specify the selectors manually in D,
instead of trying to infer it. This allows to have a more natural names
for the methods in D. Example:
)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD import) core.attribute : selector;
$(D_KEYWORD extern) (Objective-C)
$(D_KEYWORD extern) $(D_KEYWORD class) NSString
{
NSString initWith($(D_KEYWORD in) $(D_KEYWORD char)*) @selector($(D_STRING "initWithUTF8String:"));
NSString initWith(NSString) @selector($(D_STRING "initWithString:"));
}
)
$(DDOC_BLANKLINE )
$(P Here the method initWith
is overloaded with two versions, one
accepting in char*
, the other one NSString
. These two methods are
mapped to two different Objective-C selectors, initWithUTF8String:
and initWithString:
.
)
$(DDOC_BLANKLINE )
$(P The attribute is defined in druntime in
$(DPLLINK phobos/core_attribute.html, core.attribute
). The attribute
is only defined when the version identifier
$(LINK2 #objc-version-identifier, D_ObjectiveC
) is enabled.
)
$(DDOC_BLANKLINE )
$(SECTION3 $(LNAME2 compiler-checks, Compiler Checks))
$(DDOC_BLANKLINE )
$(P The compiler performs the following checks to enforce the correct usage
of the @selector
attribute:
)
$(DDOC_BLANKLINE )
$(UL $(LI The attribute can only be attached to methods with Objective-C
linkage
)
$(DDOC_BLANKLINE )
$(LI The attribute can only be attached once to a method)
$(LI The attribute cannot be attached to a template method)
$(DDOC_BLANKLINE )
$(LI The number of colons in the selector needs to match the number of
parameters the method is declared with
)
)
$(DDOC_BLANKLINE )
$(P If any of the checks fail, a compile error will occur.)
$(DDOC_BLANKLINE )
$(SECTION2 $(LNAME2 optional-attribute, The @optional
Attribute))
$(DDOC_BLANKLINE )
$(P The @optional
attribute is a compiler recognized
$(LINK2 attribute.html#uda, UDA). It is used to tell the compiler that a
method, with Objective-C linkage, declared inside an interface is
optional. That means that the class that implements the interface does
not have to implement the method.
)
$(DDOC_BLANKLINE )
$(P To safely call an optional method, a runtime check should be performed
to make sure the receiver implements the method.
)
$(DDOC_BLANKLINE )
$(P The attribute is defined in druntime in
$(DPLLINK phobos/core_attribute.html, core.attribute
). The attribute
is only defined when the version identifier
$(LINK2 #objc-version-identifier, D_ObjectiveC
) is enabled.
)
$(DDOC_BLANKLINE )
$(SECTION3 $(LNAME2 compiler-checks, Compiler Checks))
$(DDOC_BLANKLINE )
$(P The compiler performs the following checks to enforce the correct usage
of the @optional
attribute:
)
$(UL $(LI The attribute can only be attached to methods with Objective-C linkage)
$(LI The attribute can only be attached to a method inside an interface)
$(LI The attribute can only be attached once to a method)
$(LI The attribute cannot be attached to a template method
)
)
$(P If any of the checks fail, a compile error will occur.)
$(DDOC_BLANKLINE )
$(SECTION2 $(LNAME2 objc-version-identifier, The D_ObjectiveC
Version Identifier)
)
$(DDOC_BLANKLINE )
$(P The D_ObjectiveC
version identifier is a predefined version
identifier. It is enabled if Objective-C support is available for the
target.
)
$(DDOC_BLANKLINE )
$(SECTION2 $(LNAME2 objc-linkage, Objective-C Linkage))
$(DDOC_BLANKLINE )
$(P Objective-C linkage is achieved by attaching the extern (Objective-C)
attribute to a class. Example:
)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD import) core.attribute : selector;
$(D_KEYWORD extern) (Objective-C)
$(D_KEYWORD extern) $(D_KEYWORD class) NSObject
{
NSObject init() @selector($(D_STRING "init"));
}
)
$(DDOC_BLANKLINE )
$(P All methods inside a class declared as extern (Objective-C)
will
get implicit Objective-C linkage.
)
$(DDOC_BLANKLINE )
$(P The linkage is recognized on all platforms but will issue a compile
error if it is used on a platform where Objective-C support is not
available. This allows to easily hide Objective-C declarations from
platforms where it is not available using the
$(LINK2 version.html#version, version
) statement, without resorting to
string mixins or other workarounds.
)
$(DDOC_BLANKLINE )
$(SECTION2 $(LNAME2 memory-management, Memory Management))
$(DDOC_BLANKLINE )
$(P The preferred way to do memory management in Objective-C is to use
Automatic Reference Counting, $(LINK2 https://developer.apple.com/library/mac/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html, ARC).
This is not supported in D, therefore manual memory management is
required to be used instead. This is achieved by calling $(LINK2 https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/index.html#//apple_ref/occ/intfm/NSObject/release, release
)
on an Objective-C instance, like in the old days of Objective-C.
)
$(DDOC_BLANKLINE )
$(SECTION2 $(LNAME2 frameworks, Frameworks))
$(DDOC_BLANKLINE )
$(P Most Objective-C code is bundled in something called a "Framework".
This is basically a regular directory, with the .framework
extension
and a specific directory layout. A framework contains a dynamic
library, all public header files and any resources (images, sounds and
so on) required by the framework.
)
$(DDOC_BLANKLINE )
$(P These directories are recognized by some tools, like the Objective-C
compiler and linker, to be frameworks. To link with a framework from
DMD, use the following flags:
)
$(DDOC_BLANKLINE )
$(D_CODE -L-framework -L<Framework>
)
$(DDOC_BLANKLINE )
where <Framework>
is the name of the framework to link with, without
the .framework
extension. The two -L
flags are required because the
linker expects a space between the -framework
flag and the name of
the framework. DMD cannot handle this and will instead interpret the
name of the framework as a separate flag.
$(DDOC_BLANKLINE )
$(SECTION3 $(LNAME2 framework-paths, Framework Paths))
$(DDOC_BLANKLINE )
$(P Using the above flag, the linker will search in the standard framework
paths. The standard search paths for frameworks are:
)
$(DDOC_BLANKLINE )
$(UL $(LI /System/Library/Frameworks
)
$(LI /Library/Frameworks
)
)
$(DDOC_BLANKLINE )
$(P The following flag from DMD can be used to add a new path in which to
search for frameworks:
)
$(DDOC_BLANKLINE )
$(D_CODE -L-F<framework_path>
)
$(DDOC_BLANKLINE )
$(P For more information see the $(LINK2 https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPFrameworks/Tasks/IncludingFrameworks.html, reference documentation)
and the ld
man page.
)
$(DDOC_BLANKLINE )
$(SECTION2 $(LNAME2 usage-example, Full Usage Example))
$(DDOC_BLANKLINE )
$(P This example will create an Objective-C string, NSString
, and log the
message using NSLog
to stderr.
)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD import) core.attribute : selector;
$(D_KEYWORD extern) (Objective-C)
$(D_KEYWORD extern) $(D_KEYWORD class) NSString
{
$(D_KEYWORD static) NSString alloc() @selector($(D_STRING "alloc"));
NSString initWithUTF8String($(D_KEYWORD in) $(D_KEYWORD char)* str) @selector($(D_STRING "initWithUTF8String:"));
$(D_KEYWORD void) release() @selector($(D_STRING "release"));
}
)
$(DDOC_BLANKLINE )
$(P This is a simplified declaration of the $(LINK2 https://developer.apple.com/documentation/foundation/nsstring?language=objc, NSString
)
class. The $(LINK2 https://developer.apple.com/documentation/objectivec/nsobject/1571958-alloc?language=objc, alloc
)
method allocates an instance of the class. The $(LINK2 https://developer.apple.com/documentation/foundation/nsstring/1412128-initwithutf8string?language=objc, initWithUTF8String:
)
method will be used to convert a C string in UTF-8 to an Objective-C
string, NSString
. The $(LINK2 https://developer.apple.com/documentation/objectivec/1418956-nsobject/1571957-release?language=objc, release
)
method is used to release an deallocate the string. Since D doesn't
support $(LINK2 https://developer.apple.com/library/mac/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html, ARC)
it's needed to manually release Objective-C instances.
)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD extern) (C) $(D_KEYWORD void) NSLog(NSString, ...);
)
$(DDOC_BLANKLINE )
$(P This $(LINK2 https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/index.html#//apple_ref/c/func/NSLog, NSLog
)
function prints a message to the System Log facility, i.e. to stderr
and Console.
)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD auto) str = NSString.alloc();
)
$(DDOC_BLANKLINE )
$(P Allocate an instance of the class, NSString
.)
$(DDOC_BLANKLINE )
$(D_CODE str = str.initWithUTF8String($(D_STRING "Hello World!"))
)
$(DDOC_BLANKLINE )
$(P Initialize the Objective-C string using a C string.)
$(DDOC_BLANKLINE )
$(D_CODE NSLog(str);
)
$(DDOC_BLANKLINE )
$(P Log the string to stderr, this will print something like this in the
terminal:
)
$(DDOC_BLANKLINE )
$(D_CODE 2015-07-18 13:14:27.978 main[11045:2934950] Hello World!
)
$(DDOC_BLANKLINE )
$(D_CODE str.release();
)
$(DDOC_BLANKLINE )
$(P Release and deallocate the string.)
$(DDOC_BLANKLINE )
$(P All steps combined look like this:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD module) main;
$(D_KEYWORD import) core.attribute : selector;
$(D_KEYWORD extern) (Objective-C)
$(D_KEYWORD extern) $(D_KEYWORD class) NSString
{
$(D_KEYWORD static) NSString alloc() @selector($(D_STRING "alloc"));
NSString initWithUTF8String($(D_KEYWORD in) $(D_KEYWORD char)* str) @selector($(D_STRING "initWithUTF8String:"));
$(D_KEYWORD void) release() @selector($(D_STRING "release"));
}
$(D_KEYWORD extern) (C) $(D_KEYWORD void) NSLog(NSString, ...);
$(D_KEYWORD void) main()
{
$(D_KEYWORD auto) str = NSString.alloc().initWithUTF8String($(D_STRING "Hello World!"));
NSLog(str);
str.release();
}
)
$(DDOC_BLANKLINE )
$(P When compiling the application remember to link with the required
libraries, in this case the Foundation framework. Example:
)
$(DDOC_BLANKLINE )
$(D_CODE dmd -L-framework -LFoundation main.d
)
$(SPEC_SUBNAV_PREV_NEXT cpp_interface, Interfacing to C++, portability, Portability Guide)
)
)