1.In Objective-C, a class is itself an object with an opaque type called Class
. Classes can’t have properties defined using the declaration syntax shown earlier for instances, but they can receive messages.
2 Literals Offer a Concise Object-Creation Syntax
some classes allow you to use a more concise, literal syntax to create instances.
You can create an NSString
instance, for example, using a special literal notation, like this:
NSString *someString = @"Hello, World!"; |
This is effectively the same as allocating and initializing an NSString
or using one of its class factory methods:
NSString *someString = [NSString stringWithCString:"Hello, World!" |
encoding:NSUTF8StringEncoding]; |
The NSNumber
class also allows a variety of literals:
NSNumber *myBOOL = @YES; |
NSNumber *myFloat = @3.14f; |
NSNumber *myInt = @42; |
NSNumber *myLong = @42L; |
NSArray
and
NSDictionary
objects;
NSArray *someArray = @[firstObject, secondObject, thirdObject];
NSDictionary *dictionary = @{ |
@"anObject" : someObject, |
@"helloString" : @"Hello, World!", |
@"magicNumber" : @42, |
@"aValue" : someValue |
}; |
It’s always a good idea to initialize scalar variables at the time you declare them, otherwise their initial values will contain garbage from the previous stack contents:
BOOL success = NO; |
int magicNumber = 42; |
This isn’t necessary for object pointers, because the compiler will automatically set the variable to nil
if you don’t specify any other initial value:
XYZPerson *somePerson; |
// somePerson is automatically set to nil |
If you want to use a different name for an accessor method, it’s possible to specify a custom name by adding attributes to the property. In the case of Boolean properties (properties that have a YES
or NO
value), it’s customary for the getter method to start with the word “is.” The getter method for a property called finished
, for example, should be called isFinished
.
Again, it’s possible to add an attribute on the property:
@property (getter=isFinished) BOOL finished; |
5, Most Properties Are Backed by Instance Variables
firstName
, for example, the synthesized instance variable will be called
_firstName
.
@synthesize firstName = ivar_firstName; |
In this case, the property will still be called firstName
, and be accessible through firstName
and setFirstName:
accessor methods or dot syntax, but it will be backed by an instance variable called ivar_firstName
.
You Can Define Instance Variables without Properties
use strong references for their synthesized instance variables. To declare a weak reference, add an attribute to the property, like this:
@property (weak) id delegate; |
If you don’t want a variable to maintain a strong reference, you can declare it as __weak
, like this:
NSObject * __weak weakVariable; |
This means that if you use a weak variable in the previous date example:
NSDate * __weak originalDate = self.lastModificationDate; |
self.lastModificationDate = [NSDate date]; |
__weak
is
__strong
. Again, you don’t need to specify
__strong
explicitly, because it is the default.
copy
property must support NSCopying
, which means that it should conform to the NSCopying
protocol.
Categories Add Methods to Existing Classes
Any methods that you declare in a category will be available to all instances of the original class, as well as any subclasses of the original class. At runtime, there’s no difference between a method added by a category and one that is implemented by the original class.Class Extensions Extend the Internal Implementation
A class extension bears some similarity to a category, but it can only be added to a class for which you have the source code at compile time (the class is compiled at the same time as the class extension). The methods declared by a class extension are implemented in the@implementation
block for the original class so you can’t,for example, declare a class extension on a framework class, such as a Cocoa or Cocoa Touch class like NSString
.
Unlike regular categories, a class extension can add its own properties and instance variables to a class. If you declare a property in a class extension, like this:
@interface XYZPerson () |
@property NSObject *extraProperty; |
@end |
the compiler will automatically synthesize the relevant accessor methods, as well as an instance variable, inside the primary class implementation.
If you add any methods in a class extension, these must be implemented in the primary implementation for the class.
It’s also possible to use a class extension to add custom instance variables. These are declared inside braces in the class extension interface:
@interface XYZPerson () { |
id _someCustomInstanceVariable; |
} |
... |
@end |
Use Class Extensions to Hide Private Information
@interface XYZPerson : NSObject |
... |
@property (readonly) NSString *uniqueIdentifier; |
- (void)assignUniqueIdentifier; |
@end |
@interface XYZPerson () |
@property (readwrite) NSString *uniqueIdentifier; |
@end |
|
@implementation XYZPerson |
... |
@end |
This means that the compiler will now also synthesize a setter method, so any method inside the XYZPerson
implementation will be able to set the property directly using either the setter or dot syntax.
By declaring the class extension inside the source code file for the XYZPerson
implementation, the information stays private to the XYZPerson
class. If another type of object tries to set the property, the compiler will generate an error.
Protocols Define Messaging Contracts
A class interface declares the methods and properties associated with that class. A protocol, by contrast, is used to declare methods and properties that are independent of any specific class.
The basic syntax to define a protocol looks like this:
@protocol ProtocolName |
// list of methods and properties |
@end |
Protocols Inherit from Other Protocols
@protocol MyProtocol <NSObject> |
... |
@end |
Protocols Are Used for Anonymity
NSInteger sectionNumber = ... |
id <NSFetchedResultsSectionInfo> sectionInfo = |
[self.fetchedResultsController.sections objectAtIndex:sectionNumber]; |
NSInteger numberOfRowsInSection = [sectionInfo numberOfObjects]; |
Even though you don’t know the class of the sectionInfo
object, the NSFetchedResultsSectionInfo
protocol dictates that it can respond to the numberOfObjects
message.
In order for this anonymousUtility
object to be useful, the developer of the framework can publish a protocol that reveals some of its methods. Even though the original class interface isn’t provided, which means the class stays anonymous, the object can still be used in a limited way:
id <XYZFrameworkUtility> utility = [frameworkObject anonymousUtility]; |
Represent Other Values Using Instances of the NSValue Class
The NSNumber
class is itself a subclass of the basic NSValue
class, which provides an object wrapper around a single value or data item. In addition to the basic C scalar types, NSValue
can also be used to represent pointers and structures.
The NSValue
class offers various factory methods to create a value with a given standard structure, which makes it easy to create an instance to represent, for example, an NSRange
, like the example from earlier in the chapter:
NSString *mainString = @"This is a long string"; |
NSRange substringRange = [mainString rangeOfString:@"long"]; |
NSValue *rangeValue = [NSValue valueWithRange:substringRange]; |
It’s also possible to create NSValue
objects to represent custom structures. If you have a particular need to use a C structure (rather than an Objective-C object) to store information, like this:
typedef struct { |
int i; |
float f; |
} MyIntegerFloatStruct; |
you can create an NSValue
instance by providing a pointer to the structure as well as an encoded Objective-C type. The @encode()
compiler directive is used to create the correct Objective-C type, like this:
struct MyIntegerFloatStruct aStruct; |
aStruct.i = 42; |
aStruct.f = 3.14; |
|
NSValue *structValue = [NSValue value:&aStruct |
withObjCType:@encode(MyIntegerFloatStruct)]; |
The standard C reference operator (&
) is used to provide the address of aStruct
for the value
parameter.
NSDictionary
NSNumber *storedNumber = [dictionary objectForKey:@"magicNumber"];
There’s also an alternative syntax to using objectForKey:
, which looks like this:
NSNumber *storedNumber = dictionary[@"magicNumber"]; |
If you use fast enumeration with a dictionary, you iterate over the dictionary keys, like this:
for (NSString *eachKey in dictionary) { |
id object = dictionary[eachKey]; |
NSLog(@"Object: %@ for key: %@", object, eachKey); |
} |
If you need to keep track of the current index, simply count the iterations as they occur:
int index = 0; |
for (id eachObject in array) { |
NSLog(@"Object at index %i is: %@", index, eachObject); |
index++; |
} |
Represent nil with NSNull
It’s not possible to add nil
to the collection classes described in this section because nil
in Objective-C means “no object.” If you need to represent “no object” in a collection, you can use the NSNull
class:
NSArray *array = @[ @"string", @42, [NSNull null] ]; |
NSNull
is a singleton class, which means that the null
method will always return the same instance. This means that you can check whether an object in an array is equal to the shared NSNull
instance:
for (id object in array) { |
if (object == [NSNull null]) { |
NSLog(@"Found a null object"); |
} |
} |
Most Collections Also Support Enumerator Objects
It’s also possible to enumerate many Cocoa and Cocoa Touch collections by using an NSEnumerator
object.
You can ask an NSArray
, for example, for an objectEnumerator
or a reverseObjectEnumerator
. It’s possible to use these objects with fast enumeration, like this:
for (id eachObject in [array reverseObjectEnumerator]) { |
... |
} |
In this example, the loop will iterate over the collected objects in reverse order, so the last object will be first, and so on.
It’s also possible to iterate through the contents by calling the enumerator’s nextObject
method repeatedly, like this:
id eachObject; |
while ( (eachObject = [enumator nextObject]) ) { |
NSLog(@"Current object is: %@", eachObject); |
} |