Embeddinator-4000/docs/ObjC.md

5.7 KiB

id:{8e48fb46-9a13-4a1b-a8af-9fe87458a293} title:ObjC Support

//: # (This allows all contributors (including external) to submit, using a PR, updates to the documentation that match the tools changes) //: # (Modifications outside of mono/Embeddinator-4000 will be lost on future updates)

ObjC

Specific features

The generation of ObjC has a few, special features that are worth noting.

Automatic Reference Counting

The use of Automatic Reference Counting (ARC) is required to call the generated bindings. Project using a embeddinator-based library must be compiled with -fobjc-arc.

NSString support

APIs that expose System.String types are converted into NSString. This makes memory management easier than dealing with char*.

Protocols support

Managed interfaces are converted into ObjC protocols where all members are @required.

NSObject Protocol support

By default we assume the default hashing and equality of both .net and the ObjC runtime are fine and interchangeable as they share very similar semantics.

When a managed type overrides Equals(Object) or GetHashCode then it generally means the defaut (.net) behaviour was not the best one. We can assume the default ObjC behaviour would not be either.

In such case the generator overrides the isEqual: method and hash property defined in the NSObject protocol. This allows the custom managed implementation to be used from ObjC code transparently.

Comparison

Managed types that implement IComparable or it's generic version IComparable<T> will produce ObjC friendly methods that returns a NSComparisonResult and accept a nil argument. This makes the generated API more friendly to ObjC developers, e.g.

- (NSComparisonResult)compare:(XAMComparableType * _Nullable)other;

Categories

Managed extensions methods are converted into categories. For example the following extension methods on Collection

	public static class SomeExtensions {

		public static int CountNonNull (this Collection collection) { ... }

		public static int CountNull (this Collection collection) { ... }
	}

would create an ObjC category like this one:

@interface Collection (SomeExtensions)

- (int)countNonNull;
- (int)countNull;
@end

When a single managed type extends several types then multiple ObjC categories are generated.

Subscripting

Managed indexed properties are converted into object subscripting. For example:

	public bool this[int index] {
		get { return c[index]; }
		set { c[index] = value; }
	}

would create ObjC similar to :

- (id)objectAtIndexedSubscript:(int)idx;
- (void)setObject:(id)obj atIndexedSubscript:(int)idx;

which can be used via the ObjC subscripting syntax:

    if ([intCollection [0] isEqual:@42])
	    intCollection[0] = @13;

Depending on the type of your indexer, indexed or keyed subscripting will be generated where appropriate.

This article is a great introduction to subscripting.

Main differences with .NET

Constructors v.s. Initializers

In Objective-C, you can call any of the initializer prototypes of any of the parent classes in the inheritance chain unless it is marked as unavailable (NS_UNAVAILABLE).

In C# you must explicitly declare a constructor member inside a class, this means constructors are not inherited.

In order to expose the right representation of the C# API to Objective-C, we add NS_UNAVAILABLE to any initializer that is not present in the child class from the parent class.

C# API:

public class Unique {
	public Unique () : this (1)
	{
	}

	public Unique (int id)
	{
	}
}

public class SuperUnique : Unique {
	public SuperUnique () : base (911)
	{
	}
}

Objective-C surfaced API:

@interface SuperUnique : Unique

- (instancetype)initWithId:(int)id NS_UNAVAILABLE;
- (instancetype)init;

@end

Here we can see that initWithId: has been marked as unavailable.

Operator

ObjC does not support operator overloading as C# does, so operators are converted to class selectors:

	public static AllOperators operator + (AllOperators c1, AllOperators c2)
	{
		return new AllOperators (c1.Value + c2.Value);
	}

to

+ (instancetype)add:(Overloads_AllOperators *)anObjectC1 c2:(Overloads_AllOperators *)anObjectC2;

However, some .NET languages do not support operator overloading, so it is common to also include a "friendly" named method in addition to the operator overload.

If both the operator version and the "friendly" version are found, only the friendly version will be generated, as they will generate to the same objective-c name.

	public static AllOperatorsWithFriendly operator + (AllOperatorsWithFriendly c1, AllOperatorsWithFriendly c2)
	{
		return new AllOperatorsWithFriendly (c1.Value + c2.Value);
	}

	public static AllOperatorsWithFriendly Add (AllOperatorsWithFriendly c1, AllOperatorsWithFriendly c2)
	{
		return new AllOperatorsWithFriendly (c1.Value + c2.Value);
	}

becomes:

+ (instancetype)add:(Overloads_AllOperatorsWithFriendly *)anObjectC1 c2:(Overloads_AllOperatorsWithFriendly *)anObjectC2;

Equality operator

In general operator == in C# is handled as a general operator as noted above.

However, if the "friendly" Equals operator is found, both operator == and operator != will be skipped in generation.