PerlObjCBridge(3) User Contributed Perl Documentation PerlObjCBridge(3)
NAME
PerlObjCBridge.pm - Bridges Perl and Objective-C runtimes
SYNOPSIS
use Foundation;
$s1 = NSString->stringWithCString_("Hello ");
$s2 = NSString->alloc()->initWithCString_("World");
$s3 = $s1->stringByAppendingString_($s2);
printf "%s
", $s3->cString();
SUMMARY
The PerlObjCBridge module supports creating and messaging Objective-C objects from Perl programs, allowing Cocoa objects in Apple
Computer's Mac OS X to be directly manipulated from Perl. In addition, Perl objects can be messaged from Objective-C, making it possible
for Perl objects to function as Cocoa delegates and as targets of notifications and other Cocoa call-back messages. Perl programs can take
advantage of Cocoa's Distributed Objects mechanism to support messaging between Perl objects and Objective-C objects (or other Perl
objects) in different address spaces, possibly on different machines.
LIMITATION
This version of PerlObjCBridge does not directly support writing GUI Cocoa applications in Perl. Consult
http://www.sourceforge.net/projects/camelbones for a freeware package that supports GUI Perl/Cocoa apps.
DESCRIPTION
Using PerlObjCBridge, Perl programs can reference Objective-C objects in a manner similar to references to native Perl objects. The
Objective-C objects must inherit from the NSObject root class in the Mac OS X Foundation framework (which is true for Cocoa objects). In
Objective-C an object is accessed via an object identifier that is implemented as a pointer to a structure containing the object's instance
data. PerlObjCBridge represents an Objective-C object as a Perl reference to a scalar value that contains the Objective-C ID. For example,
if an Objective-C object has an ID with value 0x12345678, then PerlObjCBridge represents that object as a reference to a scalar with value
0x12345678. The Perl reference is "blessed" into a Perl class that has the same name as the Objective-C class. The Perl inheritance
mechanism is then used to route any messages sent to the object from Perl through the PerlObjCBridge extension module and ultimately to the
Objective-C object. The return values of the Objective-C messages are similarly routed back through the bridge where they are converted
into Perl return values.
It is also possible to use Perl objects in places where Cocoa methods normally take Objective-C arguments. For example, one can register
Perl objects to receive NSNotifications, in which case the perl objects provide the notification handling methods that are asynchronously
messaged by NSNotificationCenter when interesting events occur. As another example, a Perl object can be registered as a server object via
NSConnection, after which Objective-C or Perl objects in other address spaces can send messages to the server object via the Distributed
Objects mechanism. In these examples an Objective-C proxy object is created by PerlObjCBridge that gets passed to Objective-C methods, and
that forwards messages from Objective-C to the Perl object.
MESSAGING
Ordinary Perl "object->method(argument-list)" syntax is used to send messages to Objective-C objects. The ':' character that delimits
arguments in Objective-C is illegal in Perl method names, so underscores are used instead. An method that is invoked in Objective-C as:
[anObject arg1:x arg2:y];
can be invoked from Perl using something like:
$anObject->arg1_arg2_($x, $y);
Contrast the following Objective-C code fragment with its Perl analogue in the synopsis at the top of this man page:
#import <Foundation/Foundation.h>
NSString *s1 = [NSString stringWithCString:"Hello "];
NSString *s2 = [[NSString alloc] initWithCString:"World"];
NSString *s3 = [s1 stringByAppendingString:s2];
printf("%s
", [s3 cString]);
To send a message to an Objective-C class, one uses the syntactic form ClassName->method(...args...). For example, one can send the
"defaultManager" message to the NSFileManager class as follows:
$defMgr = NSFileManager->defaultManager();
An important special case of a class method is a "factory" method that constructs a new instance of a class:
$array = NSMutableArray->array();
$string = NSString->stringWithCString_("Hi there");
The NSString factory method illustrates how PerlObjCBridge passes Perl strings to Objective-C as char *'s.
To send a message to an Objective-C object, one uses the syntactic form $object->method(...args...). If $array is a reference to an
NSMutableArray then one can add the NSString referenced by $string by sending $array the "addObject:" message:
$array->addObject_($string);
Message sends can be chained from left to right:
$hostName = NSProcessInfo->processInfo()->hostName();
In the above example, the object returned by NSProcessInfo->processInfo() is in turn sent the hostName message.
USING COCOA FRAMEWORKS
PerlObjCBridge automatically generates a bridge module for the Foundation framework that is included with the Cocoa environment in Mac OS
X. This bridge module is created when PerlObjCBridge is built. The bridge module for a framework causes that framework to be dynamically
loaded into the Perl program's address space. In addition Perl packages are created for each of the Objective-C classes in the framework so
that the Objective-C classes exist in the Perl name space.
To access a framework from Perl "use" its bridge module. For example, to access Foundation objects do:
use Foundation;
DISTRIBUTED MESSAGING
Perl objects can send messages to other objects (Perl or Cocoa) in different address spaces by using Cocoa's Distributed Objects (DO)
mechanism. This makes it easy to create distributed systems (such as client/server systems) that mix Perl and Cocoa programs. It also makes
it easy to create a pure Perl distributed system, where Perl objects in different address spaces communicate via Cocoa DO.
Here is a complete example of a distributed client/server system, where the client and server objects are written in Perl but communicate
by means of DO. The system consists of a Perl client program, a Perl server program, and a Perl XSUB module that provides the glue between
the Perl programs and DO. The XSUB module is initially created by running the following command:
h2xs -A -n AddSystem
An AddSystem directory is created with these files:
ppport.h
lib/AddSystem.pm
AddSystem.xs
Makefile.PL
README
t/AddSystem.t
Changes
MANIFEST
Edit the Makefile.PL DEFINE entry to add the -ObjC flag:
'DEFINE' => '-ObjC', # e.g., '-DHAVE_SOMETHING'
Modify the contents of AddSystem.pm to contain:
package AddSystem;
@ISA = qw(Exporter DynaLoader);
@EXPORT = qw( );
$VERSION = '1.0';
bootstrap AddSystem $VERSION;
use Foundation;
1;
and modify AddSystem.xs to have the contents:
#include <mach-o/dyld.h>
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#ifdef Move
#undef Move
#endif Move
#ifdef DEBUG
#undef DEBUG
#endif DEBUG
#ifdef I_POLL
#undef I_POLL
#endif I_POLL
#import <Foundation/Foundation.h>
@interface AddClient : NSObject
@end
@implementation AddClient
- (int)firstNumber { return 0; }
- (int)secondNumber { return 0; }
@end
@interface AddServer: NSObject
@end
@implementation AddServer
- (int)addNumbersForClient:(NSObject *)client { return 0; }
@end
MODULE = AddSystem PACKAGE = AddSystem
AddSystem.xs defines "dummy" AddClient and AddServer Objective-C classes that implement the methods that the Perl client and server will
provide. These dummy Objective-C classes are needed in this case because there would otherwise not be enough information for the DO runtime
system to determine the numbers, types, and sizes of the method arguments and return values. These dummy Objective-C implementations are
usually only needed when DO is being used and the Perl program does not link against any libraries that contain objects that already
implement the methods. The actual method bodies are irrelevant and can be trivially defined to return 0 or NULL. In the case of methods
that return void, the dummy methods can have empty bodies.
After modifying Makefile.PL, AddSystem.pm, and AddSystem.xs, execute the following commands (as root or as an admin user):
perl Makefile.PL
make install
Now add two Perl programs to the AddSystem directory. The first program is addServer:
#!/usr/bin/perl
use AddSystem;
package AddServer;
@ISA = qw(PerlObjCBridge);
@EXPORT = qw( );
PerlObjCBridge::preloadSelectors('AddClient');
sub new
{
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}
sub addNumbersForClient_
{
my($self, $client) = @_;
my $first = $client->firstNumber();
my $second = $client->secondNumber();
return int($first + $second);
}
$server = new AddServer;
$connection = NSConnection->defaultConnection();
$connection->setRootObject_($server);
$connection->registerName_(NSString->stringWithCString_("AddServer"));
NSRunLoop->currentRunLoop()->run();
Make sure that the line "#!/usr/bin/perl" does not contain leading whitespace.
The line:
use AddSystem;
causes addServer to load the AddSystem XSUB module, which in turn loads the dummy AddClient and AddServer Objective-C classes, thus making
them available to the DO runtime system. The lines:
package AddServer;
@ISA = qw(PerlObjCBridge);
@EXPORT = qw( );
cause the AddServer package to inherit from PerlObjCBridge. As a consequence, messages to and from AddServer objects will be routed through
PerlObjCBridge.
The line:
PerlObjCBridge::preloadSelectors('AddClient');
instructs PerlObjCBridge to pre-cache all method selectors for the Objective-C class AddClient. By doing this, PerlObjCBridge is "primed"
with the information needed to send messages to objects of class AddClient.
After a standard "new" constructor method, there is a addNumbersForClient_ method that provides the service vended by the AddServer class.
The method name "addNumbersForClient_" corresponds to the Objective-C selector "addNumbersForClient:", which has a dummy implementation in
AddSystem.xs. In addition to the standard $self argument, addNumbersForClient_ takes a second argument $client which is a reference to the
invoking client object. The client object is then sent the messages "firstNumber" and "secondNumber", each of which returns an integer. The
server adds the two numbers and returns the result.
The lines:
$server = new AddServer;
$connection = NSConnection->defaultConnection();
$connection->setRootObject_($server);
$connection->registerName_(NSString->stringWithCString_("AddServer"));
create a new AddServer object and set it as the root object of a DO connection, registered with the name "AddServer". Clients can then look
up the name "AddServer" to connect to this object.
The final line:
NSRunLoop->currentRunLoop()->run();
puts addServer into a event loop, waiting for incoming connections from clients.
The second program, addClient, consists of:
#!/usr/bin/perl
use AddSystem;
package AddClient;
@ISA = qw(PerlObjCBridge);
@EXPORT = qw( );
PerlObjCBridge::preloadSelectors('AddServer');
sub new
{
my $class = shift;
my $self = {};
bless $self, $class;
$self{'firstNumber'} = shift;
$self{'secondNumber'} = shift;
return $self;
}
sub firstNumber
{
my($self) = @_;
return $self{'firstNumber'};
}
sub secondNumber
{
my($self) = @_;
return $self{'secondNumber'};
}
die "usage: perlClient <firstNumber> <secondNumber>
" unless @ARGV == 2;
# create client
$client = new AddClient (@ARGV);
# create connection to server
$name = NSString->stringWithCString_("AddServer");
$server = NSConnection->rootProxyForConnectionWithRegisteredName_host_($name, 0);
if (!$server or !$$server) {
print "Can't get server
";
exit(1);
}
$server->retain();
printf "%d
", $server->addNumbersForClient_($client);
Make sure that the line "#!/usr/bin/perl" does not contain leading whitespace.
The AddClient methods "firstNumber" and "secondNumber" implement the call-back methods invoked by the AddServer. The lines:
$name = NSString->stringWithCString_("AddServer");
$server = NSConnection->rootProxyForConnectionWithRegisteredName_host_($name, 0);
if (!$server or !$$server) {
print "Can't get server
";
exit(1);
}
$server->retain();
results in $server being assigned a DO "proxy" object for the AddServer object in the addServer program. Any messages sent by the client
will by forwarded by the DO proxy to the actual AddServer object in the addServer address space.
The final line:
printf "%d
", $server->addNumbersForClient_($client);
invokes the AddServer object with a reference to the client object. The control flow that results is:
addClient sends "addNumbersForClient:" to addServer
addServer sends "firstNumber" to addClient
addClient returns first number
addServer sends "secondNumber" to addClient
addClient returns second number
addServer returns sum of first and second number
To execute these programs, first make sure addServer and addClient are executable:
chmod +x addServer addClient
Next run the server in one shell:
addServer
then the client in another shell:
addClient 1 2
3
AUTOMATIC STRING CONVERSION
For convenience, PerlObjCBridge automatically converts Perl strings into NSString Objective-C objects when an NSObject is expected as the
argument to an Objective-C method. For example, suppose an Objective-C dictionary is created:
$dict = NSMutableDictionary->dictionary();
The dictionary method "setObject:forKey:" expects the key argument to be an NSString and the value argument to be any NSObject. The
following automatically converts both "aKey" and "aValue" to NSStrings and then inserts the pair into the dictionary:
$dict->setObject_forKey_("aValue", "aKey");
The value can be retrieved as follows, where "aKey" is again automatically converted to an NSString:
$value = $dict->objectForKey_("aKey");
printf "value is %s
", $value->cString();
Note that the return value assigned to $value is a reference to an NSString and is not automatically converted to a Perl string. The
automatic conversions occur only from Perl strings to NSStrings for Objective-C method arguments. NSStrings return values are not
automatically converted to Perl strings.
Automatic conversion also occurs when a Perl string is passed as an argument to a method that expects an Objective-C selector. For example,
the "performSelector:" message can be sent to any NSObject. The argument to the "performSelector:" message must be an Objective-C selector.
In Objective-C, one can copy an existing NSString "origString" by asking it to perform the "copy" selector:
copy = [origString performSelector:@selector(copy)];
This is equivalent to:
copy = [origString copy];
In Perl the selector form can be executed as:
$copy = $origString->performSelector_("copy");
In this case the Perl string "copy" is automatically converted to an Objective-C selector.
NIL ARGUMENTS AND RETURN VALUES
It is sometimes necessary to pass the Objective-C object ID "nil" (a null pointer) as an argument to an Objective-C method. Since
PerlObjCBridge represents Objective-C ID's as Perl references, strictly speaking the Perl value 0 is not a legal representation for
Objective-C's nil because it is a simple scalar, not a reference. However, for convenience, when an argument to an Objective-C method is
expected to be an object ID and the value 0 is passed from Perl, PerlObjCBridge coerces the 0 value to a reference to a zero-valued scalar
and the Objective-C method receives nil for that argument. In the following example, the Objective-c method "arg1:optionalArg:" would
receive nil as its second argument.
MyClass->arg1_optionalArg_($obj, 0);
The special value "undef" can also be used:
MyClass->arg1_optionalArg_($obj, undef);
When an Objective-C method returns nil, the corresponding Perl return value is a reference to a zero-valued scalar. This return value can
subsequently be passed as an argument to an Objective-C method. In the following example, if "aMethod" returns nil then "arg1:optionalArg:"
would receive nil as its second argument:
MyClass->arg1_optionalArg_($obj, YourClass->aMethod());
To determine whether an Objective-C method returned nil one should test both the Perl reference and its referent. The referent will be
zero-valued when the Objective-C method returned nil, but it is also possible for the reference itself to be undefined (for example, when
the method raised an NSException, as discussed below). The following example illustrates the use of an Objective-C NSEnumerator object to
print the elements of an NSArray. In Objective-C, the enumerator returns nil after the last object in the array has been enumerated. In the
Perl loop, both the reference $obj and the referent $$obj are tested in the loop condition. Under normal circumstances looping ends when
$$obj becomes zero-valued, indicating the enumerator returned nil.
$enumerator = $array->objectEnumerator();
while ($obj = $enumerator->nextObject() and $$obj) {
printf "%s
", $obj->description()->cString();
}
ARGUMENTS THAT RETURN OBJECTS BY REFERENCE
Sometimes Objective-C methods return objects to the caller using return-by-reference arguments. For example, the following method defines
an argument that returns an NSError object by reference.
- (BOOL)doSomethingAndReturnError:(NSError **)error;
When the doSomethingAndReturnError: method fails it returns a Boolean value of NO and (optionally) also returns a by-reference NSError
argument. An Objective-C caller of this method will typically do something like the following.
NSError *error = nil;
BOOL result = [anObject doSomethingAndReturnError:&error];
if ( ! result ) {
if ( error ) {
NSLog(@"error code is %d", [error code]);
}
}
Return-by-reference arguments can be used from Perl as illustrated by the following example.
my $errorVal; # declare $errorVal as an uninitialized scalar
my $error = $errorVal; # initialize $error as a reference to $errorVal
my $result = $anObject->doSomethingAndReturnError_($error);
if ( ! $result ) {
if ( $$error ) {
printf "error code is %d
", $error->code( );
}
}
Note: the following does not work.
my $errorVal;
my $result = $anObject->doSomethingAndReturnError_($errorVal);
The following does not work either.
my $errorVal;
my $result = $anObject->doSomethingAndReturnError_($errorVal);
Pass undef to avoid returning the Objective-C object:
my $result = $anObject->doSomethingAndReturnError_(undef);
POINTERS TO BUFFERS
For Objective-C methods such as -[NSData getBytes:length:] it is necessary to pass an argument that represents a pointer to a buffer of
known length. This may be accomplished using the pack() and unpack() functions, as shown in the following example. The "L!" template
forces the unpack() function to return a native-length unsigned long value, which is always the same size as a pointer in Mac OS X.
In this example, assume $nsData points to a valid NSData object:
my $buffer;
if ($nsData && $$nsData) {
my $length = $nsData->length();
$buffer = '