AndreaSmalltalkLecture:QnA 07
- 질문과 답변-7
- :Objective-C의 개략적 개념
Objective-C의 개략적 개념
음, 저도 Objective-C가 구체적으로 어떤 문법을 가지고 있는지는 잘 모 릅니다. 그저 간단하게 제가 알고있는 사항을 정리해드린다면...
우선 Objective-C는 보통 C언어에다가 Smalltalk를 합쳐놓았다고 생각하면 쉽습니다. 즉 C++는 OOP를 표현하기 위해서 C언어의 문법을 확장했지만, Objective-C는 C언어의 문법을 거의 그대로 사용하면서 OOP를 표현하고 싶을 때에는 Smalltalk의 형식을 빌었다고 생각됩니다. 이를테면
if ( [anObject isKindOf: Integer] ) return -1;
뭐 이런 식으로 코드가 적용된다고 알고 있습니다. 즉 C언어식으로 코딩을 하다가 OOP의 방식을 사용해야 할 경우에는 대괄호[ ] 안에 명령을 적어주는 식입니다. 흡사 Smalltalk에서 대괄호 안에 명령문을 적어넣어서 덩이(block)를 만드는 것과 같다고 보면 됩니다.
그런데 이 Objective-C 는 두 개의 패러다임이 전혀 융화되지 않은 상태이고, 즉 물과 기름을 섞어놓은 듯한 상태이고, 또한 사용자들은 C 라는 철학과 OOP라는 철학을 함께 익히고 있어야 하므로 문법이 매우 복잡해 진다는 단점을 가지고 있습니다. 즉 이거면 이거, 저거면 저거가 아니라 물에 물 탄듯, 술에 술탄듯 하는 형상이라는 것이지요.
뭐 C++도 별반 칭찬해줄 것은 없지만, 그래도 C++는 나름대로 C언어 안에서 OOP를 표현해보려고 시도를 한 셈이지요. 그러나 Objective-C 의 경우는 그게 그렇지가 않았으니까요. 한 가지 예를 더 들자면, 대괄호 안에서는 Smalltalk처럼 자료형에 대한 개념이 없으며(즉 변수에 자료형을 지정하지 않음), 대괄호 밖에서는 C언어처럼 자료형을 따집니다.
아무튼 C++과 맞먹을 정도로 황당한 언어가 Objective-C라고 생각합니다.
Objective-C의 셈플코드
Objective-C의 셈플 소스코드랍니다. 저도 아직 다 이해는 못해봤지만, 도움이되셨으면 좋겠습니다.
http://www.cis.ohio-state.edu/hypertext/faq/usenet/Objective-C/sample/faq.html 의 내용을 발췌한 것입니다.
// This is a comment, just like the previous line. Everything to the right
// of a double slash is ignored.
/* Classes are the one real extension which Objective-C adds to C. A class
is a description of a collection of data, like a C structure, and the
methods by which that data may be accessed or manipulated. Instances of
a class are called objects, and methods are invoked by sending messages
to either the class itself, to produce objects, or to those objects. The
recipient of a message is called a "receiver". The form of a message is:
[receiver method andMaybeSomeArguments]
the receiver and method components are mandatory, as are the square
brackets surrounding the message. Additional arguments may or may not be
present, depending upon the method definition. Messages may appear
anywhere a statement is allowed in C.
The first thing we do is bring in some include files, as in C. On the
NeXT, it is customary to use the "import" statement which guarantees that
the file isn't included more than once. Using GNU CC this is not all
that nice sinds it generates a huge warning for every file being
compiled. So, since it does not really matter, we'll stick to
`#include'. */
#import <stdio.h>
#import <objc/Object.h>
#import "Queue.h"
#import "Stack.h"
/* That brought in class definitions for Objects, Queues, and Stacks. The
Object class is the basis for all other classes, which is why it gets
brought in first. It provides basic functional behavior which is
inherited by all derived classes. All user created classes normally have
Object somewhere in their ancestry.
Queue and Stack are classes of our own construction, and provide FIFO and
LIFO storage capabilities, respectively. I'm not going to go into
implementation details here. It's irrelevant how they work, all that is
important is that they both respond to 'put:' and 'get'. If you want to
inspect them, look into the Queue.m, Stack.m, Queue.h and Stack.h files.
A simple Class definition follows. It inherits directly from the base
class "Object". This gives it lots of nice properties, not the least of
which is the ability to be referenced by any pointer of the generic
object type "id". All objects can be pointed to by any id variable, and
the default return type from methods is id. This allows messages to be
embedded in other messages, either as receivers or arguments.
An Int object allocates space for a single integer. The "report" message
causes it to report its value. Everything between the @implementation
and the @end is part of the class definition.
Note - It is *highly* unusual to have a class implementation in your main
program. Since the object is fully defined before it gets used, no
interface description is required. There is nothing illegal about doing
things this way, but it is so unusual that the compiler will produce a
warning for this class. The Int class implementation is here solely for
expository purposes. */
@implementation Int: Object // Int is derived from Object
{
int value; // This is the data portion. Like a struct.
}
/* The following are the method definitions. A `+' prefix means it is a
factory method, i.e., how to manufacture instances of the class. The
body of the method is between braces, like a C function.
This class doesn't define any factory methods. It relies on the +alloc
method defined in class Object. For examples of factory methods, look at
the +new method defined in the Stack or Queue implementations.
Self is a special variable, which refers to the object currently being
manipulated. Super refers to the parent class of self. The following
method asks the parent class (Object) to hand us a new instance, which
becomes self. Then we update the instance variables and return a pointer
to the new object.
It is standard for methods that do not need to return any special value
to instead return self. This allows for a nested syntax of method calls.
The "-" in front of init means that it's an instance method, i.e.,
something a particular object should respond to. */
-init: (int) i
{
/* Have our superclass initialize its part of us. After that,
initialize the part of us introduced by this class (Int). */
[super init];
value = i;
return self;
}
-report
{
printf ("%4d", value);
return self;
}
@end
/* We have implemented Float and Char classes more traditionally, using
separate files for the interface (.h) and implementation (.m). The Float
and Char objects are like the Int object, but with the obvious difference
that they work with floats and characters. We include the interface
definitions at this point. */
#import "Float.h"
#import "Char.h"
/* If you inspect those files, note polymorphism -- methods have same
names as in the Int class. */
int main (void)
{
/* First create instances of "Stack" and "Queue" data structures. */
id queue = [[Queue alloc] init];
id stack = [[Stack alloc] init];
int i, reply;
fprintf (stderr, "Include the Char class in the demo? (y/n): ");
/* Anything not matching `y.*' means no. */
reply = getchar ();
for (i = 5; i > -6; --i)
{
/* Depending on which version of the demo we're running, we
alternately put Ints and Floats onto the queue and stack, or
Ints, Floats, and Chars. */
if (reply == 'y')
{
/* If I is odd we put an Int on the queue and a Char on the
stack. If I is even we put an Char on the queue and a Float
on the stack.
Since there is more than one method `-init:' and since
`+alloc' returns a plain, typeless, `id', the compiler
doesn't know the type of the object returned by alloc. An
explicit cast (i.e. static type indication) ensures that the
compiler knows which `init:' is invoked---the one accepting a
char or the other one accepting an int.
Another solution, which avoids the static type indication, is
to put typing information on the method in the method's name.
This is done for the Float class. */
id my_char = [(Char *) [Char alloc] init: 'm' + i];
if (i & 1)
{
[queue put: [(Int *) [Int alloc] init: i]];
[stack put: my_char];
}
else
{
[queue put: my_char];
[stack put: [[Float alloc] initFloatValue: i]];
}
}
else
{
/* If I is odd we put an Int on the queue and a Float on the
stack. If I is even we put a Float on the queue and an Int
on the stack. */
[queue put: ((i & 1)
? [(Int *) [Int alloc] init: i]
: [[Float alloc] initFloatValue: i])];
[stack put: ((i & 1)
? [[Float alloc] initFloatValue: i]
: [(Int*) [Int alloc] init: i])];
}
}
while ([queue size] && [stack size])
{
/* The following illustrates run-time binding. Will report be
invoked for a Float object or an Int object? Did the user elect
for Char objects at run time? We don't know ahead of time, but
with run-time binding and polymorphism it works properly. The
burden is on the class implementer rather than the class user.
Note that the following lines remain unchanged, whether we are
using the Char class or not. The queue and stack hand us the
next object, it reports itself regardless of its type, and then
it frees itself. */
printf ("queue:");
[[[queue get] report] free];
printf (", stack:");
[[[stack get] report] free];
putchar('\n');
}
return 0;
}
__EOF__