Please observe this method declaration:
NSString *NSStringFromClass(Class aClass);
I want to understand the Class class, but I can't find docs about it because I don't know how to create a good enough regular expression and google with it. The fact that the class name is Class and not NSClass makes me think Class is not a real class (like java.lang.Class). But, I don't know, and so I want to research this. But, I can't find docs.
If I need to ask a specific question:
is "Class" in obj-c a real class? what are its methods and properties?
Quoting from Cocoa with Love
Every object has a class. This is a fundamental object-oriented concept but in Objective-C, it is also a fundamental part of the data. Any data structure which has a pointer to a class in the right location can be treated as an object.
In Objective-C, an object's class is determined by its isa pointer. The isa pointer points to the object's Class.
And as a proof of it, here's the declaration of id as a pointer to the objc_object struct.
typedef struct objc_object {
Class isa;
} *id;
So here we get to the point. What is a Class?
Let's look at the definition
Class is defined as follows (it may actually vary depending on the runtime, but let's keep it simple)
struct objc_class {
Class isa;
}
typedef struct objc_class *Class;
As you can see a Class has a isa pointer as well. It looks suspiciously like the objc_object definition and the reason is simple: Class is in fact an object.
But what is the class of a Class? It's - by definition - a meta-class.
According to the same source (in bold the part that tackles your question directly),
The meta-class, like the Class before it, is also an object. This means that you can invoke methods on it too. Naturally, this means that it must also have a class.
All meta-classes use the base class' meta-class (the meta-class of the top Class in their inheritance hierarchy) as their class. This means that for all classes that descend from NSObject (most classes), the meta-class has the NSObject meta-class as its class.
Following the rule that all meta-classes use the base class' meta-class as their class, any base meta-classes will be its own class (their isa pointer points to themselves). This means that the isa pointer on the NSObject meta-class points to itself (it is an instance of itself).
For further reading on the subject, here's another great explanation by Greg Parker.
Related
In the official documentation for Objective C 2.0 titled The Objective-C 2.0 Programming Language from Apple, released in 2009, there is a paragraph about Class Objects on page 28.
I don't understand what Class Objects are, and how to define them aside from the rest of the language and what properties they have. In the same document it's explained that everything in Objective-C 2.0 is an object, this object is basically a pointer to a struct that contains an isa field and the pointer itself is of type id.
From this I'm deducing that:
inheritance in Obj-C 2.0 basically consists in chaining those struct through the id and isa field
objects that are superclass construct the isa field in a way that it points to a nil object.
id is a valid datatype for pretty much everything in Objective C 2.0
when defining a class, everything that defines the class itself ( methods and variables ) is packed starting from after/below the isa pointer
Assuming that I got how Objective C 2.0 works, what is a class object and how is it different from the way instances are created? What kind of properties does a class object offer that an instance doesn't have ? Can you make a parallel with C or C++?
OK, so you define a class. We'll call him Charlie:
#interface Charlie : NSObject
#end
There's our little class! Since — like every other class in Obective-C — Charlie is an object, you can send it messages like [Charlie alloc] to have Charlie allocate an instance for you. This is what we mean by a class object: It's the object that represents the class you defined.
What is an object in Objective-C? Classes are defined based on this struct:
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
And a class is represented by a struct like this:
struct objc_class {
struct objc_class *isa;
struct objc_class *super_class;
// A bunch of other members …
}
As you can see, the both start with an isa referring to a class. So a class is just an extension of normal objects.
When Charlie creates an instance, that instance's isa will point to Charlie. But what does Charlie's isa point to? Well, it points to a metaclass. A metaclass is a strange thing — it's a special kind of class that exists just to act as a class's class. You never interact with it directly; it just does its classly duties† when you interact with its sole instance, Charlie.
So that's what we mean when we talk about a class object — it's just the object that represents the class you defined in code.
† You might be wondering what a class's duties are. Well, the obvious biggie is that it's how you create your objects. But besides that, instances in Objective-C do not hold their own methods. Instead, method resolution is done based on an object's isa, so the class's most important function, besides creating instances for you, is determining what methods your object has.
what is a class object and how it's different from the way instances
are created ? What kind of properties a class object offers that an
instance doesn't have ? Can you make a parallel with C or C++ ?
Let's try to compare with C and C++. First there is no comparison to C, because C is not object oriented, so the concept of object or class does not exist. In C++ you have classes (or objects) which you declare in your .h file, there you write the definition of the class (the name, the instance variables, and methods or functions), and then in your .cpp file you implement the methods declared in the definition.
Also in C++ you can have static variables and methods, which, as you probably know, don't belong to a specific instance of the class, we could say that they affect all instances.
In objective C, a class property or a class method is analogous to the static variables and static funcions in C++.
A class object is the way objective C encapsulates the definition of a class and makes it available at runtime. You don't necessarily instantiate class objects explicitly.
Consider this class
#interface MyObject : NSObject
{
int i;
}
- (void)myFunction;
+ (void)classFunction;
#end
You can instantiate such an object using:
MyObject *obj = [[MyObject alloc] init];
Here you're using the alloc method of the MyObject class object. Something important to understand is that you don't instantiate class Objects, the compiler creates just one object, a class object, to represent the class.
Object is a structure, that has isa field pointing to its Class. This isa allows the structure to receive Objective-C messages, which makes it an Object. Class pointed by isa is used to lookup implementations for these messages.
(Interestingly, also blocks are objects and GCD structures are too. They both can receive messages, like -copy for blocks or -description for dispatch_queue.)
Class is a structure used to look-up methods of its instances. It has a list of method implementations for their instances (-methods). Classes have an isa field, so they qualify as Objects, thus can also receive messages. isa of Class points to a Metaclass, so a Class is instance of a Metaclass. Its single instance – a singleton.
Metaclass is where I'm getting lost, but it's definitely an Object, because it has isa. Metaclass has a list of method implementations (+methods) of its single instance – the Class.
When you write this code:
#interface MYObject : NSObject
+ (void)classMethod; // Stored in Metaclass
- (void)instanceMethod; // Stored in Class
#end
You are creating a pair: Class and Metaclass.
Oh, and what is a class of Metaclass? A Root Metaclass!
And what is a class of the Root Metaclass? The Root Metaclass itself!
But then where isa of Metaclasses points to? To our old friend NSObject. Too meta, right?
I am used to seeing things like id<NSCopying> myVar or MyObject<NSCopying> myVar, where we are stating that the variable in question can happily have NSCopying methods called on it without the compiler throwing a wobbly.
But I recently spotted some code that defined a variable like this:
Class<NSCopying> myClass;
I was wondering what this actually means as it seems subtly different from the top two examples. It seems like we're saying that the variable myClass can happily accept method calls from NSCopying - but having a class type able to accept these instance variable methods doesn't seem to make much sense.
It has occurred to me that variables of type class are technically objects themselves which is probably confusing me or the compiler (probably me!).
So I guess I'm asking:
What does something like Class<NSCopying> myClass; actually mean
How does Class<NSCopying> myClass; differ to something like id<NSCopying> myVar
Where could something like Class<NSCopying> myClass; be meaningfully used?
Notes:
I am just using NSCopying as an example and isn't integral to my use case
Wherever I refer to Class I mean the Obj-C keyword Class for declaring variables that are of type Class. I am not using this as a generic term for any class type.
Well I think it might mean that Class <NSCopying> myClass points to a class that implements NSCopying protocol. It can be useful if protocol has class method declarations and you want to call them.
For example:
#protocol NSSecureCoding <NSCoding>
#required
// The Secure Coding Guide should be consulted when writing methods that decode data.
+ (BOOL)supportsSecureCoding;
#end
id <NSCopying> myVar
Using id you can supply an instance of any type of class (so long as it implements the protocol, or you cast it to make the compiler trust you).
Class <NSCopying> myClass
Using Class means you can only supply a Class, not an instance.
Say you wanted your app to be configurable. Say you had a number of classes which offered a number of different features, but you didn't care about inheritance they had, just how to create and configure them. You could use a protocol that the class has to conform to and offer a method where the classes can be registered. Then you can instantiate those classes using the defined protocol without knowing anything else about the class.
Class<ProtocolName> is the type of a variable pointing to a class object with a meta class that conforms to ProtocolName. In other words: The class implements the methods from ProtocolName with class methods.
Example: NSCopying contains one required method:
#protocol NSCopying
- (id)copyWithZone:(NSZone *)zone;
#end
Class<NSCopying> means that there is a method
+ (id)copyWithZone:(NSZone *)zone;
in the class being pointed to. That is true for all NSObject derived classes and useful for using class objects as dictionary keys. See the documentation.
As you can see below, objects in Objective-C all have an isa field which point to what kinda class it is. Eventually, everything points to NSObject which in turn points to Class. Why does this Class have a isa that points to itself? Perhaps it even makes sense to have this self referencing field for the Class object but then what is the point of Xcode showing that in the watch window? Is Xcode just being super literal here or is there ever a point to showing the isa field for the a Class object?
I'm not being nitty about what Xcode's watch window does, I am genuinely curious as I suspect this confusion means I am not understanding something.
I suspect this is to do with meta-classes. See here for a fairly thorough explanation of meta classes: What is meta class in objective-c?
In summary, there is an NSObject meta-class which is the only self defining class in objective-c, and all descendents of NSObject (including NSObject itself) will have an isa pointing to this.
The reason why this loops to itself is that the isa is used by the Objective-C method system, it must point to a class with a definition of Methods that can be invoked. Thus it points to itself at the end of the meta-class hierarchy rather than to NULL
The documentation says:
While not strictly a part of the
language, the isa pointer is required
for an object to work with the
Objective-C runtime system. An object
needs to be “equivalent” to a struct
objc_object (defined in objc/objc.h)
in whatever fields the structure
defines. However, you rarely, if ever,
need to create your own root object,
and objects that inherit from NSObject
or NSProxy automatically have the isa
variable.
While that sounds nice, I wonder how an root object would be created in Objective-C anyways?
This is for learning purposes. I just want to know this. I'd really like to see it.
It's actually a "trap" some people migrating from C# or Java style languages fall into. You simply don't specify a superclass when declaring your class i.e.
#interface MyNewRoot {
Class isa;
}
#end
vs
#interface MyObject : NSObject {
}
#end
In Java or C# these would be equivalent (in the first case the compiler would assume System.Object or java.lang.Object was the superclass), but in Objective-C no such default will be assumed, and hence a new root is created.
However you're now responsible for a number of features for your class that you typically take for granted (even simple things like memory management for allocating or destorying new instances etc). This is what the comment you quoted hints at when it talks about struct objc_object and the isa instance variable etc.
What is the purpose/use of NSObject in Objective-C? I see classes that extend NSObject like this:
#interface Fraction : NSObject
In C++ or Java, we don't use any variables like NSObject even though we have preprocessor directives and import statements in both Objective-C and Java.
Why do classes explicitly inherit from NSObject in Objective-C? What are the consequences of not declaring inheritance from NSObject?
We use NSObject to explicitly state what a given class inherits from. I'm not sure about C++, but in Java there's something similar - the Object class. The only difference is that Java doesn't require that classes explicitly descend from Object - the language assumes anything that doesn't have a specified parent class descends from Object. Objective-C is different because it allows you to define different root classes - you are allowed to make a class that doesn't inherit from NSObject.
An example of such a different root class is NSProxy.
Have a look at the GNUstep NSObject source, it shows how the methods interact with the objective-c runtime through C functions.
+ (id) allocWithZone:(NSZone*)z
{
return NSAllocateObject(self, 0, z);
}
- (void) dealloc
{
NSDeallocateObject (self);
}
+ (BOOL) isSubclassOfClass: (Class)aClass
{
return GSObjCIsKindOf(self, aClass);
}
Since object-oriented languages have the concept of an inheritance, in any inheritance hierarchy there is a root class. In Java, the default parent class (if none is provided) is java.lang.Object, whereas in Objective-C, if you don't explicitly declare a parent class, you don't get one. Essentially, your class becomes a root class itself. This is a common mistake among Objective-C newcomers, since you normally want to inherit from NSObject in such cases.
While often problematic and puzzling, this actually allows quite a bit of flexibility, since you can define your own class hierarchies that act completely differently from NSObject. (Java doesn't allow you to do this at all.) On the other hand, unless you know what you're doing, it's easy to get yourself into trouble this way. Fortunately, the compiler will provide warnings if you call a method not defined by a class with no declared parent class, such as those you would normally expect to inherit from NSObject.
As for the "use" of NSObject, check out the documentation of the NSObject class and NSObject protocol. They define common methods used for object allocation, memory management, comparison, hashing, printing descriptions, checking class membership, querying whether objects respond to a selector, etc. Basically, NSObject is "good for" providing the core functionality of Objective-C objects free of charge.
All classes don't necessarily inherit from NSObject but it is the core for many of the classes because it provides things like alloc, retain, and release.
NSObject is the root class of all classes. In my estimation, it's 3 most basic functions are to allocate and initialize memory for you (alloc & init), as well as provide a description function.
Objective-C is all about objects sending messages to other objects -- so NSObject exists to provide that basic functionality.
If this sounds strange to you, you may wish to read more about programming paradigms, particularly object-oriented programming....In a nutshell, however, Objective C is a simple extension to the C language. C gets you the ability to program computer memory, numbers, and characters, but do anything else (like use strings, or show views, for example) you need the extension part, and NSObject is the beginning of that extension.
It may be a useful exercise to pick a class (like NSString, or any for that matter), and follow it's superclasses back to NSObject, to see what functionality each class added.
Hope that helps...
NSObject
The root class of most Objective-C class hierarchies, from which
subclasses inherit a basic interface to the runtime system and the
ability to behave as Objective-C objects.
From Apple documentation - https://developer.apple.com/documentation/objectivec/nsobject.
Basically, most of OOP programming languages explicitly or implicitly specify base class or base functionality. Otherwise you cannot build system where objects communicate with each other. Properties, memory management, message sending mechanism are partly or completely provided or supported by NSObject. Apple provide parts of the Objective-C implementation - https://opensource.apple.com/source/objc4/objc4-723/runtime/NSObject.mm.auto.html, where it's possible to see what is actually inside NSObject.
Also because Objective-C is a language from C-family, so compiler and linker needs to calculate how to layout object in memory and where put and find methods, that's only possible if you know how each of the classes/instances lays in memory and where. In case of Objective-C all base classes (NSObject, NSProxy, etc) have specification of that, so it's possible to calculate their size and add on top all inherited stuff - https://clang.llvm.org/compatibility.html#objective-c.
Consequently compiler don't let to leave a class without base class. So in the end class inheritance should lead to one of the root classes. Here is the error that appears if you don't specify it (from Xcode):
Class 'ClassWithoutBaseClass' defined without specifying a base class