Difference between revisions of "FPC PasCocoa/Differences"

From Lazarus wiki
Jump to navigationJump to search
Line 54: Line 54:
 
* '''Description''': In Object Pascal, if you do not specify a parent class, a newly defined class will automatically inherit from ''TObject''. While in NeXTStep/Apple/GNUStep's Objective-C frameworks ''NSObject'' fulfills a similar role, there are also other root classes (such as ''NSProxy''). Since there is no unique root class in the Objective-C language, this means that if you do not specify a parent class, you will define a new root class.  
 
* '''Description''': In Object Pascal, if you do not specify a parent class, a newly defined class will automatically inherit from ''TObject''. While in NeXTStep/Apple/GNUStep's Objective-C frameworks ''NSObject'' fulfills a similar role, there are also other root classes (such as ''NSProxy''). Since there is no unique root class in the Objective-C language, this means that if you do not specify a parent class, you will define a new root class.  
 
* '''What to do''': Generally, you will want new classes to inherit from ''NSObject'' if they do not have to inherit from any other class.
 
* '''What to do''': Generally, you will want new classes to inherit from ''NSObject'' if they do not have to inherit from any other class.
 +
 +
===Instances that conform to protocols===
 +
 +
* '''Description''': In Objective-C, declaring a variable of a generic class type that conforms to one or more protocols happens using ''id<protocol1,protocol2>''. In Object Pascal, a variable can only be declared to implement a single interface (conform to one protocol), even though individual classes can implement many interfaces just like in Objective-C. The Pascal declaration is simply ''inteface_typename''. For now, we have limited Objective-Pascal to the same behaviour, i.e., to declare a variable of a generic class type that conforms to the ''NSCopying'' protocol, use ''var obj: NSCopying;''. The main reason is that it's not clear what the Pascal syntax for a variable conforming to multiple protocols should look like.
 +
* '''What do do''': For now, you will need to typecast your variables to different protocol types if you want call messages belonging to different protocols. This should be fixed in the future.
  
 
==Differences with Objective-C==
 
==Differences with Objective-C==

Revision as of 19:25, 12 September 2009

Introduction

FPC's "Objective-Pascal" (ObjCPas) dialect was introduced to enable seamless interfacing with Objective-C code, in particular on Mac OS X. Due to inherent differences between the C and Pascal philosophies, this language mode will have some seemingly strange behaviour regardless of whether your main background is in Objective-C or in Object-Pascal.

This page describes the conceptual differences between ObjCPas and on the one hand Objective-C, and on the other hand Object Pascal.

Differences with Object Pascal

No constructors

  • Description: The Objective-C language has no notion of a formal constructor. Hence, the constructor keyword is not supported in an objcclass.
  • What to do: Construction of class instances in Objective-C usually happens explicitly in two stages: first the alloc class message is sent to the class type that you want to instantiate, and next you send an initXXX instance message to its result. This initXXX (with XXX dependent on the class type) message is called an initializer, and will initialise in the various fields, or may in theory even return a completely different object of a different type.
  • Warning: By convention, initXXX methods free the allocated memory and return nil if the initialisation failed for some reason. This means that if you allocate and initialise an instance using two different statements, you have to assign the result of the initXXX method to your instance variable. And you always have to check for nil after calling an initXXX method before using the result if you are not certain that initialisation will always succeed!
  • Example:

<delphi> {$modeswitch objectivec1} var

 obj: NSObject;

begin

 // 1. Initialisation in two steps. First allocate the memory.
 obj:=NSObject.alloc;
 // Next, initialise. Warning: do not just use "obj.init", since in theory it could
 // return nil, or even a completely different object!
 obj:=obj.init;
 obj.release;
 // 2. Alternative one-liner. The reason for the extra NSObject() typecast is that
 // while the "alloc" method returns an "id" and "id" is assignment-compatible
 // with all objcclasses in Objective-Pascal, it is not (yet) possible to
 // to also send messages to it. This may be remedied to some extent
 // in the future (probably allowing sending messages to it as long as
 // these messages are declared for exactly one class in the current
 // scope).
 obj:=NSObject(NSObject.alloc).init;
 obj.release;

end. </delphi>

No destructors

No unique root class

  • Description: In Object Pascal, if you do not specify a parent class, a newly defined class will automatically inherit from TObject. While in NeXTStep/Apple/GNUStep's Objective-C frameworks NSObject fulfills a similar role, there are also other root classes (such as NSProxy). Since there is no unique root class in the Objective-C language, this means that if you do not specify a parent class, you will define a new root class.
  • What to do: Generally, you will want new classes to inherit from NSObject if they do not have to inherit from any other class.

Instances that conform to protocols

  • Description: In Objective-C, declaring a variable of a generic class type that conforms to one or more protocols happens using id<protocol1,protocol2>. In Object Pascal, a variable can only be declared to implement a single interface (conform to one protocol), even though individual classes can implement many interfaces just like in Objective-C. The Pascal declaration is simply inteface_typename. For now, we have limited Objective-Pascal to the same behaviour, i.e., to declare a variable of a generic class type that conforms to the NSCopying protocol, use var obj: NSCopying;. The main reason is that it's not clear what the Pascal syntax for a variable conforming to multiple protocols should look like.
  • What do do: For now, you will need to typecast your variables to different protocol types if you want call messages belonging to different protocols. This should be fixed in the future.

Differences with Objective-C

Marking methods as override

  • Description: In Object Pascal, if you want to override an inherited method then you have to add the override keyword to the declaration in the child class. Without override, the inherited method will be hidden in the syntactic scope of the child class, but when casting the child class to the parent type and call that method, the parent's implementation will still be executed.

      In Objective-C all dispatching is name-based, and hence a method with the same selector in a child class will always override a parent method with the same name (unlike in Object Pascal, it is not possible to hide an inherited method, nor to start a new inheritance tree).

      Objective-Pascal however also requires override to be specified when overriding inherited Objective-C methods for consistency with Object Pascal. If override is absent, the compiler will give an error. This can also help catch bugs, as explained in this thread and this bug report filed with Apple. There is one exception though: for external classes, override is not required because such declarations are often produced using automated parsers, and requiring them to add override in the right places could needlessly complicate them. Not adding override in such situations will however still cause the compiler to show a hint.

  • What to do: Always add override; at the end of method declarations that override inherited methods.

Class instances are implicit pointers

  • Description: In (Delphi-style) Object Pascal, all class instance variables are implicit pointers, and they are also implicitly dereferenced. In Objective-C, class instances are explicitly declared as being "a pointer to a class instance", e.g. NSObject *obj; and need to be explicitly dereferenced when accessing instance variables (although this seldom happens, since most of the time accessors or properties are used). We have chosen to follow the Object-Pascal way for Objective-Pascal to make it more familiar for people coming from Object Pascal, and because (given the identifier naming constraints outlined below) using a non-dereferenced form of Objective-C class would hardly ever be wanted in Pascal.
  • What to do: Do not use any explicit indirection when dealing with Objective-Pascal class instances.

Protocol method signatures in class declarations

  • Description: When an Objective-C class implements (conforms to) a protocol, the protocol method signatures must not be repeated in the class declaration. In Object Pascal, when a class implements an interface (~ protocol), the method signatures of the interface do have to be repeated in the class definition. The primary reason for this duplication is that in Object Pascal, it is possible to specify that an interface method is implemented using a function with a different name (since dispatching is based on VMT address rather than on name, this is no problem). For consistency with Object Pascal, Objective-Pascal also requires repeating the protocol method declarations in objcclasses that conform to these protocols.
  • What to do: copy/paste the protocol method declarations to the objcclass declaration. Repeating the message modifiers is optional, as the compiler will take them from the objcprotocol declaration if they are not specified in the objcclass declaration.

Duplicate identifiers

  • Description: Pascal allows an identifier to appear only once per scope, and treats a class declaration as a single scope. This means that all instance variables, class methods, instance methods and properties must have distinct names. In Objective-C, each of those categories has its own namespace and there is no problem with having an instance method, class method and instance variable all named foo in a single class. Similarly, a class and a protocol can also have the same name (such as the NSObject class and protocol). Also keep in mind that identifiers are case-insensitive in Pascal.
  • What to do: On the Pascal-side, all such identifiers must be made unique. In case of methods, these can easily be mapped to the real names used in the corresponding in Objective-C code due to the fact that the selector has to be specified explicitly anyway. We will probably add the ability to specify mappings for instance variables. The real name of an Objective-C class or protocol can be specified by specifying a name along with the external modifier at the end of the type declaration.
  • Example:

<delphi> type

 NSObjectProtocol = objcprotocol
    // "_class" instead of "class" on the Pascal side, because "class" is a language keyword
    function _class: pobjc_class; message name 'class';
 end; external name 'NSObject'; // specify the real name of this protocol
 NSObject = objcclass(NSObjectProtocol)
   function _class: pobjc_class; // no need to specify the selector again, it's copied by the compiler from the protocol definition
   // "classClass" (or something else) instead of "_class" because otherwise there are
   // two methods with the same name in the NSObject class.
   class function classClass: pobjc_class; message 'class';
 end; external; // name specification is optional since NSObject is the real name

</delphi>

Using class types as first class entities

  • Description: In Objective-C, it is possible to use a class name directly when sending messages to it (e.g., [NSObject alloc]), but when an argument is of a class type then the class message must be sent to it (e.g., [annotation isKindOfClass: [Building class]] rather than [annotation isKindOfClass: Building]). In Object Pascal, class types can be used directly in such situations (e.g., instance.inheritsfrom(myclasstype)). We have opted to also make class types directly usable in Objective-Pascal, because the reason that this is not possible in Objective-C seems to be mostly a parser limitation rather than a conscious design decision.
  • What to do: You can use typename.classClass and typename interchangeably in Objective-Pascal. The classClass name is due to class being a reserved word in Pascal, and _class being used for the instance method class. That can be seen as another argument in favour of relaxing the Objective-C behaviour.

Sending class messages to instances

  • Description: In Objective-C, class messages must be sent to classes and instance messages to instances. In part, this is required due to the fact that instance and class methods can have the same selector (or alternatively, you can view this as the feature responsible for enabling class and instance methods to have same-named selectors). In Pascal, since every identifier that is part of a class definition (field, instance method, class method, property) must have a unique name, it is possible to call class methods directly using an instance. Since the same identifier scoping rules apply to Objective-Pascal, we have chosen to also enable this feature for that dialect.
  • What to do: You can use instance._class.classmethod and instance.classmethod interchangeably. See the previous point regarding the reason why the class method is called _class in Objective-Pascal.