상세 컨텐츠

본문 제목

오브젝티브-C 2.0(Objective-C) FAQ 모음

카테고리 없음

by 비제이퍼블릭 2009. 9. 11. 08:30

본문

Objective-C 2.0(오브젝티브-C 2.0)
카테고리 컴퓨터/IT
지은이 마크 달림플 (비제이퍼블릭, 2009년)
상세보기


웹 서핑중에 우연히 오브젝티브-C를 처음 시작하신는 분들이 가지는 궁금증을 어느 정도 해결해 줄만한 FAQ를 발견했습니다.
번역을 해서 올릴까 0.1초 동안 생각을 했지만 능력 부족으로 포기하기로 하였습니다..^^;




The Objective-C Language

What is Objective-C?

Objective-C was invented in the early 1980s by Dr. Brad Cox. The language was developed as an Object-Oriented layer on top of the popular C programming language (in this FAQ and in much other documentation on Objective-C, knowledge of C or a related language is assumed). Cox's original implementation was a preprocessor which generated C code, and was marketed by Stepstone.
Objective-C is a true superset of C, so the language may vary depending on what version of the C language is supported by the compiler. The additions to C are few: a handful of new keywords, three extra types (of which only one, id, is frequently used) and the message-sending syntax.


Where is the Objective-C standard?

There is no standards document describing the Objective-C language as there is for C, C++ and other languages. The canonical descriptions of the language are therefore its implementations; see below.

What variants of the language exist? How common are they?

By far the most popular and actively-developed variant of Objective-C is that maintained by Apple, based on the GNU C Compiler. The 'vanilla' GNU C Compiler from the Free Software Foundation also supports the language; its implementation is very similar to that provided by Apple although newer features take some time to trickle down, and the two runtimes are not compatible with each other. David Stes' POC implementation is incompatible with the above two, and doesn't contain many of the features of Apple's Objective-C implementation. On the other hand, it does provide blocks (a Smalltalk feature, also found in Ruby) and class variables. POC is not considered further in this FAQ, due to its incompatibility with the popular Objective-C variants.

What is Objective-C 2.0?

With Mac OS X version 10.5 ("Leopard"), Apple introduced some incremental features to the Objective-C language. These features are being marketed as "Objective-C 2.0" and shall be referred to here as ObjC 2. See the section on ObjC 2 below, or where an answer needs further consideration under ObjC 2 discussion will appear in this font style. Currently ObjC 2 features are only available on the Apple runtime in Mac OS X version 10.5. The new features are entirely "opt-in" and there is no compatibility break with the existing Objective-C language.



Objective-C Development Tools

How are Objective-C source files named?

The organisation of Objective-C source is typically similar to that of C or C++ source code, with declarations and object interfaces going into header files named with a .h extension, and definitions and object implementations going in files named with a .m (short for methods) extension.

What compilers are available?

The ubiquitous gcc is really the one Objective-C compiler (though there are two flavours, discussed below) found on almost all platforms.
Historically, NeXT Computer (latterly NeXT Software) and Stepstone produced Objective-C compilers, however both companies no longer exist. NeXT's acquisition of Stepstone's Objective-C rights and Apple's subsequent acquisition of NeXT mean that Apple Computer now control their respective interests in Objective-C. Metrowerks also used to have an Objective-C compiler in their CodeWarrior development environment, but this is now defunct too.

What development environments are available?

Objective-C files can be edited in standard text editors, and editors such as emacs have syntax highlighting modes for the language. Integrated Development Environments with support for the Objective-C language include Apple's Xcode and GNUstep's ProjectCenter.



Class Libraries

What is a class library?

With just an Objective-C compiler it's possible to go ahead and code, but you'd have to reinvent a lot of wheels before getting to any interesting programming. A class library provides a pre-made set of objects and classes, usually offering functionality such as containers, data handling and user interfaces.

What are some of the class libraries available?

Many of the class libraries available for Objective-C are derived from OpenStep, by NeXT Computer and Sun Microsystems. These include Apple's Mac OS X-specific Cocoa, and the cross-platform GNUstep. Both of these provide class libraries with a Foundation (containers, strings, networking/IPC etc) and AppKit (user interface components), as well as their own specific classes. Implementations of just the Foundation part include OpenDarwin's libFoundation.
Swarm is an Objective-C framework for agent-based modeling.



The Objective-C Preprocessor

What's this #import thing?

#import is a pre-processor directive similar to C's #include, except that it will not include the same file twice. This makes #ifdef guarding inside header files unnecessary. Some versions of gcc emit warnings when #import is used; you can turn those off with -Wno-import.

How do I use comments in Objective-C?

The same comment styles that C supports may be used. They are comments which start with // and run to the end of a line:

// this is a BCPL-style comment

and comments which start with /* and end with */:

/* this is a C comment
 * it spans multiple lines
 * note that comments of this type cannot be nested!
 */



Objects And Classes

What type is an object?

The Objective-C keyword id is a type corresponding to a reference to any object. More rigourous type-checking can be enforced by using a pointer to a specific class, such as MyClass *. Any object whose class is MyClass or one of its subclasses can then be used; an object of any other class used here would cause an error.

What do self and super mean?

self is a special variable available in method implementations to refer to the current object (i.e. the one which received the message to invoke this method). super can be used to have an object invoke its superclass's implementation of a method. One common use of both of these is in initialisation:

-(id)init {
  if((self=[super init])) { // invoke superclass -init
    someIvar=[[MyClass alloc] init];
  }
  return self; //i.e. return this object
}

What are class objects?

The object representing the class of another object can be returned by sending the -class message, as in:

MyClass *someObject=[[MyClass alloc] init];
id aClass=[someObject class];

Class objects behave as instances of the root class (i.e. [aClass class] will return the root class). The object aClass is now the class object MyClass, and responds to class messages in the same way that sending messages to MyClass behaves; e.g. [[aClass alloc] init] will create a new instance of MyClass.

What is nil ?

nil is the null object, used to refer to uninitialised or cleared objects (and Nil is the null class object). Unlike in other languages, in Objective-C it is legal to send messages to nil ; the return value is nil for type id , 0 for simple C types such as int , and undefined for compound types such as struct . Both nil and Nil are defined as (id)0 .

What is @defs?

The @defs() keyword provides a list of C declarations of the in-memory layout of an object of a given class. It can therefore be used in generating a C API to Objective-C objects:

struct MyClass_t {
  @defs(MyClass)
};

Note that using @defs is generally frowned upon because it breaks encapsulation, although if a library were to use @defs on an object within that library then this problem would not arise.

How do I stop objects from sending specific messages?

In short, you don't. You could have the parameters of a message include context information (such as -(id)doSomething:(id)sender) but then you're still dependent on the sender providing accurate context.



If I changed the instance variables of a class, do I need to recompile everything?

No - in the FSF and 32-bit Apple (i.e. NeXT) runtime environemnts, you should normally just have to recompile that class and all subclasses. Any C source which uses @defs() (or anywhere else you directly access instance variables) should be recompiled, as the in-memory layout of the class will have changed. Any parts of your code which just access the instance variables through accessor methods or KVC/KVO (see "What are key-value coding and key-value observing?" below) will not need recompiling.

The reasons for recompilation above are known as the "fragile ivar" or "fragile base class" problem. In those runtimes, the position of an instance variable in an object is determined by the compiler, based on the size and layout of all preceding ivars including those in the object's superclasses. The 64-bit Apple runtime for Objective-C 2.0 doesn't suffer from the fragile base class problem; memory locations of instance variables are not determined in the same way. Therefore the above discussion doesn't apply to that environment. Note that the 64-bit environment doesn't support @defs() at all, so discussion of its behaviour is moot.

Objective-C 2.0(오브젝티브-C 2.0)
카테고리 컴퓨터/IT
지은이 마크 달림플 (비제이퍼블릭, 2009년)
상세보기



Categories

What is a category?

A category is a way to add methods to a class which already exists. It appears in code as a second set of @interface and @implementation declarations, which contain methods that get added to the class when the category is loaded at runtime. Categories are only available in compilers which implement the gcc variant of Objective-C.

How do you write a category?

The interface declaration for a category looks like:

@interface ClassName (CategoryName)
  -method;
@end

The category name has no influence at runtime, but it must be unique with respect to other categories on the same class, and it will appear in stack traces.
The implementation declaration is similar:

@implementation ClassName (CategoryName)
  -method {...}
@end

What's the point of categories?

Categories can be used to split a single class's implementation into multiple files by splitting the implementation into categories and putting each one into a different file. Categories can also be used to add methods to a class whose source code you don't control, for example adding a -md5Sum method to a data container that's part of your class library.

What if multiple categories implement the same method?

Then the fabric of the Universe as we know it ceases to exist. Actually, that's not quite true, but certainly some problems will be caused. When a category implements a method which has already appeared in a class (whether through another category, or the class' primary @implementation), that category's definition overwrites the definition which was previously present. The original definition can no longer be reached by the Objective-C code. Note that if two categories overwrite the same method then whichever was loaded last "wins", which may not be possible to predict before the code is launched.

Real problems can occur if the category implements a method with for an existing selector but with a different method signature than the replaced method, depending on the ABI of the running platform code expecting either behaviour may no longer work, and certainly at least one of them is broken.



Protocols

What is a protocol?

A protocol is basically an interface without an implementation. It's a set of messages that are bundled up together in a convenient package. You can then use the protocol to declare that a class implements the entire set of messages, and check objects to see if they conform to the protocol. You can also use protocols in a variable type declaration to show that you need a certain set of methods but don't care about the actual class of the object.

How do I declare a protocol?

You declare a protocol like this:

@protocol ProtocolName
-method;
@end

It's possible for a protocol to inherit from another protocol, which works much like classes where the "parent" protocol's methods get added to the "child". In that case, the first line of the declaration would look like:

@protocol ProtocolName <ParentProtocol>

How do I declare a class to conform to a protocol?

Write the class's interface declaration like this:

@interface ClassName <ProtocolName>

To declare conformance to multiple protocols, just separate the protocol names with commas.

My class implements all the methods in a protocol, but I get a compiler warning.

By default, when faced with a class like:

@interface MyClass : NSObject <AProtocol>

gcc will only look in the interface of MyClass for methods from the AProtocol protocol, and will issue a warning for each method not found there, even if the method is implemented by a superclass. Giving the -Wno-protocol argument to gcc makes it search the class hierarchy for protocol methods.

How do I check whether an object conforms to a protocol?

For Object, write:

[obj conformsTo:@protocol(ProtocolName)]

For NSObject, write:

[obj conformsToProtocol:@protocol(ProtocolName)]

How do I declare a variable type to conform to a protocol?

To declare a variable which conforms to a protocol but otherwise has no restrictions on type, write:

id <ProtocolName> obj;

To declare a variable which is an instance of a certain class (or one of its subclasses) and which also conforms to a protocol, write:

ClassName <ProtocolName> *obj;

Note that this construct is rare.

What's an "informal protocol"?

An informal protocol isn't actually a protocol at all, but rather a category interface declaration with no corresponding implementation. Informal protocols are frequently used in Cocoa to declare a set of methods to the compiler in a situation where there is no point in having a formal protocol, for example where many methods are optional.

What's a SEL? How do I get one?

SEL is a C type referring to a method selector (i.e. they uniquely identify particular messages). They can be obtained through use of the @selector() keyword:

SEL aSelector=@selector(doSomethingWithStuff:);

or through use of a runtime function. The appropriate function is sel_getUid() on Apple gcc or sel_get_any_uid on GNU gcc.

What do perform: and performSelector: do?

They send the message corresponding to the specified selector to an object. That is, [obj performSelector:@selector(message)] is the same as writing [obj message] . The difference is that perform: and performSelector: can be passed a variable as its argument, whereas a plain [obj message] must always send the same message.

What is an IMP? How do I get one?

IMP is a C type referring to the implementation of a method, also known as an implementation pointer. It's a pointer to a function returning id, and with self and a method selector (available inside method definitions as the variable _cmd) as the first arguments:

id (*IMP)(id, SEL, ...);

With NSObject, you can obtain the IMP for a given method by doing:

IMP imp=[obj methodForSelector:@selector(message)];

For Object, do:

IMP imp=[obj methodFor:@selector(message)];

How do I send a message given an IMP?

Dereference it, as with a C function pointer:

id anObject, theResult;
IMP someImp;
SEL aSelector;
// ...
theResult=someImp(anObject,aSelector);

Can I use IMP for methods returning non-idtypes?

IMPs are pointers to methods which return id. Just using one with a SEL that doesn't return id could cause trouble, so you should provide a new prototype and cast to that instead:

int (*foo)(id,SEL);
int result;
foo=(int(*)(id,SEL))[anArray methodForSelector:@selector(count)];
result=foo(anArray,@selector(count));

If you want to get an IMP using the Objective-C runtime functions, then use objc_msg_lookup(id,SEL) on the GNU runtime (thanks to Justin Hibbits for explaining this) and on the NeXT/Apple runtime, you could use class_getInstanceMethod(Class,SEL) to get a pointer to a struct objc_method, the method_imp member of which is the required IMP.

Can I use SEL for methods returning non-id types?

You can do this by either using your runtime's message sending functions directly, or by obtaining the IMP for the method and casting it as discussed in the previous question.

In the Apple/NeXT runtime, the message sending function is objc_msgSend(id,SEL,...), but beware the many variants which need to be used depending on the return type of the method. See /usr/include/objc/objc-runtime.h or Objective-C Runtime Referencefor more details. A simple example might look like this:

        typedef int (*IntReturn)(id,SEL);
	NSArray *ary=[NSArray alloc];
	SEL initSel=@selector(initWithObjects:),countSel=@selector(count);
	//id returned here
	ary=objc_msgSend(ary,initSel,@"Hello",@"World",nil);
	//int returned here
	c=((IntReturn)objc_msgSend)(ary,countSel);
	NSLog(@"%@: %d",ary,c);

In the GNU runtime, you could use objc_msg_sendv(id,SEL,arglist_t), which is used for sending any message but requires more setup than objc_msgSend() as a calling frame must be constructed. This example is based on the GNUstep DO code, and as usual doesn't show any memory management (i.e. the calling frame should be destroyed).

        NSArray *ary=[[NSArray alloc] initWithObjects:@"Hello",@"World",nil];
        int *cp;
        SEL countSel=@selector(count);
        Method_t meth=class_get_instance_method([ary class],countSel);
        arglist_t frame=objc_calloc(8,1);
        const char *rettype=objc_skip_type_qualifiers(meth->method_types);
        int stackargs=atoi(objc_skip_typespec(rettype));
        frame->arg_ptr=stackargs?objc_calloc(stackargs,1):0;
        cp=(int *)objc_msg_sendv(ary,countSel,frame);
        NSLog(@"%@: %d",ary,*cp);

Actually it is always simpler just to get the IMP and cast to the appropriate return type, as described in the previous question.

Objective-C 2.0(오브젝티브-C 2.0)
카테고리 컴퓨터/IT
지은이 마크 달림플 (비제이퍼블릭, 2009년)
상세보기



Exceptions

What is an exception?

An exception is an event which occurs in running a program which causes the normal flow of the program to be interrupted. Programmers coming from other object-oriented languages such as Java will be familiar with exceptions. In Objective-C programming, it's typical to find exceptions used solely for programmer error (such as testing assertions), with user-visible errors being handled as part of the normal program flow.

How do I use exceptions?

Exceptions are available currently only with the NeXT gcc runtime; see "Why do people refer to GNU gcc and Apple/NeXT gcc?" below), and must be enabled with gcc's -fobjc-exceptions. Code which could throw an exception is contained in a @try block. This is then followed by one or more @catch blocks to process any exception thrown. A subsequent @finally block may contain code which should be run whether or not an exception occurred. An exception can be any object, and is thrown with the @throw keyword. An example:

@try {
  //code which may cause an exception to be thrown
  ...
}
@catch(MyExceptionClass *ex) {
  // handle a custom exception
  ...
}
@catch(id *ex) {
  // generic exception handler
  ...
}
@finally {
  // this will happen whether or not an exception occurred.
  ...
} 

In the above case, two @catch blocks exist; one for a specific type of exception and one for catching generic exceptions. The most specific handler should always come first. Be warned that exceptions are fragile to changes in the call stack such as goto or return.

Can exceptions be re-thrown?

Yes; invoke @throw() with no arguments in the @catch block, in order to re-throw the caught exception.

I've seen exception-handling code with macros like NS_DURING and NS_HANDLER . What's that?

Before gcc had an exceptions syntax for Objective-C, Cocoa provided its own exception-handling mechanism, based on macro wrappers for the standard C longjmp() and setjmp() functions. The implementation of Objective-C's language exceptions (i.e. @try and friends) is currently binary compatible with NS_DURING and friends.



Thread Synchronization

What is @synchronized?

@synchronized is a directive that supports simple locks for multithreaded programming. It is only available in the NeXT runtime by invoking the gcc -fobjc-exceptions flag (see Exceptions above). It allows an implicit associated lock with any object, without having to explicitly create or manage locks. If you are familiar with Java, it is basically identical to Java's synchronized construct.

How do I use @synchronized ?

The syntax is as follows:

@synchronized(object) {
  ...
}

This will lock on "object", meaning that any other threads which hit a @synchronized directive with the same object will not proceed until this thread has exited its @synchronized block. It is basically the same as this code:

[[object getLock] lock];
...
[[object getLock] unlock];

With the exception that the object itself is not responsible for having such methods or managing an explicit lock object.

What are the advantages?

Not having to explicitly create or destroy locks can make code easier to write and easier to read. @synchronized is also aware of return statements and exceptions, so you can be sure that the lock is always released even if the flow of control exits from the middle of the block.

What are the disadvantages?

@synchronized is slower than explicit locks, because the overhead of looking up the implicit lock is significant. It also always uses a recursive lock, which is slower than a non-recursive lock. It is also less flexible, not supporting condition variables or other advanced threading concepts.



The gcc Objective-C Compiler

Why do people refer to GNU gcc and Apple/NeXT gcc?

The version of gcc used by Apple (and NeXT before them) provides a different Objective-C implementation (specifically, the runtime is different) from that distributed by the Free Software Foundation. Darwin and Mac OS X ship with the Apple gcc, upon which Cocoa depends. Typically other operating systems will have the GNU Objective-C variant, and it is this which GNUstep requires. However the source to both versions of the compiler are available so you could build whichever you want for your platform. Because Apple uses its own version of gcc, the features available in the two variants may differ at any time. Be sure to check the documentation for your version of gcc, to make sure that a feature you expect does indeed exist.

How do I compile .m files?

Without any reliance on class libraries, an invocation such as:

gcc -c myfile.m -lobjc

will work. Check the documentation for your class library otherwise: for instance with Cocoa you might use:

gcc -framework Cocoa -c myfile.m

What is Objective-C++?

gcc supports the mixing of Objective-C and C++ in files named with a .mm extension, called Objective-C++. This allows, for example, Objective-C objects to be instance variables of C++ objects and vice versa. It does not allow Objective-C classes to inherit from C++ classes or vice versa. The Objective-C and C++ exception mechanisms are completely different too, so you cannot use a C++ catch to catch an @throw n exception.

What is the class of string constants?

If you use an Objective-C string literal such as @"Hello world!", then the class of the resulting object will usually be NXConstantString. This can be modified at compile-time though, using gcc's -fconstant-string-class argument. In Cocoa, the class of a string literal is NSConstantString. If you wish to change the constant string class, there is no restriction on the methods it implements but its ivar layout must be the same as that for N[SX]ConstantString (see objc/NXConstStr.h or Foundation/NSString.h) as its in-memory layout will be written into the executable by the compiler.



Objective-C 2.0 Features

What's the new for syntax?

NSArray *bar=...;
for (id foo in bar)
{
  [foo doSomething];
}

will send the -doSomething message to every object in bar. In this respect the new for syntax is conceptually similar to a familiar object enumeration:

NSArray *bar=...;
NSEnumerator *e=[bar objectEnumerator];
id foo;
while(foo=[e nextObject])
{
  [foo doSomething];
}

Although in fact the implementation is different so the new syntax can be much faster, especially on large collections. It also guards against the collection having mutated during the enumeration.

What is the NSFastEnumeration protocol?

NSFastEnumeration is the protocol to which collection classes must conform in order to support the ObjC 2 for..insyntax. It declares a single instance method, ��밹ountByEnumeratingWithState:objects:count: which provides a C array of objects used by the for..in loop.

More information on how NSFastEnumeration is used and how for..in works is in the Objective-C 2.0 language guide.

How do I start using the garbage collector?

In principle, this is as simple as turning on the -fobjc-gc or -fobjc-gc-only code generation flag for GCC in your compile process. Certainly for new code that's about the size of it. However, bringing existing code into the garbage-collected world may involve some subtleties.

In a garbage-collected environment, the traditional memory-management methods of -retain, -release and -autorelease are zeroed out; they not only don't do anything, they never get called. It's very rare for a custom object to override these methods (although Apple have recommended it in implementing the gang of four Singleton pattern), but very frequently an object will clean up any resources it uses in its -dealloc method. Again, this method never gets called in the garbage-collected environment. When an object is simply managing a collection of other objects (e.g. it has an NSString instance variable), then there's nothing to do; instance variables are by default strong references in the GC environment so the link between the object and the string will be honoured by the collector. When the object manages some custom memory, it must be capable of deallocating that memory when the object disappears.

One way of achieving this, though not a recommended way, is to implement a -finalize method. This method is guaranteed to be called once when the object disappears, but there are few guaranteed preconditions; in particular, linked objects may (or may not) have already been collected. The garbage collector must wait until the -finalize method is complete before it may continue, so performing time-consuming operations inside this method is also not recommended. An object must not cause itself to be "resurrected" inside -finalize, which could happen if it refers to itself in a message to another object. Better would be to try and ensure that all referenced memory is managed by the GC, using NSMakeCollectable() on Core Foundation references, or declaring non-id instance variables with the __strong keyword. For more information, see the Garbage Collection Programming Guide.

Objective-C 2.0(오브젝티브-C 2.0)
카테고리 컴퓨터/IT
지은이 마크 달림플 (비제이퍼블릭, 2009년)
상세보기



Cocoa and GNUstep

How do I include the root class?

#import <Foundation/NSObject.h>

For completeness, note that NSProxy is also a root class.

How can I forward messages between objects?

If an object receives a message it doesn't respond to, the runtime will also send it a -forwardInvocation: message before an error occurs. This means that you can override the method -(void)forwardInvocation:(NSInvocation *)inv in your class to have unrecognised messages forwarded to other objects; see Forwarding at developer.apple.com. You also need to override -(NSMethodSignature *)methodSignatureForSelector:(SEL)selector otherwise it returns nil and forwardInvocation: does not get called.

Here's a simple example of forwarding, which is used to internalise a loop performed on a collection class. That is, we're going to give collections a way to send a message to each of their contents. Firstly, a trampoline class CollectionTrampoline is required which, given a collection, forwards any message it receives to the contents of that collection. This is the interface for the class:

@interface CollectionTrampoline : NSProxy
{
	id theCollection;
}
- (id)initOnCollection:(id)aCollection;
@end

Here are the instantiation and deallocation methods:

- (id)initOnCollection:(id)aCollection
{
	theCollection=[aCollection retain];
	return self;
}

- (void)dealloc
{
	[theCollection release];
	[super dealloc];
}

As described above, we need to override -methodSignatureForSelector: to have forwarding work. There's no API provided for constructing method signatures, so here we find the method signature from the first object in the collection. If that doesn't work, then we supply a default method signature; that doesn't really matter because if we didn't already get a signature then either the collection is empty so no forwarding will occur, or the first object doesn't implement the method so we'll raise an exception before doing any work. Thanks to Michael Ash for supplying this example.

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
  NSMethodSignature *sig = nil;

  if([theCollection count])
    sig = [[theCollection objectAtIndex:0] methodSignatureForSelector:aSelector];

  if(!sig)
    sig = [NSObject instanceMethodSignatureForSelector:@selector(release)];

   return sig;
}

Now our implementation of -forwardInvocation:. Here, an NSEnumerator is used to iterate through the objects, and the method is invoked on each. In this simple example, the return values from the objects is not considered. A complete example would build up a collection of the responses and pass that back.

- (void)forwardInvocation:(NSInvocation *)anInv
{
	id i;
	NSEnumerator *en=[theCollection objectEnumerator];
	
	while(i=[en nextObject])
	{
		[anInv invokeWithTarget:i];
	}
}

Finally, the collection classes must have a way to return a trampoline object. Here's a category on NSArray to do that:

@interface NSArray (Trampolines)
- (id)do;
@end

@implementation NSArray (Trampolines)
- (id)do
{
	return [[[CollectionTrampoline alloc] initOnCollection:self] autorelease];
}
@end

Now this trampoline can be used to send a message to every object in a collection:

[[anArray do] doSomething];

How can I forward messages between remote objects?

This is known in Cocoa and GNUstep as Distributed Objects, or DO. DO can be used to send messages to an object in a different process, in a different thread in the same process or even to a process running on a different system. The server application vends an object by registering it with a name service. A client application acquires a connection to this vended object by querying the name service, when it receives a proxy for the vended object. From there, DO is transparent; messages sent to the proxy object get forwarded to the remote object and its result passed back to the proxy as if the local object itself were performing the methods. Here's an example, with comments, which shows a simple use of DO; for more information see Apple DO guide or GNUstep DO tutorial. This example does not use networked DO, nor does it properly take care of errors.

DOprotocol.h

This protocol is included in both the server and the client, and describes the messages which the vended object can respond to. In this case, there is only one such message.

@protocol DOExampleProtocol
-(NSNumber *)myPid;
@end

DOserver.m

The server process.

#import <Cocoa/Cocoa.h>
#import "DOprotocol.h"
#import <sys/types.h>
#import <unistd.h>

@interface VendedObject : NSObject <DOExampleProtocol>{
}
-(NSNumber *)myPid;
@end

@implementation VendedObject
-(NSNumber *)myPid
{
	NSLog(@"Vending out my pid...");
	return [NSNumber numberWithInt:getpid()];
}
@end

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	VendedObject *vo=[[VendedObject alloc] init];

        //get the default DO connection
	NSConnection *conn=[NSConnection defaultConnection];
	BOOL status;

        //set which object we vend, and register with the name server	
	[conn setRootObject:vo];
	status=[conn registerName:@"VendingServer"];
	if(status==NO)
	{
		NSLog(@"Couldn't register as VendingServer");
		exit(EXIT_FAILURE);
	}

        //now wait for connections
	[[NSRunLoop currentRunLoop] run];
	
    [pool release];
    return 0;
}

DOclient.m

This client just looks on the local machine for a DO server with the appropriate name, then gets the pid of the remote process.

#import <Foundation/Foundation.h>
#import <stdlib.h>
#import "DOprotocol.h"

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	
	//specify a protocol below for compiler type checking
	id <DOExampleProtocol> clientObject;
	NSNumber *serverPid;
	
	/*find a server with the appropriate name and get a proxy from it.
	 * using nil as the host means localhost-only.
	 */
	clientObject=(id <DOExampleProtocol>)[NSConnection rootProxyForConnectionWith
RegisteredName:@"VendingServer" host:nil]; /* -setProtocolForProxy: tells the DO mechanism that the remote object * conforms to the specified protocol. This means that it doesn't check * messages in that protocol before sending them. Other messages not in * the given protocol may still be sent. */ [clientObject setProtocolForProxy:@protocol(DOExampleProtocol)]; if(clientObject==nil) { NSLog(@"Error: did not get a proxy object for VendingServer service"); exit(EXIT_FAILURE); } serverPid=[clientObject myPid]; if(serverPid!=nil) { NSLog(@"Remote server on pid %@",serverPid); } else { NSLog(@"Error, did not get the server's pid"); exit(EXIT_FAILURE); } [pool release]; return 0; }



How does reference counting work?

I'm in fact going to take this as an opportunity to answer the related question, "how do I manage object lifecycles in (Cocoa|GNUstep|Cocotron)" when there's no garbage collector around? There are two steps to generating most objects, allocating the memory for an instance and then initialising that instance. Both steps must be performed to have a useful instance of an object, so the messages are usually chained:

GLObject *myObject = [[GLObject alloc] init];

The above line says "allocate memory for an instance of a GLObject, and initialise it". Note that this myObject is my object; because I created it I have declared an interest in the existence of myObject.

When creating a brand new class, there's a simple convention in the initialiser which gets all of the management details correct. -[GLObject init] would usually look like:

- (GLObject *)init
{
  if (self = [super init])
  {
    //do something with all of my own instance variables
  }
  return self;
}

This method first initialises the superclass (remember that an instance of a class can also be treated as an instance of its superclass), then performs any initialisation specific to this class. It only does that if the superclass' initialiser doesn't return nil, which is a conventional way to indicate that the initialisation didn't work.

Note that it's rare to override +alloc, which does all the runtime-level memory grabbing (actually it doesn't, it calls +allocWithZone:, and that does all the memory grabbing).

As mentioned above, I now own that initialised object. The other cases in which I own an object are if I create a -copy, or if I find some object that already exists, and I want it to stay around; in this last case I would send it a -retain message, which increments its reference count. If I don't want the object any more, I should -release it. The object will be deallocated (i.e. destroyed) only when all of the interested parties have released it; we say that the object's reference count has decreased to zero. When this happens, the object gets the -dealloc message. In an overridden -dealloc, the last task should be a call to [super dealloc].

Any other object is not owned by me. I therefore do not need to worry about disposing of it; it's someone else's responsibility. Frequently, objects which I receive without taking ownership are said to be autoreleased objects. That means that they have been sent the -autorelease message before I got them. There is a class in Cocoa called NSAutoreleasePool, which manages deferred releasing of objects.

A good article on reference counting is Hold Me, Use Me, Free Me.

Note that the FSF version of GCC can use the Boehm conservative garbage collector in managing Objective-C memory, and not the Apple garbage collector.

Ok, just One More Thing... I mentioned that allocation and initialisation are required in most cases. Of course, static objects (such as @"string constants" and @protocol()s) are written directly into the executable code and so never need initialising.

How do I memory-manage @"strings"?

I wrote in the refcounting answer that string constants - instances of NSConstantString declared like @"this" - are written directly into the application binary and therefore do not need to be initialised. Does this mean that they must be given special treatment in the reference-counted memory model?

No. In fact, if you treat string constants just like other objects as described above and in the referenced article, you won't run into memory management issues with them. In particular, this means that if you create a string by doing:

NSString *myString = @"Hello, world!";

then you are not responsible for disposing of it.

What are key-value coding and key-value observing?

In Cocoa, Key-Value Coding (KVC) and Key-Value Observing (KVO) provide an indirect route to inspecting and modifying properties of an object. The principle use of KVC and KVO is in Cocoa Bindings, an efficient approach to synchronising an application's user interface with its data model.

If the object myObject provides accessor methods such as -someVariable and -setSomeVariable , or an instance variable called someVariable or _someVariable , where someVariable is of type id , then the value of someVariable can be accessed (even if it's a derived property) through code such as:

[myObject valueForKeyPath:@"someVariable"];
[myObject setValue:@"Hello" forKeyPath:@"someVariable"];

Another object, such as myOtherObject , could be notified when the value of someVariable changes through KVO:

[myObject addObserver:myOtherObject forKeyPath:@"someVariable" options:NSKeyValue
ObservingOptionNew context:NULL];

now when someVariable is modified, myOtherObject will receive a -observerValueForKeyPath:ofObject:change:context: message.
See the KVO Programming Guide and the KVC programming guide.


Objective-C 2.0(오브젝티브-C 2.0)
카테고리 컴퓨터/IT
지은이 마크 달림플 (비제이퍼블릭, 2009년)
상세보기



댓글 영역