Home > Articles > Programming > General Programming/Other Languages

  • Print
  • + Share This
This chapter is from the book

Swift Programming Basics

We’ve explored the notion of classes, methods, and instance variables, but you probably still don’t have a real idea of how to go about making a program do something. So, this section reviews several key programming tasks that you’ll be using to implement your methods:

  • Declaring variables and constants
  • Understanding built-in swift data types
  • Making sense of optional values
  • Initializing objects
  • Using an object’s instance methods
  • Making decisions with expressions
  • Branching and looping

Declaring Variables and Constants

Earlier we documented what variable properties will look like in your Swift files, but we didn’t really get into the process of how you declare them (or use them). Nor did we talk about variables within methods!

Whatever the purpose, you declare your variables using this syntax:

var <Variable Name>[: Variable Type][Optional modifier][ = Initialization]

Holy cow—that looks complicated! In practice, nearly everything but the var keyword and the variable name are optional. I make a point of always trying to provide the variable type, but if you don’t, Swift will try to figure it out for you. The type is either a Swift data type or the name of a class that you want to instantiate and use.

Let’s begin by taking a look at a few Swift data types and how they are declared and used.

Swift Data Types

Swift includes a number of data types that enable you to work with common types of information. Most basic logic that we implement in this book will take advantage of one of these data types:

  • Int: Integers (whole numbers such as 1, 0, and –99).
  • Float: Floating-point numbers (numbers with decimal points in them).
  • Double: Highly precise floating-point numbers that can handle a large number of digits.
  • String: Collections of characters (numbers, letters, and symbols). Throughout this book, you’ll often use strings to collect user input and to create and format user output.
  • Bool: A Boolean value (that is, true or false), often used to evaluate conditions within your code.
  • Arrays: A collection of ordered values that are accessed via a numeric index.
  • Dictionaries: A collection of key/value pairs. A given value is accessed by providing its key.
Integers and Floating-Point Numbers

Let’s start with something easy: integers (Int) and floating-point numbers (Float or Double). To declare an integer variable that will hold a user’s age, you might enter the following:

var userAge: Int

If you wanted, you could even initialize it with a value, all in the same line:

var userAge: Int = 30

After a variable is declared, it can be used for assignments and mathematical operations. The following code, for example, declares two variables, userAge and userAgeInDays, and uses the first (an age in years) to calculate the second (the age in days):

var userAge: Int = 30
var userAgeInDays: Int
userAgeInDays = userAge * 365

Notice that for the userAgeInDays, I declare it, then use it later. You’re welcome to do this, or declare and initialize the variables on the exact same line.

Floating-point numbers work the same way—you declare them, then use them. A simple example to calculate the circumference of a circle (circumference = diameter * 3.141), for example, could be written like this:

var diameter: Float = 2.5
var circumference: Float = diameter * 3.141

Pretty easy, don’t you think? Swift data types have much more to offer as well. Let’s see what else they can do!

Strings

Strings are one of the most frequently used Swift types in this book. You’ll be using strings for user input and output, data manipulation, and so on. As with every other variable, the life of a string begins with a declaration and an initialization:

var myName: String = "John"

Here, a string (myName) is initialized to the value "John". Once initialized, the string can be manipulated using a number of techniques. String concatenation (adding two or more strings together) is performed with the addition (+) operator. To change myName to include my last name, I’d write the following:

myName = myName + " Ray"

You can even use a process called string interpolation to combine existing Strings, values returned by methods, and other Swift data types into a new String. Consider this line:

var sentence: String = "Your name is \(myName) and you are \(userAge) years old"

Here I’ve combined the myName string and userAge integer into a single string, assigned to a new variable named sentence. Any time Swift encounters the pattern \(<variable or method name>) in your code, it takes the result, turns it into a string, and substitutes it in place of the pattern. You can use this to quickly format strings based on other variables and methods.

In many languages, strings require special functions to check for equality. In Swift, the same comparison operators you’d use to compare two numbers also work for strings. We’ll look at comparisons a bit later.

Boolean Values

A Boolean value has only two states—represented by true or false in Swift. Booleans are most often used in comparisons, although some methods have Boolean parameters that you’ll need to supply. As expected, Booleans are initialized using the same pattern you’ve seen for numbers and strings:

var myFlag: Bool = false
Arrays

A useful category of data type is a collection. Collections enable your applications to store multiple pieces of information in a single object. An Array is an example of a collection data type that can hold multiple objects, accessed by a numeric index.

You might, for instance, want to create an array that contains all the user feedback strings you want to display in an application:

var userMessages: [String] = ["Good job!", "Bad Job", "Mediocre Job"]

Notice that the word Array doesn’t even appear in the declaration and initialization? That’s because all we need to do to declare an array is wrap the type we want to store (in this case, String values) in square brackets. If I wanted an array of integers, I’d use a type of [Int] and so on. The initialization values are provided as a comma-separated list enclosed in square brackets; if you use [] alone, the array is initialized as empty.

To access the strings in the userMessages array, you use an index value. This is the number that represents a position in the list, starting with 0. To return the "Bad job" message, we use the number 1 (the second item in the list):

userMessages[1]

You can also use the index to assign values to an array, replacing what is currently stored:

userMessages[1]="Try again"

Swift lets you add new items to the end of the list using the array’s append method. For example, to add a new message (“Meh”) to the end of userMessages, I might write the following:

userMessages.append("Meh")

There are several other means of accessing and modifying arrays that we’ll use over the course of the next 21 hours.

Dictionaries

Like arrays, dictionaries are another collection data type, but with an important difference. Whereas the objects in an array are accessed by a numeric index, dictionaries store information as key/value pairs. The key is usually an arbitrary string, whereas the value can be anything you want, even objects. If the previous userMessages array were to be created as a Dictionary instead, it might look like this:

var userMessages: [String:String] =
       ["positive":"Good job!", "negative":"Bad Job", "indifferent":"Mediocre Job"]

Similar to declaring the strings, I declare the dictionary without ever using the word dictionary. Instead, I provide the type data types that will form the keys and values within square brackets—for example, [<key data type>:<value data type>]. For the userMessage dictionary, I’m using keys that are strings, and values that are strings. The initialization is similar to an array, but consists of the key, a colon (:), and then the value. Each key/value pair is separated by a comma. Empty dictionaries can be created with the initializer [:].

To access a value in the dictionary, I index into userMessages just like an array, but using the key rather than an integer. To access the “Bad Job” message (tied to the “negative” key), I could type the following:

userMessages["negative"]

Keys can also be used to modify or assign new values. Here the key “apathy” is assigned the value “Meh”:

userMessages["apathy"] = "Meh"

Dictionaries are useful because they let you store and access data in abstract ways rather than in a strict numeric order.

Object Data Types

Just about everything that you’ll be working with in your iOS applications will be an object. Onscreen text, for example, will be instances of the class UILabel. Buttons that you display are objects of the class UIButton. You’ll learn about several of the common object classes in the next hour’s lesson. Apple has literally provided thousands of different classes that you can use to store and manipulate data.

Objects are declared and initialized just like Swift data types. For example, to declare and create a new instance of the UILabel class, you could use the following code:

var myLabel: UILabel = UILabel()

Here, the initializer is UILabel(). This returns a new, ready-to-use instance of the UILabel class. You can initialize all classes using this same syntax <class name>(), but most will require additional setup after initialization. To speed things along, many will provide convenience methods. Convenience methods speed the process of creating a new object by taking the basic parameters needed to create and configure the object, all at once.

Convenience Methods

When we initialized the UILabel instance, we did create an object, but it doesn’t yet have any of the additional information that makes it useful. Attributes such as what the label should say, or where it should be shown on the screen, have yet to be set. We would need to use several of the object’s other methods to really turn it into something ready to be displayed.

These configuration steps are sometimes a necessary evil, but Apple’s classes often provide a special initialization method called a convenience method. These methods can be invoked to set up an object with a basic configuration so that it can be used almost immediately.

For example, the NSURL class, which you use later to work with web addresses, defines a convenience method called initWithString. We can use it to create a brand-new NSURL object, complete with the URL, just by typing the following:

var iOSURL: NSURL = NSURL(string: "http://www.teachyourselfios.com/")!

This is where we (briefly) go off the tracks. Notice that nowhere in that line does initWithString appear. The initWithString method is the name of a convenience method in Objective-C. The method still goes by the same name when used in Swift, but it takes on a simplified form.

The general rule of thumb is that, in Swift, the initWith is removed from the name of convenience method. Whatever remains of the name becomes the first named parameter of the method. A named parameter, as you’ll learn a bit later, is a parameter that requires you to spell out its name in the method call (in this case, string).

Because Xcode supports autocompletion, it is usually pretty easy to start typing in a method named and find it in the list that appears. Just keep in mind that what you see in the Xcode documentation doesn’t necessarily apply to both Objective-C and Swift.

Type Conversion and Type Casting

In your adventures in Swift, you will encounter code that doesn’t quite work the way you want. You’ll find legacy CGFloat floating-point numbers that must be used in place of Swift Float. You’ll find places where you need to turn Floats into Ints, and vice versa. You’ll even encounter objects that have no idea what they are. To get around these little snags, you’ll likely employ type conversion, or type casting.

Type Conversion

For most of the simple data types, you can convert between types by using the syntax: <Type Name>(<Value to Convert>). For example, if a method calls for a CGFloat and you have a Float value (in the variable myFloat), you can convert it to the proper type with the following:

CGFloat(myFloat)

Swift does everything it can to silently bridge these older data types with the new built-in Swift types—and it is usually very successful. Unfortunately, sometimes this manual conversion will have to happen.

Another common circumstance is when a method returns an object of one type when it needs to be another. When this happens, you must type cast the result.

Type Casting

Type casting takes an object of a higher-level class and tells Xcode which specific subclass it should be. Some methods will return an object of the type AnyObject rather than a specific type. Does this make any sense? Not really, but it happens often.

For example, the NSDate class includes several methods that return a date, but instead of being of the type NSDate, they are of the type AnyObject. The NSDate type method distantPast is one of these methods:

var myPastDate: NSDate = NSDate.distantPast() as NSDate

Because distantPast() results in an object of the type AnyObject, we must “tell” Xcode that it is really an NSDate by adding as NSDate to the end of the assigment. Using the syntax as <class name> after any object will attempt to type cast that object as being of whatever class you name.

After a variable is cast to an object of the correct type, we can interact with it directly as that type. This looks a bit unusual, I know, but it will come in handy later in the book. It’s easier to understand when you see it in an actual application; so for the moment, just be aware that it is an available development tool.

Constants

Constants are declared and initialized just like variables, except they begin with the keyword let. For example, to create a constant named lastName that holds a String with my last name, I would write the following:

let lastName: String = "Ray"

The key difference between a constant and a variable is that constants, once assigned, cannot be changed or reassigned. This, however, isn’t as limiting as you might think. When you assign an object to a constant, you can access and modify all the variable properties in that object, execute all its methods, and so on. It can still be used just like any other variable—you just can’t reassign it later.

Constants are more efficient than variables and should be used in their place wherever possible. I think you’ll be surprised to find that we use more constants in our applications than actual variables.

Optional Values

Possibly the most confusing, infuriating thing about Swift is the notion of optional values. In theory, it’s really quite simple. If you’ve developed in other languages, you’ve almost certainly written code that thought it was working with a value, only to find that the value had never been set or that a method that was supposed to return a value didn’t. Making the assumption that we know what is in a variable is dangerous, but it’s something that developers do every day.

In Swift, Apple decided that developers should acknowledge when they’re using a value that might not contain what they expect. The result requires interesting additions to the development process:

  1. Method, variable, and constant declarations should state when they may not have, or may not return, a value. These are known as optional values.
  2. Why would a method programmed to return a result ever make that result optional? If the method has bad input, or otherwise can’t complete the operation it is tasked with performing, it makes perfect sense to return “nothing”—represented in Swift using the keyword nil.
  3. When attempting to access methods or variables that are optional, developers must unwrap the values. This means that the developer acknowledges that he or she knows what is in a variable (or returned by a method), and wants to access and use the results.

Now, you might think to yourself, “Hey, I know what I’m doing, I’m not going to write any code where I name a variable or method return type as optional! That would just be extra work!” You’re right, it is extra work—but it’s utterly unavoidable.

All the code that makes up the Cocoa Touch classes is being updated by Apple to denote which variable properties and methods return optional values—and there are many (and the list is growing). I could tell you stories about the number of times I’ve opened a project while writing this book, only to find that Apple has changed a class somewhere that breaks the code I’ve written. That’s one of the difficulties of being an early technology adopter.

Okay, enough ranting. What does all of this actually mean in terms of coding?

Declaring Optionals

First, when declaring a variable, you can define it as optional by adding a ? after the type name:

var myOptionalString: NSString? = "John"

This also means that if the string isn’t immediately initialized, it automatically contains the value nil.

For method definitions, you denote an optional return value by adding ? after the return type. In the sample class in Listing 3.1, I defined the method as having an optional return value of NSDate using this syntax in the declaration:

func myInstanceMethod(myString: String, myURL: NSURL) -> NSDate? {
Unwrapping and Implicit Unwrapping

After you’ve either created (or encountered Swift variables and methods) that are optional, you need to know how to access them. Accessing optional values is called unwrapping in Swift. The easiest, most brute-force way is to use optional values is to unwrap them by adding an exclamation mark (!) to the end of their name.

In other words, each time I wanted to use the value in myOptionalString, I would reference it as follows:

myOptionalString!

The same goes for the myInstanceMethod method. To use it, I might write a line like this:

var myReturnedDate: NSDate = myInstanceMethod("A cool string", myURL: iOSURL)!

The addition of the ! tells Xcode that we want to access the return value and that we don’t care if it is nil. We can take this a step further by defining what is called an implicitly unwrapped optional. This is just an optional value that will always be unwrapped automatically when we use it.

To create an implicitly unwrapped variable, you add a ! after the type name. For example, I could write the preceding line of code using an implicitly unwrapped variable, like this:

var myReturnedDate: NSDate! = myInstanceMethod("A cool string", myURL: iOSURL)

This declares myReturnedDate as an optional NSDate variable, but one that will be implicitly unwrapped. I can assign it the result of an optional method without unwrapping the return value of the method (because both are optional). However, when I go to use myReturnedDate elsewhere in my code, it will automatically be unwrapped for me—just as if I had put the ! after it each time.

You really won’t be doing this very often, but Xcode is going to do it a lot when it writes code for you. Why? Because every interface object that connects to your code will be referenced through an implicitly unwrapped variable. An interface object may be nil before it is loaded, so it has to be optional; but once your code is active, it should always have a value, and there’s no point in hindering its use—thus, it is implicitly unwrapped for you.

Optional Binding

Another (gentler) way to deal with optional values is called optional binding. This is the assignment of an optional value to a constant. If the assignment succeeds, the optional value is accessible through the constant. If it fails, the optional value is nil.

Applying optional binding to the myOptionalString variable, I might write this simple logic to test to see whether an optional value should be used:

if let stringValue:String = myOptionalString {
    // myOptionalString has a non-nil value.
}

This is a good approach for working with optionals in production-ready code. It gives you an opportunity to react to situations where optionals are set to nil and errors may have arisen. If you unwrap an optional and try to work with it even if it is nil, you may crash your code.

For most of the examples in the book, I manually unwrap values with ! because the code is simple and we know how the different components are going to interact. In apps bound for the App Store, I recommend using optional binding to trap for error conditions that you may not have anticipated.

Using Methods

You’ve already seen how to declare and initialize objects, but this is only a tiny picture of the methods you’ll be using in your apps. Let’s start by reviewing the syntax of calling methods in Swift.

Method Syntax

To use a method, provide the name of the variable that is referencing your object followed by the name of the method, followed by a period, the name of the method, and empty parentheses () (empty if there are no parameters). If you’re using a type (class) method, just provide the name of the class rather than a variable name:

<object variable or class name>.<method name>()

Things start to look a little more complicated when the method has parameters. A single parameter method call looks like this:

<object variable or class name>.<method name>([parameter:]<parameter value>)

Earlier I noted that convenience initialization methods will usually include at least one named parameter, such as string when initializing an NSURL object:

var iOSURL: NSURL = NSURL(string: "http://www.teachyourselfios.com/")!

This is important to note because the style of using an initial named parameter is only really used in convenience initialization methods. In other (general use) methods, the first parameter is just provided as a value.

For example, let’s look at a method that takes multiple parameters:

var myFullName: String = "John Ray"
var myDescription: String =
myFullName.stringByReplacingOccurrencesOfString(myFullName, withString: "is awesome!")

This code fragment stores my name in the myFullName variable, then uses the stringByReplacingOccurrencesOfString:withString method to change my last name from “Ray” to “is awesome!”

In this example, the first parameter to the stringByReplacingOccurrencesOfString:with-String method has no name; I just put in the value (myFullName). The second parameter does have a name (withString:), which must be provided along with the value.

The syntax for multiple parameter method calls looks like this:

<object variable or class name>.<method name>([parameter:]<parameter value>,
     <parameter>:<parameter value>, <parameter>:<parameter value> ...)

Chaining

Something that you’ll see when looking at Swift code is that often the result of a method is used directly as a parameter within another method. In some cases, if the result of a method is an object, a developer may immediately use a method or variable property of that object without first assigning it to a variable. This is known as chaining.

Chaining results directly eliminates the need for temporary variables and can make code shorter and easier to maintain.

For example, consider this completely contrived code:

var myString: String = "JoHN ray"
myString = myString.lowercaseString
myString = myString.stringByReplacingOccurrencesOfString("john", withString: "will")
myString = myString.capitalizedString

Here I’ve created a string (myString) that holds my name with very very poor capitalization. I decide that I want to replace my first name (John) with my brother’s name (Will). Because I cannot just search and replace on John because my capitalization is all messy and I don’t want to try to remember how I wrote my name (this is contrived folks), I decide to first convert myString to lowercase by accessing the lowercaseString variable property. Once complete, I can just search for john and replace it with will without worrying about capitalization. Unfortunately, that means I still need a properly capitalized version of the string when I’m done. So, I access the capitalizedString variable property of my string when finished, and use its value for myString. (In case you’re wondering, capitalizedString provides a copy of the string with all of the first letters capitalized.)

The code should make sense, even if my logic is a bit shaky. That said, each of the methods and variable properties I’ve used return a string. Instead of assigning things over and over, I can chain each of these actions together into a single line:

var myString: String = "JoHN ray".lowercaseString.stringByReplacingOccurrencesOfString("john", withString: "will").capitalizedString

Chaining can be a powerful way to structure your code, but when overused it may lead to lines that can be difficult to parse. Do what makes you comfortable; both approaches are equally valid and have the same outcome.

Closures

Although most of your coding will be within methods, you will also encounter closures when using the iOS frameworks. Sometimes referred to as handler blocks in the Xcode documentation, these are chunks of code that can be passed as values when calling a method. They provide instructions that the method should run when reacting to a certain event.

For example, imagine a personInformation object with a method called setDisplayName that would define a format for showing a person’s name. Instead of just showing the name, however, setDisplayName might use a closure to let you define, programmatically, how the name should be shown:

personInformation.setDisplayName({(firstName: String, lastName: String) in
                   // Implement code here to modify the first name and last name
                   // and display it however you want.
                })

Interesting, isn’t it? Closures are relatively new to iOS development and are used throughout this book. You’ll first encounter closures when writing alerts. The closure will provide the instructions that are executed when a person acts on an alert.

Expressions and Decision Making

For an application to react to user input and process information, it must be capable of making decisions. Every decision in an app boils down to a true or false result based on evaluating a set of tests. These can be as simple as comparing two values, to something as complex as checking the results of a complicated mathematical calculation. The combination of tests used to make a decision is called an expression.

Using Expressions

If you recall your high school algebra, you’ll be right at home with expressions. An expression can combine arithmetic, comparison, and logical operations.

A simple numeric comparison checking to see whether a variable userAge is greater than 30 could be written as follows:

userAge>30

When working with objects, we need to use variable properties within the object and values returned from methods to create expressions. If I have stored an NSDate object with my birthday in it (myBirthday), I could check to see whether the current day is my birthday with the expression:

myBirthday.isEqualToDate(NSDate())

Expressions are not limited to the evaluation of a single condition. We could easily combine the previous two expressions to find a person who is over 30 and is celebrating their birthday today:

userAge > 30 && myBirthday.isEqualToDate(NSDate())

As mentioned repeatedly, you’re going to be spending lots of time working with complex objects and using the methods within the objects. You cannot make direct comparisons between objects as you can with simple data types. To successfully create expressions for the myriad objects you’ll be using, you must review each object’s methods and variable properties.

Making Decisions with if-then-else and switch Statements

Typically, depending on the outcome of the evaluated expression, different code statements are executed. The most common way of defining these different execution paths is with an if-then-else statement:

if <expression> {
  // do this, the expression is true.
} else {
  // the expression isn't true, do this instead!
}

For example, consider the comparison we used earlier to check a myBirthday NSDate variable to see whether it was equal to the current date. If we want to react to that comparison, we might write the following:

if myBirthday.isEqualToDate(NSDate()) {
    let myMessage: String = "Happy Birthday!"
} else {
    let myMessage: String = "Sorry, it's not your birthday."
}

Another approach to implementing different code paths when there are potentially many different outcomes to an expression is to use a switch statement. A switch statement checks a variable for a value and then executes different blocks of code depending on the value that is found:

switch (<some value>) {
  case <value option 1>:
    // The value matches this option
  case <value option 2>:
    // The value matches this option
  default:
    // None of the options match the number.
}

Applying this to a situation where we might want to check a user’s age (stored in userAge) for some key milestones and then set an appropriate userMessage string if they are found, the result might look like this:

switch userAge {
    case 18:
        let userMessage: String = "Congratulations, you're an adult!"
    case 21:
        let userMessage: String = "Congratulations, you can drink champagne!"
    case 50:
        let userMessage: String = "You're half a century old!"
    default:
        let userMessage: String = "Sorry, there's nothing special about your age."
}

Repetition with Loops

In some situations, you will need to repeat several instructions over and over in your code. Instead of typing the lines repeatedly, you can loop over them. A loop defines the start and end of several lines of code. As long as the loop is running, the program executes the lines from top to bottom and then restarts again from the top. The loops you’ll use are of two types: for loops and condition-based while/do-while loops.

for Loops

In a for loop, the statements are repeated a (mostly) predetermined number of times. You might want to count to 1000 and output each number, or create a dozen copies of an object. These are perfect uses for a for loop.

The for loop you’ll encounter most often consists of this syntax:

for  <initialization>;<test condition>;<count update> {
  // Do this, over and over!
}

The three “unknowns” in the for statement syntax are a statement to initialize a counter variable to track the number of times the loop has executed, a condition to check to see whether the loop should continue, and finally, an increment for the counter. A loop that uses the integer variable count to loop 50 times could be written as follows:

for var count=0;count<50;count=count+1 {
    // Do this, 50 times!
}

The for loop starts by setting the count variable to 0. The loop then starts and continues as long as the condition of count<50 remains true. When the loop hits the bottom curly brace (}) and starts over, the increment operation is carried out and count is increased by 1.

for loops can also iterate over collections using the following syntax:

for  <variable> in <collection> {
  // Do this for each value in the collection, where <variable> contains the value
}

Consider the array of messages we created earlier in the hour:

var userMessages: [String] = ["Good job!", "Bad Job", "Mediocre Job"]

To loop over this array of messages, we can write a “for in” loop as follows:

for message in userMessages {
    // The message variable now holds an individual message
}

The same applies to dictionaries as well, but the syntax changes just a little bit. If userMessages is defined as a dictionary:

var userMessages: [String:String] =
       ["positive":"Good job!", "negative":"Bad Job", "indifferent":"Mediocre Job"]

We can loop over each key/value pair like this:

for (key, value) in userMessages {
    // The key and value variables hold an individual dictionary entry
}
while and do-while Loops

In a condition-based loop, the loop continues while an expression remains true. You’ll encounter two variables of this loop type, while and do-while:

while <expression> {
  // Do this, over and over, while the expression is true!
}

and

do {
  // Do this, over and over, while the expression is true!
} while <expression>

The only difference between these two loops is when the expression is evaluated. In a standard while loop, the check is done at the beginning of the loop. In the do-while loop, however, the expression is evaluated at the end of every loop. In practice, this difference ensures that in a do-while loop, the code block is executed at least once; a while loop may not execute the block at all.

For example, suppose that you are asking users to input their names and you want to keep prompting them until they type John. You might format a do-while loop like this:

do {
    // Get the user's input in this part of the loop
} while userName != "John"

The assumption is that the name is stored in a string called userName. Because you wouldn’t have requested the user’s input when the loop first starts, you would use a do-while loop to put the test condition at the end.

Loops are a very useful part of programming and, along with the decision statements, will form the basis for structuring the code within your object methods. They allow code to branch and extend beyond a linear flow.

Although an all-encompassing picture of programming is beyond the scope of this book, this should give you some sense of what to expect in the rest of the book. Let’s now close out the hour with a topic that causes quite a bit of confusion for beginning developers: memory management.

  • + Share This
  • 🔖 Save To Your Account