FPC PasCocoa

From Lazarus wiki
Revision as of 00:00, 16 November 2009 by Jonas (talk | contribs) (→‎Syntax (proposed): -- description of how current work-in-progress compiler handles categories)
Jump to navigationJump to search

Note: This page as well as PasCocoa compiler feature is under construction.

So if you have anything to add or have your own idea feel free to express

yourself on this page, or mailling lists.

But DO NOT erase others people's writtings.


Thank you


PasCocoa wrappers

Before the compiler supported Objective-Pascal (as described below), the primary way for Objective-C interfacing was through ObjFPC wrapper classes. This process is described on the PasCocoa page.

Objective-C FPC Compiler

All Objective-C support is now available in svn trunk. For instructions on how to check it out, see http://www.freepascal.org/develop.var#svn

For an overview of some conceptual differences between Objective-Pascal and Object Pascal on the one hand, and between Objective-Pascal and Objective-C on the other hand, see FPC_PasCocoa/Differences.

Syntax (implemented)

Enabling Objective-C/Objective-Pascal

The compiler has a mode switch to enable the use of Objective-C-related constructs. It can be activated either using {$modeswitch objectivec1} in a source file, or using the -Mobjectivec1 command line switch. Due to it being a mode switch and not a plain mode, it can be used in combination with every other compiler mode (fpc, objfpc, tp, delphi, macpas).

Note that mode switches are reset when the general syntax mode is changed using, e.g., {$mode objfpc}. So if you have such a statement in a unit, compiling it with -Mobjectivec1 will not change anything. You will have to add {$modeswitch objectivec1} in the source code after the {$mode objfpc} in that case.

Whether or not the compiler has support for Objective-C-related constructs can be checked using {$ifdef FPC_HAS_FEATURE_OBJECTIVEC1}. There will be no separate switches when new Objective-C-related features are implemented. Since all this works happens in FPC trunk, simply expect that people are using the latest version available.

Selectors

To declare a selector for a method, use the objcselector() statement. It returns a value of the type system.SEL. It accepts one parameter, which can either be a constant string representing an Objective-C method name, or a method of an Objective-C class (once support for directly declaring Objective-C classes is implemented in FPC).

<delphi> {$modeswitch objectivec1} var

 a: SEL;

begin

 a:=objcselector('initiWithWidth:andHeight:');
 a:=objcselector('myMethod');

end. </delphi>

Class declaration

To declare an Objective-c class use the keyword objcclass. Objective-C classes must be declared like regular Object Pascal types in a type block: <delphi> type

 ObjCClassName =  objcclass [([ObjCSuperClassName] [, ProtocolName, ProtocolName])] 
 [private, protected, public]
   [variables declarations]
   [method declarations]

end; [external [name 'ExternalClassName'];] </delphi>

  • objcclass: defines an Objective-C class type declaration
  • ObjCSuperClassName: defines parent (or super) class for the class. If name is not specified, the class defines a new root class. See http://wiki.freepascal.org/FPC_PasCocoa/Differences#No_unique_root_class for more information.
  • external: Many Objective-C classes are implemented in external frameworks, such as Cocoa. The external modifiers enables you to declare such external classes for use in Pascal code. It is possible to specify a different external name for the class than the identifier used in Pascal code, because the duplicate identifier rules in Objective-C are laxer than those of Pascal (e.g., there are both a class and a protocol called NSObject in Cocoa, so one of the two has to be renamed in Pascal)

for example: <delphi> // NSView is an external class, declared in some external framework // External classes from Apple's framework // have all field in private section NSView = objcclass(NSResponder) private

 _subview  : id; // private field

public

 function initWithFrame_(rect : NSRect): id; message 'initWithFrame:';
 procedure addSubview_(aview: NSView); message 'addSubview:';
 procedure setAutoresizingMask_(mask: NSUInteger); message 'setAutoresizingMask:';
 procedure setAutoresizesSubviews_(flag: LongBool); message 'setAutoresizesSubviews:';
 procedure drawRect_(dirtyRect: NSRect); message 'drawRect:';

end; external;

// MyView - is declared class // that's should be declared in the local unit // * drawRect_ method is overriden (you don't to need specify 'message' for the second time) // * customMessage_ is newly added method // * data is a newly added field MyView = objcclass(MSView) public

 data : Integer;
 procedure customMessage_(dirtyRect: NSRect); message 'customMessage';
 procedure drawRect_(dirtyRect: NSRect); override;

end;

... procedure MyView.customMessage_(dirtyRect: NSRect); begin end;

procedure MyView.drawRect_(dirtyRect: NSRect); begin end;


</delphi>

Protocol declaration

An Objective-C Protocol is pretty much the same as an interface in Object Pascal: it lists a number of methods to be implemented by classes that conform to the protocol. The only difference is that protocol methods can be optional. Moreover, unlike in Object Pascal, a protocol can inherit from multiple other protocols:

<delphi> type

 ObjCProtocolName =  objcprotocol [(ObjCParentProtocol1[, ObjCParentProtocol2, ...])] 
  [required, optional]
   [method declarations]

end; [external [name 'ExternalClassName'];] </delphi>

  • objcprotocol: defines an Objective-C protocol type declaration
  • ObjCParentProtocols: the protocols this protocol inherits from (so a class conforming to this protocol, will also have to implement all methods from those other protocols)
  • required, optional: If nothing is specified, the default is required. optional methods do not have to be implemented by classes conforming to this protocol)
  • external: same as with objcclass

Example: <delphi> type

 MyProtocol = objccprotocol
   // default is required
   procedure aRequiredMethod; message 'aRequiredMethod';
  optional
   procedure anOptionalMethodWithPara_(para: longint); message 'anOptionalMethodWithPara:';
   procedure anotherOptionalMethod; message 'anotherOptionalMethod';
  required
   function aSecondRequiredMethod: longint; message 'aSecondRequiredMethod';
 end;
 MyClassImplementingProtocol = objcclass(NSObject,MyProtocol)
   // the "message 'xxx'" modifiers can be repeated, but this is not required
   procedure aRequiredMethod;
   procedure anOptionalMethodWithPara_(para: longint);
   function aSecondRequiredMethod: longint;
 end;

</delphi>

Similar to objcclasses, objcprotocols can be external in case they are implemented in e.g. the Cocoa framework.

Method declaration

In general methods are declared in the same way as pascal classes are declared, with the except that each objcclass method must also have a messaging name specified:

<delphi> NSSomeObject = objcclass(NSObject)

 procedure method_(params: Integer); message 'method:';

end; </delphi>

each objcclass method is followed by keyword "message", and the keyword is followed by a string constant, that defines obj-c method name.


If method name is preceeded with keyword 'class', than it means that method is objcclass method (marked as preceeding "+" at objective-c), rather than instance method (marked as preceeding "-")


Note: it's common for objective-c to start method's name in lower case letter. PasCocoa should follow the same way for objcclasses.

  • I am against using the keyword "message", because it is already used for something else. Having a keyword with 2 meanings is against the Pascal principle of readability. --Sekelsenmat 01:18, 10 March 2009 (CET)

Syntax (proposed)

Protocol declaration

An Objective-C category is similar to a class helper in Object Pascal (not yet supported by FPC): it allows adding methods to an existing class, without inheriting from that class. In Objective-C, this goes even one step further: a category can also replace existing methods in another class. Since all methods are virtual in Objective-C, this also means that this method changes for all classes that inherit from the class in which the method was replace (unless they override it). An Objective-C category can also implement protocols.

<delphi> type

 ObjCCategoryName =  objccategory([ExtendedObjCClass[,ObjCProtocol1, ObjCProtocol2, ...])] 
   [method declarations]

end; [external [name 'ExternalCategoryName'];] </delphi>

  • objccategory: defines an Objective-C category type declaration
  • method declarations: the methods that this category adds or replaces in the extended class. These can be both class and instance methods.
  • ObjCParentProtocols: the protocols this category implements. The category will have to implement all required methods from these protocols.
  • external: same as with objcclass. As of Objective-C, the name of a category is optional. To declare an external category without a name, use an empty name.

Example: <delphi> type

 MyProtocol = objcprotocol
   procedure protocolmethod; message 'protocolmethod';
 end;
 MyCategory = objccprotocol(NSObject,MyProtocol)
  // method replaced in NSObject. This means that every objcclass that does not
  // override the hash function will now use this method instead. 
  function hash: cuint; reintroduce;
  // we have to implement this method, since we declare that we implement the
  // MyProtocol protocol. This method is added to NSObject afterwards.
  procedure protocolmethod;
  // a random class methods added to NSObject
  class procedure newmethod; message 'newmethod';
 end;

</delphi>

Similar to objcclasses, objccategorys can be external in case they are implemented in e.g. the Cocoa framework.

todo:

a) how to declare a constant NSString/CFString in Pascal (the @"somestring" from Objective-C)

-- the dollar sign could work... ex. $theString


b) ideas on how to deal with categories. As Gale Paeper mentioned in a mail a couple of years ago on this list:

Since class categories and protocol usages are only selectively defined and used based on the application's use of particular Cocoa frameworks, one needs to be able to restrict the declarations the compiler sees to only those declarations applicable to used frameworks. If one doesn't have a means to restrict the seen declarations, one will need to provide in some form implementing methods for every single protocol and category methods that exits in the complete Cocoa framework, I think, whether one needs to or not. (Without restricted declaration views, I also think you'll be

building a class runtime support data structures which will have erroneous results in runtime test for class protocol and category support tests.) In order to provide restricted declaration views, one needs to be able to extend class type category and protocol method declarations and implementations in units other than the unit containing the base declaration for a class.


Since an all inclusive MacOSAll type of unit won't provide restricted declaration views, I think looking into providing FPC support for non-scoping uses clauses with uses propagation to support umbrella framework style units would be worthwhile. If one doesn't have uses propagation support and one needs restricted declaration views, the uses clause unit list can get pretty long. While there's nothing inherently wrong with long unit lists in uses clauses, it is quite a hassle that quite a few folks don't want to have to deal with.



c) probably many more things I'm currently not thinking of