ios

The Xcode Build Process (& more)

The Build Process

Today I'm going to write about a few (read: many) things that I've been meaning to get to - I'll touch VERY briefly upon the Xcode build process, blocks & closures, symbolic links using terminal. A followup post will be about Unit Testing in Swift, and I'll possibly tack on an interesting (seemingly simple) coding problem at the end of the second post.

The build process! Xcode has hidden so many internals from us that without setting compiler flags and checking build logs, we really don't know exactly how our Swift or Objective-C code compiles to a machine code executable. Let's start with build logs, since they can be quite telling - the way to do this is to navigate to the "report navigator" tab in your file navigator (just hit Cmd+8). Build your project with cmd+B and then click "build" under project name dropdown. 

Results of displaying a build log in Xcode

Results of displaying a build log in Xcode

It's too much for me to go into what some of the dropdown information means (you can click on any of the build steps and a button to expand the process log should appear) - this is mostly because I just plain don't know. I did some googling around to try and find some reachable material for someone of my level, but it would take me longer than I want to spend on this article figuring out what it all means. With that being said, here are the basic steps of an Xcode build - preprocessing #include and import statements, compilation into assembly (you can view the assembly code by clicking "debug->debug workflow->always show disassembly),  machine code translation into an executable file and then linking executable files together (using the Mach-O linker). Magical, isn't it? There's more information about each step involved, but it does get very low-level; although it would take me a while to fully understand what happens under the hood of a build command, I know it's in my best interest to spend the time familiarizing myself with the process.

Delegates, Blocks & Closures

Imagine the following scenario - you have a tableview in which you need to download images on a background thread, display them if they exist, and if not, display some other placeholder image. You can't run this on the main thread since you'll be guaranteed to provide a horrible user experience - do it on a background thread. Delegation is a possible solution here since we have a one to one form of communication going on (controller to utility class doing the download). I personally think the best use case for delegation is passing data backwards to a parent controller - however, you can also use delegation to break controllers down into smaller modules and avoid large amounts of code in one place. Downloading images a nice example of that - we could also use GCD within the VC code to asynchronously download images. We could have something like:

    -(void) viewDidLoad {

    NSURL *urlForDownload = self.urlArray[objectAtIndexPath: indexPath.row];

    UIImage * cellImageToDisplay = [self downloadImageInBackgroundWithURL: urlForDownload];

    if (cellImageToDisplay) {

        cell.image = cellImageToDisplay;

    }}

    -(UIImage *)downloadImageInBackgroundWithURL:(NSURL *)url { ...a lot of code here...}

Assume that "downloadImageInBackgroundWithURL" returns a UIImage (we do all of the computation and type conversions within that method). This could add more code than we want to see in our ViewController, so we could break it up using delegates. Delegation requires three components: a protocol, a delegate and a delegator. The glue between the delegate and the delegator class is the protocol - both know about the protocol, but the delegator has no knowledge of the delegate. Makes sense, right? If you want something done, you don't care who does it, as long as they're capable of doing it. This is how delegation works - the delegating class has no knowledge of the class doing some kind of work - the way it knows that it is capable is because it implements the PROTOCOL methods.

Class A, a VC, adheres to a certain protocol. It says "ok, I promise to carry out these protocol methods". A model class (Class B), in this case an image downloading utility class, is aware of the protocol (I often declare them in the same class). It also has a weak property (to avoid retain cycles) called "delegate" which it knows will adhere to the protocol method(s). 

Class A imports the header from Class B, which gives it knowledge of the protocol methods and the "imageDownload" method implemented by Class B. It creates an instance of Class B and declares itself as ClassB.delegate, effectively saying that whenever Class B refers to a delegate within a method, that delegate will actually refer to Class A. We can call the "imageDownload" method from Class B and pass it a URL - Class B will then use that URL to download an image, and will call [self.delegate do something with the image we just downloaded]. Remember that self.delegate really means Class A, so now Class A has an image that has been downloaded for it in a totally separate class on a background thread. Delegation allows for some loose coupling, since Class B has no knowledge of Class A but still manages to send it data. If this sounds confusing, just check out the code screenshots below and it'll clear things up. This is a way to use delegates as a callback! 

Our protocol declaration (written in the same file as the delegating class).

Our protocol declaration (written in the same file as the delegating class).

The implementation of our delegating computation. This downloads an image based on what URL was passed to it and sends that data back to its delegate.

The implementation of our delegating computation. This downloads an image based on what URL was passed to it and sends that data back to its delegate.

This is the delegate class code. It implements the protocol method (which is called by Class B when it has finished downloading data). The protocol method is what gets passed the data as a parameter, which can then be used by the delegate (in this case, our ViewController class).

This is the delegate class code. It implements the protocol method (which is called by Class B when it has finished downloading data). The protocol method is what gets passed the data as a parameter, which can then be used by the delegate (in this case, our ViewController class).

This seems like a lot of code to download an image using an NSURLSessionTask - we could have put all of this code into our ViewController class, but that has 2 drawbacks: 1. It makes our controller file much larger and 2. It isn't reusable. By factoring out the image downloading code into a separate class and including a protocol, any other class can conform to this protocol and have images downloaded asynchronously without having to write more code.

Onto blocks and closures - anonymous functions that can be passed around as objects. Known in other languages as lambda functions, blocks have the useful ability to capture variables in the same lexical scope (lexical scope refers to a practice whereby a range of functionality of a variable is set so that it can only be accessed from the code in which it has been defined). Blocks are then, essentially, objects that can carry out some computation or perform some operation (think function or method) while being able to use variables defined outside of the block. can be used in place of delegation, since instead of dealing with a large amount of code that potentially isn't reusable, we can just create a block and define and use it inline. Think of blocks as some object you can use to carry data and throw around. Complete some task, call a block and pass it the data, throw it to a controller (which will call the method to complete said task and implement the block), and use the data that was passed. Ta-da! A perfect way to create cleanly-written callbacks. 

The same task can be accomplished (arguable more elegantly) using blocks! That's right, we've finally arrived at blocks...the syntax can be intimidating (which is often dealt with by using typedef to make them more readable), but worry not - click here for a quick guide to block syntax. Let's look at some code that makes use of blocks and also downloads images asynchronously - the key difference to note between this technique and the delegate approach is that with blocks, we don't need to declare a protocol and implement some separate method that makes use of the data. We can just call the block from wherever we want and use the code right there in the same lexical scope. No extra method, less code, and variables right in front of your face so there's no confusion about what data is being used and where it came from.

 *NOTE - You can refer to "self" within a block; this will result in a memory leak for the simple fact that blocks hold strong references to self parameters used within the block. The way around this is to use a "block variable" - this is declared before the block and simply REDECLARES self as a weak variable. You then use that block variable within the block body. 

If you wanted to use blocks to download images for a tableView, it's important to keep data synchronized so that the tableView cell receives and displays the correct image. You also need to think about caching images, checking for an existing image for that data model instance before downloading it (if it doesn't exist, download it and save it to that model instance. Other things that could be considered include 1. How to save downloaded images to disk as a file and store the file path to the model instance 2. The best way to load images from disk asynchronously 3. Figure out an efficient way to find out whether the image exists on disk so that you know whether to load it or download it 4. Choosing how you want to clear the cache 5. Factoring all of this logic out of a VC. It clearly isn't as simple as calling a download method with a completion handler at the end. 

Here we actually process the data, downloading an image based on a URL passed to the method.

Here we actually process the data, downloading an image based on a URL passed to the method.

We call the block from our VC and use the data contained in it.

We call the block from our VC and use the data contained in it.

As you can see, we require significantly less code when using a block based callback method. We use the build in NSURLSession API to create a task and download the image, and we call our completion handler (which we passed as an argument to our download method) on the main thread. 

The block we created as an argument for our method is able to hold a BOOL and a UIImage as parameters - the block itself doesn't do anything with this data other than carry it around. In our VC is where we invoke the block, gain access to the data it's carrying with it, and do something with that data. Concise, elegant, and you can see exactly what's going on inline!

 

Symlinks

Symbolic links, which we can create at the command line, allow an object to point to a different object in the file system. They're like aliases for whatever you're pointing to - instead of having to type in the file path for SublimeText in order to use it to open a particular file, you can create a symlink for it and have "sublime" point to that executable file path. 

The way to create a symlink is to use the terminal command "ln -s", followed by the original file path, followed by the alias file path. If you had an installed version of SublimeText sitting in your "Downloads" folder, here's how you would set up a symlink so that typing "sublime" at any point in a terminal session (outside of running a server or being in REPL) will open SublimeText:

    $ ln -s~/Downloads/Sublime\ Text\2.app/Contents/SharedSupport/bin/subl /usr/local/bin/sublime

The ln -s command tells the system we're creating a symbolic link. Sublime Text 2 actually has a unix executable file that is hidden in the package contents. We want to point to that by creating an alias (a symlink) in our local user bin directory. The bolded part is where we are doing that - adding "sublime" at the end tells the system that when we type "sublime" in to terminal, we want to access the unix executable file path that comes bundled with Sublime Text 2. 

 

 

 

 

 

Reference and Value Types in Swift

References and Values in Theory

In Swift, a class is considered a reference type, plain and simple. This is similar to Objective-C, where everything that inherits from NSObject is a reference type. Being a reference type means that references share one single copy of data in memory, and they all refer to that same memory address. An issue with this involves the mutability of such an object - if somebody using a reference to Object A in a class changes some mutable values around, and I’m using Object A in a different class and I’m expecting a different value, we have a problem. 

Structs, enums and tuples are all value types in Swift (Objective-C also uses value types in number literals or C structs). If we say that:

let entree = Entree ()

let entree.protein = “Tofu”

let meal = entree

meal.protein = “Chicken”

Then entree.protein should ALSO have a value of “Chicken”. Both entree and meal reference the same object in memory, so changing the value of one changes the other. 

If we redefined the above as a struct, then the it would become a value type, and changing one would NOT change the other. Changing the protein value of meal wouldn’t change the protein value of entree.

It’s important to note that “let” as a constant keyword means different things for value and reference types - for reference types, let means that the reference must remain constant, but the properties are mutable. For value types, let means that the instance must remain constant, meaning that properties are immutable. 

Struct foo { var isReferenceType = False }

Class foo2 {var isReferenceType = True }

let exampleOne = foo ()

let exampleTwo = foo2 ()

// the below code WON’T work, since we are attempting to change the reference of a reference type

exampleTwo = foo

//the below code WILL work, since we are attempting to change a property value for a reference type

exampleTwo.isReferenceType = False

//the below code WON’T work, since we are attempting to change a property value for a value type

exampleOne.isReferenceType = True

 

To sum up, we can’t change the reference of a let constant if it’s a class, but we can change property values of that reference. We also can’t change property values of a let constant if it’s a struct. 


It’s important to note that Swift favors values almost exclusively. If you need to compare two struct instances for equality (assume we’re talking about value equality, not memory equality) then we need to conform to the Equatable protocol (this is something often required with value types). 

extension structName: Equatable { 

    func == (firstArgument: structName, secondArgument: structName) {

        return (firstArgument.value1 == secondArgument.value1) && (firstArgument.value2 == secondArgument.value2)

    }

}

To make data thread-safe, we would need to use a reference type (class) and implement locking. If you need to compare memory (physical) equality, then you should use a reference type. 

References and Values in Practice

Swift makes use of an optimization technique known as Copy-on-Write for value types - if you assign Object B = Object A, Swift will let both share a memory address even though they're value types. Object B will simply contain a reference to Object A. When the value of one changes, a copy is made, and the objects no longer share a memory address. In a cool optimization twist, if you later change one other object to match the other AGAIN, they'll once again share a memory address.

With some low-level Swift programming (of which there are a few resources about online, none of them actually any good), you can prove that copy-on-write exists - HOWEVER, it only seems to work for a few built-in types (such as arrays)! That's because if you want all that optimized goodness that comes from copy-on-write, you've got to implement it yourself for your custom value types. 

To learn about implementing it yourself, and how to build a Swift array from scratch, check out this article by Mike Ash. He runs an incredibly deep and advanced blog on Objective-C and Swift programming, and writes articles all the way down to the compiler level. Most of it is beyond me right now, but it's still good to glance at. 

You mix and match reference and value types all the time - remember that literals are value types, so whenever you have a a class variable that’s a string or a custom struct you created in a different file, your class is a reference type containing value types. This is common, and works out nicely. 

Things get tricky when you have a reference type contained within a value type. 

Let’s get something cleared up that’s related to the protocol-oriented nature of swift - value types should be equatable! This the expected behavior of a value type (as mentioned in a WWDC15 talk); if we have two integers (a value type in swift), don’t we expect to be able to compare their VALUES? I know I do. The same goes for strings, which, as a literal, are also equatable value types. 

Value types need to implement the == operator. The three properties of equality that must be addressed are that the comparison must be reflexive, symmetric and transitive

  1. To be reflexive, we must make sure that “x == x” returns true.
  2. To be symmetric, we must make sure that “x==y” and “y==x” return the same value, whether true or false.
  3. To be transitive, our == operator must make sure that is “a==b” is true, and “b==c” is true, then “a==c” must also be true. 

If your struct contains variables that are already literal value types, then you can declare the equatable function as a global function and simply compare the variables using the == operator. If your struct contains a struct, that nested struct should also conform to the equatable protocol in order to work your way up. It’s also convention to declare protocol adherence as an extension of the Type definition. 

Making reference types equatable is slightly different - it’s entirely case-specific. Say you have a “Person” class (a reference type) - it has a name variable. Two people can have the same name, but be different people. So how you implement whether or not they're equatable is up to you. 

Let’s return to a value type containing a reference type. We can have a struct be mutated very easily if it contains a reference type. There are ways around this, some better than others, which we’ll visit in a moment. Look at the screenshot below - we have two struct instances, one equal to the other (remember copy-on-write here). We change the value of a reference type variables of the first - desired behavior would be that since they’re both value types, changing data in one wouldn’t affect the other. Not so.

We've managed to mutate a struct by changing data in a different struct! This showcases their coupling based on the existence of a reference type in the struct definition.

We've managed to mutate a struct by changing data in a different struct! This showcases their coupling based on the existence of a reference type in the struct definition.

We can prevent this by forcing our Restaurant to use an explicit initializer - this actually creates a copy of the Person being passed in, instead of holding a reference to it. If we do this, and we pass in "managerTest" to be the manager, our Restaurant will use a COPY of the "managerTest" object. You can check the memory addresses and see that they're different. This means that mutating values of the "managerTest" object won't mutate the "manager" property of our Restaurant. Without this initializer, the two share a memory address, so mutating one mutates the other. The flaw in our design is when we act directly on "bestResto.manager.name" - there is nothing preventing this kind of mutation. 

We can mutate struct values here by just acting directly on the struct instance instead of the original reference type we used to create the "Manager".

We can mutate struct values here by just acting directly on the struct instance instead of the original reference type we used to create the "Manager".

The concept at hand here, a far cry from where we came, is data encapsulation. We've covered this in a previous post, but not with the complexity of a reference type contained within a value type. Let's start planning: a good solution would be to create setters and getters for the “manager” property of our Restaurant struct - get simply returns the current value, and set would create a copy of the manager and set the “manager” property to be equal to the new value. This way, if the data is changed, a deep copy of the reference type is created (vs a shallow copy, which creates a new object that simply points to the copied object...aka a referencing object). 

See any problems with this? I do. For one, we're creating a copy every single time the data is written to, instead of just mutating the existing copy. How can we say "If this object is currently referencing another object (shared resource) then make a deep copy with the changes. If nothing else is referencing this, just mutate the existing object instead of making a copy"? In comes "isUniquelyReferencedNonObjC", a method that detects the reference count of an object and returns a boolean value. You pass in the address of an object, it find other references to that object, and if there are no other references, it returns "True". Basically, if this returns "True" for our manager object, it has nothing else referencing it, and we can just mutate the existing copy. This raises a few questions that we'll address in a moment. 

Check out the code below - this is in a project. We have out VC code which creates two structs, the second equal to the first. We check the memory address of the MANAGER (which is a reference type), NOT the struct. We only implemented copy-on-write for the manager here - remember that custom structs don't implement copy-on-write automatically, so if we wanted that, we'd have to implement it ourselves. In this case, we just did it for the Person object. 

The managers share the same memory address - this is what we'd expect. After we change the manager object of the second restaurant object, the memory addresses are suddenly different! Our copy-on-write worked! The "withUnsafePointer" code is how we get the memory address of a struct. This is to prove that our struct doesn't implement copy-on-write (they have different addresses in memory from the very beginning, even though they have equivalent values). The other functions used to obtain memory addresses are just C functions that we're using in our Swift code.  

Memory address console prints to show copy-on-write in action

Memory address console prints to show copy-on-write in action

The last thing I'll add is a screenshot of my code for Restaurant - it changed since the last time we wrote it. The "manager" variable is now private, and we can only change it by using explicit APIs - this is to avoid developers accidentally changing the manager of a restaurant. If they want to change it, they call a clearly defined API that tells them exactly what they're doing so that any confusion is avoided. We get the current manager through a computed property "currentManager" and we implement copy-on-write in our "assignNewManager" function. The function is mutating since, if the manager object is uniquely referenced, we want to change it in place. Else, create a copy of the manager being passed in and assign it to our manager property. 

Encapsulated logic for creating a Restaurant struct

Encapsulated logic for creating a Restaurant struct

There's one more thing to talk about! What if we want to have two restaurants that share the same manager? This is a perfect representation of our situation - Restaurants are unique, and so should be value types. Managers can be the same person, and so CAN be references, but managers with the same name can ALSO be unique, and so CAN be value types (read - structs). Solution - it doesn't work here. We can create a new Restaurant object and pass in the Person we created as the manager, but our init method forces a copy to be made. We need to tinker with our init method and perhaps implement copy-on-write somewhere else in our code.

There are other edge cases to consider where our code WILL NOT WORK. I've been racking my brains for a few days here and there, trying to come up with an elegant solution that works. I'll keep working on it and I'll be sure to post here when I find a good solution.

That’s a ton to cover - we’ve reviewed lazy variables and working with constants, lazy sequences, reference and value types, equatable protocol implementation for value types, and discussed scenarios where you may WANT to have a reference type within a value type and how to write solid, defensive code in that situation. This was a long post, but definitely one of value. 

Being Lazy in Swift

Lazy Variables & Constants in Swift

Say you’re working on some application that has optional features - perhaps you have an image that CAN be resized if a thumbnail is required, but there is a chance that a thumbnail will never be viewed. Image scaling algorithms are computationally taxing - what if our user never ends up accessing a thumbnail? In that case, it doesn’t seem right to demand that a user ALWAYS wait for such an operation to be performed. Remember, allocation is slow, so defer until needed, and memory is limited, so don’t waste it. Use lazy instantiating when the initial value for a property isn’t known until after object initialization, when performing some intense algorithm or perhaps when calculating a large number (like pi to a large number of digits). 

In comes lazy loading - this is a technique used to defer instantiation of a variable or sequence (in swift) until it is actively needed. In swift, if you have a variable in a class that is part of the class’ designated initializer, the value must be computed since the compiler forces that every property within an initializer method be initialized. That may look something like this:

extension UIImage {

    fun resizedTo(size: CGSize) -> UIImage {

        // insert algorithm here    

    }

}

Class UserImage {

    static let smallSize = CGSize(width:36, height: 36)

    var thumbnail: UIImage

    var defaultImage: UIImage

    init (defaultImage: UIImage) {
        self.defaultImage = defaultImage

        self.thumbnail = defaultImage.resizedTo(UserImage.smallSize)

    }

}

What if we never need to use the thumbnail? We just performed an intensive operation for no reason at all - lazy variables take away this problem. In Objc we could leave the thumbnail out of the initializer and do a check for its value by making the thumbnail a computed property with set and get values. In the get, if the value is nil, THEN we would call our resizedTo method; in the set, simply give it a new value. We can immediately set a thumbnail value if we wish to, but if we end up attempting to access it without having initially set a value, it will call our resizing method. Thankfully, lazy instantiation with swift allows us to write less code while still achieving the same result.

The above can be achieved with just a single line of code- 

lazy var thumbnail: UIImage = self.defaultImage.resizedTo(UserImage.smallSize)

If we need to add more lines of code to compute our property, we can just use a closure - 

lazy var thumbnail: UIImage = { 

    let size = CGSize(

        width:min(UserImage.smallSize.width, self.defaultImage.size.width), 

        height: min(UserImage.smallSize.height, self.defaultImage.size.height)

    return self.defaultImage.resizedTo(size) 

} ()

The fact that a property is lazy means the value will only be computed when the entire class has been initialized, so it is safe to reference self within a computed lazy property. 

Note that let constants, if declared at global scope or as a type property (static let) are automatically lazy. Other than that, you can’t make them lazy. An interesting thing to note, as stated by Chris Lattner (designer and project leader of LLVM, Clang and Swift), is that “let” is about physical immutability, not logical immutability. Physical immutability means that once the bits are set in memory (when the let value is initialized), they can’t be changed.

Lazy Sequences in Swift

The SequenceType and CollectionType protocols in Swift have a computed property named lazy - it returns a LazySequence or LazyCollection, depending on which protocol we’re implementing. Lazy applies only to high-order functions in Swift (array.lazy.map(addOneFunction)). This code would only execute for values we actually need; if we printed (array[0], array[10], array[20]) and lazily mapped our function to those values, our program would only make three calculations, one for each value we try to access. If our array has 20,000 elements in it, we can use this to avoid calculating the values of each element after mapping a function to it, and limit our processing to just the values we want. 

Data Structures - Stacks & Queues

Today we're going to talk about stacks and queues - two abstract data types that just use data structures (arrays or linked lists) under the hood. For Objective-C support, head near the bottom of the post.

   LIFO (stack) vs FIFO (queue)

 

LIFO (stack) vs FIFO (queue)

To visualize a stack, think of a stack of dishes - you wash the first one, dry it, and place it on a table. Every other dish that is washed and dried is placed on top of this first one, creating a stack. Which dish comes off first when it's time to put them away? The conventional way to do it would be to pull the topmost item off of the stack and put it away. This explains the protocol followed by stacks which is LIFO, or last in, first out. The last item to go on the stack (dish at the top) is the first to come out.

Stacks are simple because you can generally perform three operations - push (add something to the top of the stack), pop (you can take something off the top of the stack), and peek (you can see what item is at the top of the stack without doing anything to it). They're sort of a wrapper around an array or linked list with limitations on what operations can be performed.

The term "stack" can also refer to a concept involving memory allocation - see the bottom of the post for information on this.

Queues can be visualized by thinking of a line in a store - the first person to get on line is the first person to be serviced at the counter. This data type follows the FIFO protocol - first in, last out. The first person in line is the last person to get out of the line. Often times, queues are used in concurrency situations - you can create queues to order processes and control what process runs when.

*In the context of this blog, we are talking about heaps and stacks as they exist within the realms of CPU memory. Keep in mind, however, that heap may also refer to the tree-based data structure, entirely separate from CPU memory. Heaps can be max or min heaps; there is a lot to learn about them, so we will not be covering them as data structures in this blog series.

OBJECTIVE-C SUPPORT:

Stacks are not explicitly supported in Objective-C, but they are simple enough to implement with, say, an NSMutableArray. You can add a category to the NSMutableArray class and add methods for pop, push and peek; it's that simple.

Queues are supported in Objective-C in a few ways: NSOperationQueue/NSOperations, and Grand Central Dispatch (GCD). GCD is lower-level than NSOperationQueue, and is written in C; however, it is important to understand that although it adds another layer of abstraction, NSOperationQueue uses GCD under the hood. You can also implement a queue yourself similarly to how we discussed doing so for a stack using a mutable array.

GCD provides queue implementation for use with many things, concurrency being one of them; it does so in the form of blocks (read up on them!). Both allow for priority queues, meaning you can insert processes to run into the middle of an existing queue by setting its priority.

Whether to use GCD vs NSOperationQueue is a point of contention, but it should be noted that GCD is much more light-weight than NSOperationQueue. It involves no thread-safety locking mechanisms, and is less taxing on the CPU. As such, some developers prefer to use it, as long as it suits their needs and they require none of the higher-level functionality of NSOperationQueue.

*In following convention, UI updates should happen on the main queue, and other processes should be executed on background queues. This ensures that there is minimal (or no) lag for the end-user of an application.

Left for review are hash tables, trees and a brief introduction to graphs. I will try and be brief with the last two so that we can move on to more iOS specific concepts.

 

Perhaps without realizing it, programmers use stacks every day in coding - the stack refers to a place in computer memory (along with the heap). The heap refers to memory on the CPU dedicated to items that are explicitly allocated memory in a program. It is up to the programmer to release the memory holding onto those objects when they're no longer needed. Heap memory is accessed using pointers (or some other reference), and as such, it is somewhat slow to read/write to. The stack, on the other hand, is an abstract data type used to "manage" a region of CPU memory that stores temporary variables which are created by functions. When we refer to "the stack", we are talking about the region of memory (versus just "a stack" which refers to some instance of the data type). Whenever we enter a function, all variables created are pushed onto a stack frame, which itself sits on the stack. A new stack frame exists for each function. When a function exits, the frame is popped off of the top; once a stack variable is freed, that piece of memory becomes available for other stack variables. FOR A LIST OF PROS AND CONS BETWEEN STACKS AND HEAPS, REFER TO THE BOTTOM OF THIS POST.

HEAP

PROS:

  • Variables can be globally accessed
  • There is no limit on memory size (limitations set by CPU)

CONS:

  • Relatively slow access
  • Programmer is in charge of allocating and deallocating variables
  • Efficiency regarding use of memory is not guaranteed.

STACK

PROS:

  • Fast access
  • Don't need to explicitly deallocate variables
  • Memory won't become fragmented from deallocation (efficient use of memory)

CONS:

  • Access to local variables (within function) only
  • There are limitations on size of the stack