Chapter    4

Performing Operations

This chapter will focus on performing operations, including arithmetic, logical, bitwise, type checking, and type casting. Operator precedence and associativity rules will also be reviewed.

Basic Operators

Swift includes the same basic operators as Objective-C, yet it adheres to a slightly different set of rules for operator precedence and associativity that are intended to be simpler and more predictable. Precedence and associativity will be covered later in this chapter.

The basic operators are implemented in Swift in the same manner as Objective-C, with a few exceptions and additions that will be mentioned next, followed by a summary of all basic operators with examples in Tables 4-1 through 4-4.

In both Objective-C and Swift, == and != operators test for value equality (or inequality) for number values (NSInteger, NSUInteger, NSNumber, etc. in Objective-C; Int, UInt, etc. in Swift). For objects (NSObject and subclasses) in Objective-C and reference types in Swift, == and != test that the objects/reference types are the same identical thing – same hash value – or are not the same identical thing, respectively. Custom reference types do not automatically implement these equal to operators; see Chapter 7 for details.

These equal to operators are also not implemented for other types such as structures in either language. However, custom operators can be created in Swift, and existing operators can be overloaded. Creating custom operators and overloading existing operators will be covered in Chapter 6.

Swift also introduces two new identity equality operators (=== and !==), which check referential equality of reference types (and thus could have been called referential equality operators). These operators automatically apply to custom reference types, too.

The + operator is used in Objective-C to add scalar numbers (including typedefs such as NSInteger). Similarly,  + is used in Swift to add two number value types. As was covered in the last chapter, the + operator can also be used to concatenate two values of type String or type Array, and it can be used to concatenate two Character types to create a String type.

The += operator can be used in Objective-C with scalar numbers and associated typedefs as shorthand syntax to combine addition and assignment (a += b is the same as a = a + b). The += operator is used in the same way in Swift with number value types, and, as explained in the last chapter, it can be used to concatenate and assign two values of type String or Array, but not Character (because the resulting String type is not assignable to a Character type).

The % operator, known as the modulo operator in Objective-C, is referred to in Swift as the remainder operator. Swift expands usage of the remainder operator to include floating-point numbers.

In Objective-C, value overflow and underflow is automatic, which can result in unexpected behavior and sometimes hard-to-find bugs. For example, if you create 8-bit integer (of which maximum value is 255) and attempt to add 1 to that value, it will overflow and essentially “wrap around” to the value 0. As part of Swift’s “safe by design” credo, overflow and underflow conditions will result in a compile-time error by default, versus creating an invalid or unexpected value during runtime. However, you can opt in to overflow/underflow handling in Swift by using the overflow operators for addition (&+), subtraction (&-), multiplication (&*), division (&/), and remainder (&%), in place of the regular operators.

Note  When using an overflow operator, the first operand is referenced to determine legality of assignment before the operation is performed, instead of the result of the whole operation.

Along similar lines, division by 0 or calculating modulo 0 in Objective-C will return 0. In Swift, either attempt will result in a compile-time error unless you use the equivalent overflow operator, in which case 0 will be returned just like in Objective-C.

Swift adds a new nil coalescing operator (??) for use with optionals. This operator provides a shorthand syntax alternative to using a ternary conditional operator.

Although the ternary conditional operator (?:) is implemented the same way in both languages, it should be pointed out that nonoptional values in Swift cannot be checked in a boolean manner such as in Objective-C. In Swift, it is necessary to explicitly check if a nonoptional variable does not equal nil (!= nil) in a ternary conditional operator, or in other conditional checks, as you’ll see in the next chapter.

There are two more new Swift operators: the closed range operator (..., as in a...e) and half-open range operator (..<, as in a..<e). Because these operators are predominantly used in controlling program flow, they will be covered in the next chapter, focused entirely on that topic.

Unary operators operate on a single target, binary on two targets, and ternary on three targets. Tables 4-1 through 4-4 summarize the basic operators found in Objective-C and Swift. For Swift’s overflow operators (that do not exist in Objective-C), comparable examples from Objective-C are provided.

Table 4-1. Comparison operators in Objective-C and Swift

 

Objective-C

Swift

 

NSInteger a = 4;

NSInteger b = 2;

NSObject *x = [NSObject new];

NSObject *y = [NSObject new];

NSObject *z = x;

let a = 4

let b = 2

let x = NSObject()

let y = NSObject()

let z = x

==

a == b // False

x == y // False

x == z // True

a == b // False

x == y // False

x == z // True

!=

a != b // True

x != y // True

a != b // True

>

a > b // True

a > b // True

<

a < b // False

a < b // False

>=

a >= (b * b) // True

a >= (b * b) // True

<=

a <= (b * b) // True

a <= (b * b) // True

===

N/A

x === z // True

!==

N/A

x !== y // True

Table 4-2. Basic unary operators in Objective-C and Swift

 

Objective-C

Swift

 

NSInteger a = 1;

var a = 1

++

NSInteger b = ++a;

// b = 2, a = 2

NSInteger c = a++;

// c = 2, a = 3

let b = ++a

// b = 2, a = 2

let c = a++

// c = 2, a = 3

--

NSInteger d = a--;

// d = 3, a = 2

NSInteger e = --a;

// e = 1, a = 1

let d = a--

// d = 3, a = 2

let e = --a

// e = 1, a = 1

Table 4-3. Basic binary operators in Objective-C and Swift

 

Objective-C

Swift

 

NSInteger a = 7;

NSInteger b = 3;

uint8_t g = 255;

uint8_t h = 0;

var a = 7

let b = 3

var c = "Hello "

let d = "9781484204078_unFig04-01.jpg"

let e = [1, 2, 3]

let f = [4, 5, 6]

let g = UInt8.max

let h = UInt8.min

+

NSInteger i = a + b;

// i = 10

let i = a + b

// i = 10

let j = c + d

// j = "Hello 9781484204078_unFig04-01.jpg"

var k = e + f

// k = [1, 2, 3, 4, 5, 6]

+=

a += 1 // a = 8

a += 1 // a = 8

c += d // c = "Hello 9781484204078_unFig04-01.jpg"

k += [7]

// k = [1, 2, 3, 4, 5, 6, 7]

-, -=

NSInteger l = a - b;

// l = 5

l -= b; // l = 2

var l = a - b

// l = 5

l -= b // l = 2

*, *=

NSInteger m = a * b;

// m = 24

m *= b; // k = 72

var m = a * b

// m = 24

m *= b // k = 72

/, /=

NSInteger n = 10 / 4;

// n = 2

n /= 2; // l = 1

CGFloat o = 10 / 4.0f;

// o = 2.5

var n = 10 / 4

// n = 2

n /= 2 // n = 1

let o = 10 / 4.0

// o = 2.5 (o is inferred to be a Double)

let p: Int = 10 / 4.0

// p = 2

%

NSInteger q = 10 % 4;

// q = 2

NSInteger r = -10 % 4;

// r = -2

NSInteger s = 10 % -4;

// s = 2

NSInteger t = -10 % -4;

// t = -2

let q = 10 % 4

// q = 2

let r = -10 % 4

// r = -2

let s = 10 % -4

// s = 2

let t = -10 % -4

// t = -2

let u = 10 % 4.0

// u = 2.0

let v = 10.5 % 4.5

// v = 1.5

&+

uint8_t w = g + 1;

// w = 0

let w = g &+ 1

// w = 0

&-

uint8_t x = h - 1;

// x = 255

let x = h &- 1

// x = 255

&*

uint8_t y = g * 2;

// y = 254

let y = g &* 2

// y = 254

&/

uint8_t z = a / 0;

// Division by zero is undefined

let z = a &/ 0

// z = 0

&%

uint8_t aa = 255 % 0;

// Remainder by zero is undefined

let aa = 255 &% 0

// aa = 0

??

N/A

let defaultSize = "M"

var selectedSize: String?

let orderSize = selectedSize ?? defaultSize

// orderSize = "M"

Table 4-4. Ternary conditional operators in Objective-C and Swift

 

Objective-C

Swift

 

NSInteger a = 0;

let a = 0

?:

NSInteger b = a ? a : 5;

// b = 5

NSInteger c = a ?: 5;

// c = 5

let b = a != nil ? a : 5

// b = 5

Logical Operators

Swift implements the three standard logical operators in the same way as Objective-C. The logical NOT operator (!) inverts a boolean value. In Objective-C, you may have also been used to using the NOT operator to check that a nonboolean variable is not nil.

[NSURLConnection sendAsynchronousRequest:request queue:queue 
completionHandler:^(NSURLResponse *response, NSData *data, NSError
*connectionError) {

  if (!error) {
    // ...
  }
}];

As will be further explained in the next chapter on controlling program flow, a regular (nonoptional) variable or constant in Swift cannot be checked for existence in a boolean manner.

The logical AND operator (&&) returns a boolean true if both expressions are true, and the logical OR operator (||) returns a boolean true if either expression is true. Both && and || short-circuit, such that if the first expression is false in the case of && or true in the case of ||, the second is not evaluated. It may therefore benefit performance to place more computationally expensive expressions to the right of simpler expressions. Logical operators are evaluated left to right, so use parentheses to group expressions and specify order of evaluation. And, generally, the use of parentheses is encouraged whenever it will help improve readability.

let levelCompleted = (defeatedEnemy && savedPrincess) || enteredCheatCode

Logical operators in Objective-C and Swift are summarized in Table 4-5.

Table 4-5. Logical operators in Objective-C and Swift

 

Objective-C

Swift

 

BOOL warmOutside = NO;

BOOL raining = NO;

BOOL sunny = YES;

let warmOutside = false

let raining = false

let sunny = true

!

BOOL bringAJacket = !warmOutside;

// bringAJacket = YES

let bringAJacket = !warmOutside

// bringAJacket = true

&&

BOOL goSwimming = sunny && warmOutside;

// goSwimming = NO

let goSwimming = sunny && warmOutside

// goSwimming = false

||

BOOL seeMovie = !warmOutside || raining;

// seeMovie = YES

let seeMovie = !warmOutside || raining

// seeMovie = true

Bitwise Operators

Bitwise operators enable lower-level programming capabilities typically reserved for manipulating raw data. Swift supports all of the bitwise operators available in Objective-C. An in-depth discussion of performing bitwise operations is beyond the scope of this book; however, the examples in Table 4-6 should be sufficient to match these operations between Objective-C and Swift. Observe also that each bitwise shift left or right doubles or halves the value using overflow operators, that is, &* 2 and &/ 2 in Swift, respectively.

Table 4-6. Bitwise operations in Objective-C and Swift

Table4-6a.jpg
Table4-6b.jpg

Shorthand assignment syntax is also available for each of the bitwise binary operators in Swift, just as in Objective-C. For example, a &= b is equivalent to a = a & b.

Advanced Operators

The advanced operators covered in this section include the is and as operators, and the pattern matching operator (~=). As in the Basic Operators section, these operators will be introduced narratively with mention of important points relative to their counterpart operators or operations in Objective-C, followed by a summary of examples in Table 4-7; the if conditional syntax used will be covered in the next chapter.

Checking if an object is of a certain class type in Objective-C involves calling two NSObject methods: -[NSObject isKindOfClass:] and  +[NSObject class]. Casting an object to a subclass is accomplished by prepending the instance with the subclass name in parentheses, which subsequently allows treating that object as the casted subclass. Because the actual object of its original type is actually returned, however, it is necessary to call -[NSObject isKindOfClass:] or –[NSObject respondsToSelector:] before calling a method of that casted subclass, in order to avoid a potential runtime exception if the cast was not successful.

Type checking and type casting are performed in Swift via the is and as operators, respectively. The is operator returns true if the stored value being checked is an instance of the specified type (or a subclass in the case of class types), or false if not. The as operator will return an instance of the casted type if successful. For class types, when a stored value being presented as of a specific class type is actually an instance of a subclass, the as operator will force downcast that instance to the subclass type. However, if the stored value is not actually an instance of the casted type (or a subclass in the case of class types), the downcast will fail at runtime and a runtime error will be triggered. Therefore, the as? operator should be used when it is not certain that the item being casted is actually an instance of the casted type (or a subclass for class types). The as? operator will always return an optional value, which will either be the casted instance, or nil if the cast fails. However, the compiler will flag an error and thus prevent writing code that attempts to explicitly cast to a type that the stored value is not an actual instance of (or a subclass of in the case of class types). Casting a value does not change the value in any way. For class types, full access to the properties and methods of the downcasted class type will only be available for the scope of the cast. The type checking and casting examples in Table 4-7 include empty class, subclass, and structure definitions, and program control flow statements necessary to demonstrate these concepts. Control flow statements are covered in Chapter 5 classes and structures in Chapter 7, and subclasses in Chapter 9.

Swift introduces the ability to overload an operator, and perhaps one of the most useful examples of this capability is the pattern matching operator (~=). By default, ~= simply compares two values using the == operator. The pattern matching operator is included here, complete with setup code, however, coverage of that setup code (which involves creating a function to overload the ~= operator) is deferred to Chapter 6.

Table 4-7. Advanced operations in Objective-C and Swift

 

Objective-C

Swift

 

// In MyCustomClass.h

@import Foundation;

@interface ParentClass : NSObject

@end

@interface Subclass : ParentClass

@end

@interface SomeOtherClass : NSObject

@end

// In MyCustomClass.m

@implementation ParentClass

@end

@implementation Subclass

@end

@implementation SomeOtherClass

@end

// In -[AnotherClass someMethod] in AnotherClass.m

NSArray *arrayOfClassInstances = @[[ParentClass new], [Subclass new], [SomeOtherClass new]];

struct StructA { }

struct StructB { }

let arrayOfStructureInstances: [Any] = [StructA(), StructB()]

class ParentClass { }

class Subclass: ParentClass { }

class SomeOtherClass { }

let arrayOfClassInstances = [ParentClass(), Subclass(), SomeOtherClass()]

func ~= (string: String, integer: Int) -> Bool {

    return string == "(integer)"

}

is

// In -[AnotherClass someMethod] in AnotherClass.m

for (id value in arrayOfClassInstances) {

   if ([value isKindOfClass:[ParentClass class]]) {

     NSLog(@"%@", NSStringFromClass([value class]));

   } else {

     NSLog(@"Not an instance of Subclass or ParentClass");

   }

}

/* Prints:

ParentClass

Subclass

Not an instance of Subclass or ParentClass

*/

for value in arrayOfStructureInstances {

   if value is StructA {

     println(_stdlib_getDemangledTypeName(value))

   } else {

     println("Not an instance of StructA")

   }

}

/* Prints:

...StructA

Not an instance of StructA

*/

as

// In -[AnotherClass someMethod] in AnotherClass.m

for (id value in arrayOfClassInstances) {

   Subclass *item = (Subclass *)value;

   if ([item isKindOfClass:[Subclass class]]) {

     NSLog(@"%@", NSStringFromClass([item class]));

   } else {

     NSLog(@"Not an instance of Subclass");

   }

}

/* Prints:

Not an instance of Subclass

Subclass

Not an instance of Subclass

*/

for value in arrayOfClassInstances {

   if value is Subclass {

     let item = value as Subclass

     println(_stdlib_getDemangledTypeName(item))

   } else {

     println("Not an instance of Subclass")

   }

}

/* Prints:

Not an instance of Subclass

...Subclass

Not an instance of Subclass

*/

as?

N/A

for value in arrayOfClassInstances {

   if let item = value as? ParentClass {

     println(_stdlib_getDemangledTypeName(item))

   } else {

     println("Not an instance of ParentClass or a subclass")

   }

}

/* Prints:

...ParentClass

...ParentClass

Not an instance of ParentClass or a subclass

*/

~=

N/A

let equal = "1" ~= 1

// equal = true

Operator Precedence and Associativity

Precedence rules apply when an expression contains two or more binary operators and parentheses are not used to group and explicitly specify the order in which operations should be performed. Associativity rules group these operations in a left-to-right or right-to-left fashion based on the operator type. A general best practice when dealing with a complex arithmetic expression is to always use parentheses; this not only helps to ensure intended precedence and associativity, but it also clearly conveys your intentions.

Table 4-8 compares operator precedence and associativity rules in Objective-C and Swift. Objective-C’s operator precedence is typically presented ordinally from 1st (most precedent) to 16th (least precedent). However, Swift’s precedence levels are cardinal, that is, the higher the number, the higher the precedence. So, for the benefit of comparison, Objective-C’s ordinal order of precedence is converted into a cardinal score (e.g., 1st becomes a score of 16, and 16th becomes a score of 1). Also notice that Swift precedence levels are a factor of 10 as compared with Objective-C, presumably giving Apple leeway to insert new precedence/associativity levels in the future.

Table 4-8. Binary operator precedence (and associativity) classifications in Objective-C and Swift

Table4-8a.jpg
Table4-8b.jpg

Tip  Due to the differences in precedence between Objective-C and Swift operators, be careful when migrating Objective-C code to Swift to ensure that arithmetic expressions perform the intended calculations.

Behind the scenes, Swift transforms a nonparenthesized expression made up of multiple operators from a flat list of operands and operators into a tree made up of parenthesized subexpressions based on operator precedence and associativity.

let a: Double = 1 + 2 * 3 / 4 % 5 // Flat list: 1, +, 2, *, 3, /, 4, %, 5
// Transformed into tree (1 + (((2 * 3) / 4) % 5))
// a = 1 + ((6 / 4) % 5) = 1 + (1.5 % 5) = 1 + 1.5 = 2.5

In the previous example, the + operator is lower precedence to *, /, and %, which are all of the same precedence and left-associative. Figure 4-1 presents this visually as a binary expression tree.

9781484204078_Fig04-01.jpg

Figure 4-1. Binary expression tree representing the expression 1 + 2 * 3 / 4 % 5

Understanding how Swift transforms flat expressions into trees is of lesser importance when parentheses are used to explicity group expressions.

Summary

As demonstrated in this chapter, operators in Swift facilitate performing a wide variety of operations, including doing math, checking equality, changing types, and shifting bits. Although operator precedence will logically be applied to flat expressions, it is always better to use parentheses to control order of operator execution and improve code readability.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.55.151