Class Objects
parentesys

Class Objects

This information is compiled and recorded in data structures made available to the runtime system. The compiler creates just one object, a class object, to represent the class. The class object has access to all the information about the class...

14 nov 2007


Class Objects

A class definition contains various kinds of information, much of it about instances of the class:

  • The name of the class and its superclass

  • A template describing a set of instance variables

  • The declarations of method names and their return and argument types

  • The method implementations

This information is compiled and recorded in data structures made available to the runtime system. The compiler creates just one object, a class object, to represent the class. The class object has access to all the information about the class, which means mainly information about what instances of the class are like. It’s able to produce new instances according to the plan put forward in the class definition.

Although a class object keeps the prototype of a class instance, it’s not an instance itself. It has no instance variables of its own and it can’t perform methods intended for instances of the class. However, a class definition can include methods intended specifically for the class object—class methods as opposed to instance methods. A class object inherits class methods from the classes above it in the hierarchy, just as instances inherit instance methods.

In source code, the class object is represented by the class name. In the following example, the Rectangle class returns the class version number using a method inherited from the NSObject class:

int versionNumber = [Rectangle version];

However, the class name stands for the class object only as the receiver in a message expression. Elsewhere, you need to ask an instance or the class to return the class id. Both respond to a class message:

id aClass = [anObject class];
id rectClass = [Rectangle class];

As these examples show, class objects can, like all other objects, be typed id. But class objects can also be more specifically typed to the Class data type:

Class aClass = [anObject class];
Class rectClass = [Rectangle class];

All class objects are of type Class. Using this type name for a class is equivalent to using the class name to statically type an instance.

Class objects are thus full-fledged objects that can be dynamically typed, receive messages, and inherit methods from other classes. They’re special only in that they’re created by the compiler, lack data structures (instance variables) of their own other than those built from the class definition, and are the agents for producing instances at runtime.

Note: The compiler also builds a “metaclass object” for each class. It describes the class object just as the class object describes instances of the class. But while you can send messages to instances and to the class object, the metaclass object is used only internally by the runtime system.

Creating Instances

A principal function of a class object is to create new instances. This code tells the Rectangle class to create a new Rectangle instance and assign it to the myRect variable:

id  myRect;
myRect = [Rectangle alloc];

The alloc method dynamically allocates memory for the new object’s instance variables and initializes them all to 0—all, that is, except the isa variable that connects the new instance to its class. For an object to be useful, it generally needs to be more completely initialized. That’s the function of an init method. Initialization typically follows immediately after allocation:

myRect = [[Rectangle alloc] init];

This line of code, or one like it, would be necessary before myRect could receive any of the messages that were illustrated in previous examples in this chapter. The alloc method returns a new instance and that instance performs an init method to set its initial state. Every class object has at least one method (like alloc) that enables it to produce new objects, and every instance has at least one method (like init) that prepares it for use. Initialization methods often take arguments to allow particular values to be passed and have keywords to label the arguments (initWithPosition:size:, for example, is a method that might initialize a new Rectangle instance), but they all begin with “init”.

Customization With Class Objects

It’s not just a whim of the Objective-C language that classes are treated as objects. It’s a choice that has intended, and sometimes surprising, benefits for design. It’s possible, for example, to customize an object with a class, where the class belongs to an open-ended set. In the Application Kit, for example, an NSMatrix object can be customized with a particular kind of NSCell object.

An NSMatrix object can take responsibility for creating the individual objects that represent its cells. It can do this when the matrix is first initialized and later when new cells are needed. The visible matrix that an NSMatrix object draws on the screen can grow and shrink at runtime, perhaps in response to user actions. When it grows, the matrix needs to be able to produce new objects to fill the new slots that are added.

But what kind of objects should they be? Each matrix displays just one kind of NSCell, but there are many different kinds. The inheritance hierarchy in Figure 1-3 shows some of those provided by the Application Kit. All inherit from the generic NSCell class:


Figure 1-3  Inheritance hierarchy for NSCell

Figure 1-3 Inheritance hierarchy for NSCell

When a matrix creates NSCell objects, should they be NSButtonCell objects to display a bank of buttons or switches, NSTextFieldCell objects to display fields where the user can enter and edit text, or some other kind of NSCell? The NSMatrix object must allow for any kind of cell, even types that haven’t been invented yet.

One solution to this problem is to define the NSMatrix class as an abstract class and require everyone who uses it to declare a subclass and implement the methods that produce new cells. Because they would be implementing the methods, users of the class could be sure that the objects they created were of the right type.

But this requires others to do work that ought to be done in the NSMatrix class, and it unnecessarily proliferates the number of classes. Since an application might need more than one kind of NSMatrix, each with a different kind of NSCell, it could become cluttered with NSMatrix subclasses. Every time you invented a new kind of NSCell, you’d also have to define a new kind of NSMatrix. Moreover, programmers on different projects would be writing virtually identical code to do the same job, all to make up for NSMatrix´s failure to do it.

A better solution, the solution the NSMatrix class actually adopts, is to allow NSMatrix instances to be initialized with a kind of NSCell—with a class object. It defines a setCellClass: method that passes the class object for the kind of NSCell object an NSMatrix should use to fill empty slots:

[myMatrix setCellClass:[NSButtonCell class]];

The NSMatrix object uses the class object to produce new cells when it’s first initialized and whenever it’s resized to contain more cells. This kind of customization would be difficult if classes weren’t objects that could be passed in messages and assigned to variables.

Variables and Class Objects

When you define a new class, you can specify instance variables. Every instance of the class can maintain its own copy of the variables you declare—each object controls its own data. There is, however, no “class variable” counterpart to an instance variable. Only internal data structures, initialized from the class definition, are provided for the class. Moreover, a class object has no access to the instance variables of any instances; it can’t initialize, read, or alter them.

For all the instances of a class to share data, you must define an external variable of some sort. The simplest way to do this is to declare a variable in the class implementation file as illustrated in the following code fragment.

int MCLSGlobalVariable;
 
@implementation MyClass
// implementation continues

In a more sophisticated implementation, you can declare a variable to be static, and provide class methods to manage it. Declaring a variable static limits its scope to just the class—and to just the part of the class that’s implemented in the file. (Thus unlike instance variables, static variables cannot be inherited by, or directly manipulated by, subclasses.) This pattern is commonly used to define shared instances of a class (such as singletons, see Creating a Singleton Instance).

static MyClass *MCLSSharedInstance;
 
@implementation MyClass
 
+ (MyClass *)sharedInstance
{
    // check for existence of shared instance
    // create if necessary
    return MCLSSharedInstance;
}
// implementation continues

Static variables help give the class object more functionality than just that of a “factory” producing instances; it can approach being a complete and versatile object in its own right. A class object can be used to coordinate the instances it creates, dispense instances from lists of objects already created, or manage other processes essential to the application. In the case when you need only one object of a particular class, you can put all the object’s state into static variables and use only class methods. This saves the step of allocating and initializing an instance.

Note: It is also possible to use external variables that are not declared static, but the limited scope of static variables better serves the purpose of encapsulating data into separate objects.

Initializing a Class Object

If a class object is to be used for anything besides allocating instances, it may need to be initialized just as an instance is. Although programs don’t allocate class objects, Objective-C does provide a way for programs to initialize them.

If a class makes use of static or global variables, the initialize method is a good place to set their initial values. For example, if a class maintains an array of instances, the initialize method could set up the array and even allocate one or two default instances to have them ready.

The runtime system sends an initialize message to every class object before the class receives any other messages and after its superclass has received the initialize message. This gives the class a chance to set up its runtime environment before it’s used. If no initialization is required, you don’t need to write an initialize method to respond to the message.

Because of inheritance, an initialize message sent to a class that doesn’t implement the initialize method is forwarded to the superclass, even though the superclass has already received the initialize message. For example, assume class A implements the initialize method, and class B inherits from class A but does not implement the initialize method. Just before class B is to receive its first message, the runtime system sends initialize to it. But, because class B doesn’t implement initialize, class A’s initialize is executed instead. Therefore, class A should ensure that its initialization logic is performed only once.

To avoid performing initialization logic more than once, use the template in Listing 1-1 when implementing the initialize method.

Listing 1-1  Implementation of the initialize method

+ (void)initialize
{
    static BOOL initialized = NO;
    if (!initialized) {
        // Perform initialization here.
        ...
        initialized = YES;
    }
}

Note: Remember that the runtime system sends initialize to each class individually. Therefore, in a class’s implementation of the initialize method, you must not send the initialize message to its superclass.

Methods of the Root Class

All objects, classes and instances alike, need an interface to the runtime system. Both class objects and instances should be able to introspect about their abilities and to report their place in the inheritance hierarchy. It’s the province of the NSObject class to provide this interface.

So that NSObject´s methods don’t have to be implemented twice—once to provide a runtime interface for instances and again to duplicate that interface for class objects—class objects are given special dispensation to perform instance methods defined in the root class. When a class object receives a message that it can’t respond to with a class method, the runtime system determines whether there’s a root instance method that can respond. The only instance methods that a class object can perform are those defined in the root class, and only if there’s no class method that can do the job.

For more on this peculiar ability of class objects to perform root instance methods, see the NSObject class specification in the Foundation framework reference.

Class Names in Source Code

In source code, class names can be used in only two very different contexts. These contexts reflect the dual role of a class as a data type and as an object:

  • The class name can be used as a type name for a kind of object. For example:

    Rectangle * anObject;

    Here anObject is statically typed to be a pointer to a Rectangle. The compiler expects it to have the data structure of a Rectangle instance and the instance methods defined and inherited by the Rectangle class. Static typing enables the compiler to do better type checking and makes source code more self-documenting. See “Enabling Static Behavior” for details.

    Only instances can be statically typed; class objects can’t be, since they aren’t members of a class, but rather belong to the Class data type.

  • As the receiver in a message expression, the class name refers to the class object. This usage was illustrated in several of the earlier examples. The class name can stand for the class object only as a message receiver. In any other context, you must ask the class object to reveal its id (by sending it a class message). The example below passes the Rectangle class as an argument in an isKindOfClass: message.

    if ( [anObject isKindOfClass:[Rectangle class]] )
        ...

    It would have been illegal to simply use the name “Rectangle” as the argument. The class name can only be a receiver.

    If you don’t know the class name at compile time but have it as a string at runtime, NSClassFromString will return the class object:

    NSString *className;
        ...
    if ( [anObject isKindOfClass:NSClassFromString(className)] )
        ...

    This function returns nil if the string it’s passed is not a valid class name.

Classnames exist in the same namespace as global variables and function names. A class and a global variable can’t have the same name. Classnames are about the only names with global visibility in Objective-C.

 

 

fuente : http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/index.html

Temas relacionados:

Opiniones de este contenido

Esta web se reserva el derecho de suprimir, por cualquier razón y sin previo aviso, cualquier contenido generado en los espacios de participación en caso de que los mensajes incluyan insultos, mensajes racistas, sexistas... Tampoco se permitirán los ataques personales ni los comentarios que insistan en boicotear la labor informativa de la web, ni todos aquellos mensajes no relacionados con la noticia que se esté comentando. De no respetarse estas mínimas normas de participación este medio se verá obligado a prescindir de este foro, lamentándolo sinceramente por todos cuantos intervienen y hacen en todo momento un uso absolutamente cívico y respetuoso de la libertad de expresión.




 No hay opiniones. Sé el primero en escribir.


Tu opinión

Contenidos Indexados
El Arte del Bonsái Ficus - Variedades

 3
  
 215595

English Adverbios de Frecuencia

 10
  
 199326

Informática Generador de códigos de barras en PHP

 13
  
 161422

Quejas denuncias estafas Descubre quién te visita en Facebook?

 16
  
 136626

English Something - Anything

 2
  
 128076

Marketing digital Cómo ganar dinero con un periódico o revista digital

 2
  
 124238

Informática Mejores herramientas para Eventos Virtuales

 1
  
 96052

OTROS MISCELANEA El permiso por puntos Español

 0
  
 84703

Actualidad Las profesiones que van a desaparecer.

 0
  
 82783

Autores

Admin

Este autor tiene 31 artículos publicados.

PARENTESYS

Este autor tiene 12 artículos publicados.

MILES

Este autor tiene 4 artículos publicados.


Newsletter

Suscríbete a nuestros boletines