Objective-c is an object-oriented language and a superset of C. We can use any scalar types in Standard C, such as int, float, char. In addition, there are some additional value types available in Cocoa and Cocoa Touch applications, such as NSInteger, NSUInteger, and CGFloat, which have different definitions in different schemas.

When we do not need to use object type features, we can choose to use value type data. For example, for the features and functionality provided by the NSString class, we often use instances of the NSString class to represent strings, and numeric data is often stored in local variables or attributes of value types.

It is possible to declare C-style arrays in Objective-C, but we will find that collections in Cocoa and Cocoa Touch applications are often represented using NSArray or NSDictionary. These classes can only be used to store the Objective – C object, making it if we want to put a value stored in the collection, need to value types of data encapsulation into NSValue NSNumber, nsstrings Objective – C an instance of the object, etc.

In previous articles, we used the NSString class with its initialization and factory methods several times, and created strings using the @ literal syntax. In this article we will show how to create NSValue and NSNumber objects using method or literal syntax.

Use C’s basic data types in Objective-C

The basic value types in C are straightforward to use in Objective-C:

int someInteger = 42; Float someFloatingPointNumber = 3.1415; Double someDoublePrecisionFloatingPointNumber = 6.02214199 electronics;Copy the code

The standard C operators are also available:

    int someInteger = 42;
    someInteger++;            // someInteger == 43
 
    int anotherInteger = 64;
    anotherInteger--;         // anotherInteger == 63
 
    anotherInteger *= 2;      // anotherInteger == 126
Copy the code

We can also put C’s value type data in attributes like this:

@interface XYZCalculator : NSObject
@property double currentValue;
@end
Copy the code

You can also use the C operator in conjunction with point syntax:

@implementation XYZCalculator
- (void)increment {
    self.currentValue++;
}
- (void)decrement {
    self.currentValue--;
}
- (void)multiplyBy:(double)factor {
    self.currentValue *= factor;
}
@end
Copy the code

Dot syntax is just a syntactic sugar for a getter/setter method, so each operation in the above example is equivalent to calling the getter method for the value, then performing the operator operation, and then calling the setter method to assign the value to the property.

Basic types defined in Objective-C

Objective-c defines BOOL types that use YES or NO to store Boolean data. YES is logically equivalent to true and 1, and NO is logically equivalent to false and 0.

Many method parameters in Cocoa and Cocoa Touch accept special value types, such as NSInteger or CGFloat.

For example, methods in the NSTableViewDataSouce and UITableViewDataSource protocols:

@protocol NSTableViewDataSource <NSObject> - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView; . @endCopy the code

These types, such as NSInteger and NSUInteger, have different definitions for different processor architectures. In 32-bit environments, they represent signed/unsigned 32-bit integers, and in 64-bit environments, they represent signed/unsigned 64-bit integers.

These platform-specific value types are best used when code may be used across platforms or in different environments, such as data interaction with other frameworks.

For local variables, such as counters in a loop, it is fine to use the basic C type if we can guarantee that the number of loops is within the value range of that type.

Some Cocoa and Cocoa Touch apis use C constructs to hold their values, such as NSRange:

    NSString *mainString = @"This is a long string";
    NSRange substringRange = [mainString rangeOfString:@"long"];
Copy the code

The NSRange structure holds position and length information. In the example above, a structure {10, 4} will be stored in substringRange. 10 represents the starting subscript of @”long” and 4 represents the length of the string for @long.

Similarly, when we need to use Quartz custom drawing methods, we use structure data types based on CGFloat, such as NSPoint and NSSize in OS X, CGPoint and CGsie.cgfloat in iOS also have different definitions for different processor architectures. For more information about the Quartz 2D Programming engine, see the official Quartz 2D Programming Guide.

Use Objective-C objects to represent basic data types

When we use objective-C collection types (NSArray/NSDictionary, etc.), we need to use objects to represent value types.

String –NSString

Use NSString to represent strings, such as Hello World. There are several ways to create NSString objects: basic Alloc +init, class-factory methods, or literal syntax:

NSString *firstString = [[NSString alloc] initWithCString:"Hello World!" encoding:NSUTF8StringEncoding]; NSString *secondString = [NSString stringWithCString:"Hello World!" encoding:NSUTF8StringEncoding]; NSString *thirdString = @"Hello World!" ;Copy the code

Each of the above examples similarly creates a string object representing Hello World.

Basic NSString classes are immutable; their values are determined at creation and cannot be changed later. If we need an NSString that represents a different value, we can only create a new instance object:

    NSString *name = @"John";
    name = [name stringByAppendingString:@"ny"];    // returns a new string object
Copy the code

The NSMutableString class is a mutable version of the NSString class that allows calls to methods (such as appendString: or appendFormat) at run time to modify its string contents:

    NSMutableString *name = [NSMutableString stringWithString:@"John"];
    [name appendString:@"ny"];   // same object, but now represents "Johnny"
Copy the code

If we want to use a string that contains a variable value, we need to use a format string.

Format strings allow us to insert values into positions using format characters:

    int magicNumber = ...
    NSString *magicString = [NSString stringWithFormat:@"The magic number is %i", magicNumber];
Copy the code

See String Format Specifiers for available formatting characters. For more on strings, see String Programming Guide.

Number type –NSNumber

NSNumber class used to represent arbitrary C data value type: contains char, double, float, int, long, short and unsigned version, and Objective – C – BOOL Boolean type.

NSNumber has a number of initialization/class factory method creation instances:

NSNumber *magicNumber = [[NSNumber alloc] initWithInt:42]; NSNumber *unsignedNumber = [[NSNumber alloc] initWithUnsignedInt:42u]; NSNumber *longNumber = [[NSNumber alloc] initWithLong:42l]; NSNumber *boolNumber = [[NSNumber alloc] initWithBOOL:YES]; NSNumber *simpleFloat = [NSNumber numberWithFloat:3.14f]; NSNumber * betterDouble = [NSNumber numberWithDouble: 3.1415926535]; NSNumber *someChar = [NSNumber numberWithChar:'T'];Copy the code

Similarly, NSNumber instances can be created using literal syntax, which is equivalent to creating instance objects using the class factory method:

NSNumber *magicNumber = @42; NSNumber *unsignedNumber = @42u; NSNumber *longNumber = @42l; NSNumber *boolNumber = @YES; NSNumber * simpleFloat = @ 3.14 f; NSNumber * betterDouble = @ 3.1415926535; NSNumber *someChar = @'T';Copy the code

After the NSNumber class instance object is created, the corresponding value can be obtained by using the following accessor methods:

    int scalarMagic = [magicNumber intValue];
    unsigned int scalarUnsigned = [unsignedNumber unsignedIntValue];
    long scalarLong = [longNumber longValue];
 
    BOOL scalarBool = [boolNumber boolValue];
 
    float scalarSimpleFloat = [simpleFloat floatValue];
    double scalarBetterDouble = [betterDouble doubleValue];
 
    char scalarChar = [someChar charValue];
Copy the code

The NSNumber class also provides methods to interact with objective-C primitive value types:

    NSInteger anInteger = 64;
    NSUInteger anUnsignedInteger = 100;
 
    NSNumber *firstInteger = [[NSNumber alloc] initWithInteger:anInteger];
    NSNumber *secondInteger = [NSNumber numberWithUnsignedInteger:anUnsignedInteger];
 
    NSInteger integerCheck = [firstInteger integerValue];
    NSUInteger unsignedCheck = [secondInteger unsignedIntegerValue];
Copy the code

All NSNumber classes are immutable, and there are no mutable version subclasses. If we need a different value, create a new instance.

Note: NSNumber is actually a class cluster. This means that when we create an instance of it at run time, we get the concrete subclass instance that corresponds to its value. But just use it as an instance of NSNumber.

Other value types –NSValue

The NSNumber class itself is a subclass of the NSValue class, which provides object encapsulation of individual values and data. In addition to the basic C value types, NSValues can also be used to store Pointers and structs.

The NSValue class provides a number of different class factory methods for creating class instances that correspond to struct values, such as NSRange:

    NSString *mainString = @"This is a long string";
    NSRange substringRange = [mainString rangeOfString:@"long"];
    NSValue *rangeValue = [NSValue valueWithRange:substringRange];
Copy the code

We can also store custom constructs through NSValue. Suppose there is the following C structure:

typedef struct {
    int i;
    float f;
} MyIntegerFloatStruct;
Copy the code

We can pass in a structure pointer and an encoded Objective-C type to create the corresponding NSValue instance. The @encode() compiler directive is used to tell the compiler to create a corresponding Objective-C type.

struct MyIntegerFloatStruct aStruct; aStruct.i = 42; AStruct. F = 3.14; NSValue *structValue = [NSValue value:&aStruct withObjCType:@encode(MyIntegerFloatStruct)];Copy the code

The C reference operator – & is used to provide the address of the aStruct.

Collection types in Objective-C

Most collection types in Objective-C are object types. Although C arrays can be used to store collections of values or object Pointers, most Objective-C code uses collection classes provided by Cocoa and Cocoa Touch, such as NSArray, NSSet, and NSDictionary.

These classes are used to manage groups of objects, which means that if we add an element to a collection that has to be an Objective-C instance object, if we want to add an element of value type to one of these collections, we have to wrap it with an NSNumber or an NSValue.

These collection classes are not implemented by internally maintaining copies of the elements in the collection; they internally make strong references to internal objects. So, as long as we add an element object to the collection, as long as the collection exists, this object must exist. See Manage the Object Graph through Ownership and Responsibility for the memory management involved.

In addition to storing internal elements, each Cocoa/Cocoa Touch collection class provides convenient methods such as traversing internal elements and obtaining specified objects.

The basic NSArray, NSSet, and NSDictionary classes are immutable, and they all have a corresponding mutable version class.

For a detailed introduction to the Objective-C collection class, see Collections Programming Topics.

useNSArrayManaging ordered collections

NSArray is used to represent an ordered collection of objects. The only requirement is that every element in it be an Objective-C object, there’s no requirement that the elements in NSArray be instances of the same class.

Array subscripts that indicate the order of elements in an array start at 0:

Create an array

Arrays can be created by initializing/class factory/literal syntax.

+ (id)arrayWithObject:(id)anObject; + (id)arrayWithObjects:(id)firstObject, ... ; - (id)initWithObjects:(id)firstObject, ... ;Copy the code

ArrayWithObjects: and initWithObjects: methods accept an indefinite number of arguments ending in nil.

NSArray *someArray =
  [NSArray arrayWithObjects:someObject, someString, someNumber, someValue, nil];
Copy the code

In this example, the subscript for someObject is 0 and the subscript for someValue is 3.

If we accidentally use a nil value, we might accidentally truncate the array:

id firstObject = @"someString";
    id secondObject = nil;
    id thirdObject = @"anotherString";
    NSArray *someArray =
  [NSArray arrayWithObjects:firstObject, secondObject, thirdObject, nil];
Copy the code

In this example, someArray will only store firstObject. The reason is that secondObject is nil, so you think that’s where the array ends.

We also use literal syntax to create arrays:

NSArray *someArray = @[firstObject, secondObject, thirdObject];
Copy the code

In this syntax, using nil does not truncate the array, but nil is an invalid value in this syntax, and attempts to add nil to the array as an element will result in an error:

    id firstObject = @"someString";
    id secondObject = nil;
    NSArray *someArray = @[firstObject, secondObject];
    // exception: "attempt to insert nil object"
Copy the code

If we do need to insert nil values into the collection, we should use the NSNull singleton class, as discussed later.

Use the query method in an array

Once we have created an array, we can query it for information about the array, such as the number of objects in the array, or whether it contains a particular object:

    NSUInteger numberOfItems = [someArray count];
 
    if ([someArray containsObject:someString]) {
        ...
    }
Copy the code

We can also get elements by subscripting. If the subscript is invalid, an out-of-bounds exception will be reported at runtime, so we need to check the number of objects in the array:

    if ([someArray count] > 0) {
        NSLog(@"First item is: %@", [someArray objectAtIndex:0]);
    }
Copy the code

In the example above, we check that the number of objects in the array is greater than 0 before we operate on objects with array subscript 0.

We can also get elements using subscript syntax equivalent to objectAtIndex: (this is similar to the C array value operation):

    if ([someArray count] > 0) {
        NSLog(@"First item is: %@", someArray[0]);
    }
Copy the code

Use sorting methods in arrays

The NSArray class also provides a variety of methods for sorting objects in a collection. Since NSArray is immutable, these methods return a new array object containing the sorted elements.

We can sort an array of strings by calling compare: on each string:

    NSArray *unsortedStrings = @[@"gammaString", @"alphaString", @"betaString"];
    NSArray *sortedStrings =
                 [unsortedStrings sortedArrayUsingSelector:@selector(compare:)];
Copy the code

Using mutable arrays

Although the NSArray class is immutable, this does not affect the variability of its internal elements. When we add a mutable string to an immutable array:

NSMutableString *mutableString = [NSMutableString stringWithString:@"Hello"];
NSArray *immutableArray = @[mutableString];
Copy the code

Changing the mutableString is not affected:

    if ([immutableArray count] > 0) {
        id string = immutableArray[0];
        if ([string isKindOfClass:[NSMutableString class]]) {
            [string appendString:@" World!"];
        }
    }
Copy the code

If we need to add or remove elements from an array after the initial creation, we need a mutable array called NSMutableArray. This class contains many methods for adding, modifying, and removing single or multiple objects:

    NSMutableArray *mutableArray = [NSMutableArray array];
    [mutableArray addObject:@"gamma"];
    [mutableArray addObject:@"alpha"];
    [mutableArray addObject:@"beta"];
 
    [mutableArray replaceObjectAtIndex:0 withObject:@"epsilon"];
Copy the code

The above example ends up creating a mutable array containing @”epsilon”, @”alpha”, @”beta”.

It is also possible to sort the inside of a mutable array without creating a new array:

[mutableArray sortUsingSelector:@selector(caseInsensitiveCompare:)];
Copy the code

In this case, the array will be sorted in ascending string order, resulting in @”alpha”, @”beta”, @”epsilon”.

useNSSetManaging unordered Collections

An NSSet is similar to an array, but it maintains an unordered set with no repeating elements:

Because NSsets do not manage order, they can sometimes perform better than arrays.

Similarly, an NSSet is immutable and its contents must be specified at creation time, which can be created using the initialization/class factory method:

NSSet *simpleSet =
      [NSSet setWithObjects:@"Hello, World!", @42, aValue, anObject, nil];
Copy the code

Like NSArray, the above method ends up with nil judgment sets. The mutable version of NSSet is NSMutableSet.

Even if the same object is added to an NSSet multiple times, the NSSet retains only one object:

    NSNumber *number = @42;
    NSSet *numberSet =
      [NSSet setWithObjects:number, number, number, number, nil];
    // numberSet only contains one object
Copy the code

For more information on NSsets and NSMutableSet, see Sets: Unordered Collections of Objects.

useNSDictionaryManage key-value Pairs

NSDictionary does not simply manage ordered or unordered collections. It stores objects based ona given key, and can also retrieve objects based on the key. It is generally best to use string objects as dictionary keys:

Note: It is possible to use other types of objects as keys, but Note that every key is copied when used in NSDictionary, so objects of type key must support the NSCopying protocol. If we expect to use key-value Coding, as described in the Key-value Coding Guide, we must use strings as the keys of the NSDictionary.

Creating a dictionary

Dictionaries can be created by initializing/class factory methods:

NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                   someObject, @"anObject",
             @"Hello, World!", @"helloString",
                          @42, @"magicNumber",
                    someValue, @"aValue",
                             nil];
Copy the code

For dictionaryWithObjectsAndKeys: and initWithObjectsAndKeys: method, each object specified before the key, and the need to end with nil.

Literal syntax creation:

NSDictionary *dictionary = @{ @"anObject" : someObject, @"helloString" : @"Hello, World!" , @"magicNumber" : @42, @"aValue" : someValue };Copy the code

Note: Literal syntax creates dictionaries with key first and value last, and does not end in nil.

Query the dictionary

Object values can be retrieved by keys in the dictionary:

NSNumber *storedNumber = [dictionary objectForKey:@"magicNumber"];
Copy the code

The objectForKey: method will return nil if no object is found

Objects can also be retrieved using subscript syntax equivalent to objectForKey: :

NSNumber *storedNumber = dictionary[@"magicNumber"];
Copy the code

Use a mutable dictionary

If you need to add/remove objects from the dictionary after creating it, you need to subclass NSMutableDictionary:

    [dictionary setObject:@"another string" forKey:@"secondString"];
    [dictionary removeObjectForKey:@"anObject"];
Copy the code

useNSNullsaidnil

As mentioned earlier, you can’t add nil to a collection. Because nil stands for “no object” in Objective-C. If you need to say “no object” in a collection, you can use NSNull

NSArray *array = @[ @"string", @42, [NSNull null] ];
Copy the code

NSNull is a Singleton. Therefore, null methods of NSNull always return the same instance. This means that we can check whether the objects in the array are equal to [NSNull null].

for (id object in array) { if (object == [NSNull null]) { NSLog(@"Found a null object"); }}Copy the code

persistence

It is easy to write its contents directly to disk using the NSArray and NSDictionary classes:

NSURL *fileURL = ... NSArray *array = @[@"first", @"second", @"third"]; BOOL success = [array writeToURL:fileURL atomically:YES]; if (! success) { // an error occured... }Copy the code

If all objects in NSArray and NSDictionary are of the type supported by the Property List (NSArray, NSDictionary, NSString, NSData, NSDate, NSNumber), The entire object hierarchy can be read directly from disk:

NSURL *fileURL = ... NSArray *array = [NSArray arrayWithContentsOfURL:fileURL]; if (! array) { // an error occurred... }Copy the code

For more information about Property List, see Property List Programming Guide.

If we need to persist objects of non-property List type, we can use Archiver, such as NSKeyedArchiver, to create an archive of the collection of objects.

The only requirement for creating an archive is that every object in the collection must support the NSCoding protocol: each object must know how to archive itself (encodeWithCoder:) and how to unarchive itself (initWithCoder:).

NSArray, NSSet, NSDictionary, and mutable versions of them, all support NSCoding.

In addition, if we use Interface Builder to layout the view and window, the corresponding NIB file is actually the view-level archive we created. At run time, the file will be unfiled and used.

For more information about archiving, see Archives and Serializations Programming Guide.

traverse

Objective-c can use C’s for loop to iterate over collections:

int count = [array count]; for (int index = 0; index < count; index++) { id eachObject = [array objectAtIndex:index]; . }Copy the code

In addition to using C’s for loop, Objective-C, Cocoa, Cocoa Touch provides a number of methods for traversing the contents of a collection.

Objective-c recommends traversal methods other than C’s for loop.

Fast Enumeration

Many collection classes, including NSArray, NSSet, and NSDictionary, follow the NSFastEnumeration protocol.

So we can use fast enumeration, objective-C’s language-level feature.

The syntax for using fast traversal in arrays is as follows:

    for (<Type> <variable> in <collection>) {
        ...
    }
Copy the code

For example, we can use it like this:

    for (id eachObject in array) {
        NSLog(@"Object: %@", eachObject);
    }
Copy the code

The eachObject variable is automatically set in the loop to the current object in each loop.

If we use quick traversal in a dictionary, we iterate over the key in the dictionary:

    for (NSString *eachKey in dictionary) {
        id object = dictionary[eachKey];
        NSLog(@"Object: %@ for key: %@", object, eachKey);
    }
Copy the code

The behavior of fast traversal is similar to that of C’s for loop. We can also use the break keyword to interrupt the iteration, or we can use the continue to break out of the loop and execute the next loop.

When we iterate over an ordered set, the traversal is done sequentially. If we need to store index, we can use the variable to record it in the traversal:

    int index = 0;
    for (id eachObject in array) {
        NSLog(@"Object at index %i is: %@", index, eachObject);
        index++;
    }
Copy the code

In fast traversal, even though the set is mutable, we cannot change the set being traversed. If we try to remove or add objects to the collection we are walking through in the loop, an exception will be thrown at run time.

useNSEnumeratortraverse

Most Cocoa and Cocoa Touch collections provide another way of traversing: using NSEnumerator objects.

We can get the objectEnumerator or reverseObjectEnumerator from an Instance object of NSArray.

Can also be used in combination with NSEnumerator and fast traversal:

    for (id eachObject in [array reverseObjectEnumerator]) {
        ...
    }
Copy the code

In the example above, the array would be traversed in reverse order.

You can also iterate through the collection by calling Enumerator’s nextObject method repeatedly:

    id eachObject;
    while ( (eachObject = [enumerator nextObject]) ) {
        NSLog(@"Current object is: %@", eachObject);
    }
Copy the code

In this example, eachObject is constantly set [Enumerator nextObject] in the while loop. When the loop is complete, nextObject returns nil, ending the loop.

Note: Since it is easy to misuse C’s assignment operator (=) for the equality operator (==), the compiler warns when assigning to a variable in a conditional branch or loop like this:

if (someVariable = YES) {
    ...
}
Copy the code

But if we really want to use the assignment operator, we can wrap the assignment around parentheses () :

if ( (someVariable = YES) ) {
    ...
}
Copy the code

As with fast traversal, the traversal cannot change the set in the traversal.

Use Block traversal

NSArray, NSSet, and NSDictionary can also be traversed using blocks. In the next article, we’ll cover blocks in more detail.

Resources: Values and Collections