$(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) ) )