Stanford CS193p: Developing Applications For iOS Spring 2016
Stanford CS193p: Developing Applications For iOS Spring 2016
CS193p
Spring 2016
Today
Memory Management for Reference Types
Controlling when things leave the heap
Closure Capture
Closures capture things into the heap too
Extensions
A simple, powerful, but easily overused code management syntax
Protocols
Last (but certainly not least important) typing mechanism in Swift
Delegation
An important use of protocols used throughout the iOS frameworks API
Scroll View
Scrolling around in and zooming in on big things on a small screen
CS193p
Spring 2016
Memory Management
Automatic Reference Counting
Reference types (classes) are stored in the heap.
How does the system know when to reclaim the memory for these from the heap?
It “counts references” to each of them and when there are zero references, they get tossed.
This is done automatically.
It is known as “Automatic Reference Counting” and it is NOT garbage collection.
Influencing ARC
You can influence ARC by how you declare a reference-type var with these keywords …
strong
weak
unowned
CS193p
Spring 2016
Memory Management
strong
strong is “normal” reference counting
As long as anyone, anywhere has a strong pointer to an instance, it will stay in the heap
weak
weak means “if no one else is interested in this, then neither am I, set me to nil in that case”
Because it has to be nil-able, weak only applies to Optional pointers to reference types
A weak pointer will NEVER keep an object in the heap
Great example: outlets (strongly held by the view hierarchy, so outlets can be weak)
unowned
unowned means “don’t reference count this; crash if I’m wrong”
This is very rarely used
Usually only to break memory cycles between objects (more on that in a moment)
CS193p
Spring 2016
Closures
Capturing
Closures are stored in the heap as well (i.e. they are reference types).
They can be put in Arrays, Dictionarys, etc. They are a first-class type in Swift.
What is more, they “capture” variables they use from the surrounding code into the heap too.
Those captured variables need to stay in the heap as long as the closure stays in the heap.
This can create a memory cycle …
CS193p
Spring 2016
Closures
Example
Imagine we added public API to allow a UnaryOperation to be added to the CalculatorBrain
func addUnaryOperation(symbol: String, operation: (Double) -> Double)
This method would do nothing more than add a UnaryOperation to our Dictionary of enum
Now let’s imagine a View Controller was to add the operation “red square root”.
This operation will do square root, but it will also turn the display red.
CS193p
Spring 2016
Closures
Example
Imagine we added public API to allow a UnaryOperation to be added to the CalculatorBrain
func addUnaryOperation(symbol: String, operation: (Double) -> Double)
This method would do nothing more than add a UnaryOperation to our Dictionary of enum
Now let’s imagine a View Controller was to add the operation “red square root”.
This operation will do square root, but it will also turn the display red.
CS193p
Spring 2016
Closures
Example
Imagine we added public API to allow a UnaryOperation to be added to the CalculatorBrain
func addUnaryOperation(symbol: String, operation: (Double) -> Double)
This method would do nothing more than add a UnaryOperation to our Dictionary of enum
Now let’s imagine a View Controller was to add the operation “red square root”.
This operation will do square root, but it will also turn the display red.
CS193p
Spring 2016
Closures
Example
Imagine we added public API to allow a UnaryOperation to be added to the CalculatorBrain
func addUnaryOperation(symbol: String, operation: (Double) -> Double)
This method would do nothing more than add a UnaryOperation to our Dictionary of enum
Now let’s imagine a View Controller was to add the operation “red square root”.
This operation will do square root, but it will also turn the display red.
CS193p
Spring 2016
Closures
Example
Imagine we added public API to allow a UnaryOperation to be added to the CalculatorBrain
func addUnaryOperation(symbol: String, operation: (Double) -> Double)
This method would do nothing more than add a UnaryOperation to our Dictionary of enum
Now let’s imagine a View Controller was to add the operation “red square root”.
This operation will do square root, but it will also turn the display red.
addUnaryOperation(“!√”) {
display.textColor = UIColor.redColor()
return sqrt($0)
}
CS193p
Spring 2016
Closures
Example
Imagine we added public API to allow a UnaryOperation to be added to the CalculatorBrain
func addUnaryOperation(symbol: String, operation: (Double) -> Double)
This method would do nothing more than add a UnaryOperation to our Dictionary of enum
Now let’s imagine a View Controller was to add the operation “red square root”.
This operation will do square root, but it will also turn the display red.
addUnaryOperation(“!√”) {
self. display.textColor = UIColor.redColor()
return sqrt($0)
}
Swift forces you to put self. here to remind you that self will get captured!
The Model and the Controller now point to each other through the closure.
And thus neither can ever leave the heap. This is called a memory cycle.
CS193p
Spring 2016
Closures
So how do we break this cycle?
CS193p
Spring 2016
Closures
So how do we break this cycle?
CS193p
Spring 2016
Closures
So how do we break this cycle?
CS193p
Spring 2016
Closures
So how do we break this cycle?
CS193p
Spring 2016
Closures
So how do we break this cycle?
CS193p
Spring 2016
Closures
So how do we break this cycle?
CS193p
Spring 2016
Closures
So how do we break this cycle?
CS193p
Spring 2016
Closures
So how do we break this cycle?
CS193p
Spring 2016
Demo
Red Square Root
Let’s do what we just talked about and see it in action in our Calculator
CS193p
Spring 2016
Extensions
Extending existing data structures
You can add methods/properties to a class/struct/enum (even if you don’t have the source).
For example, this adds a method contentViewController to UIViewController …
extension UIViewController {
var contentViewController: UIViewController {
if let navcon = self as? UINavigationController {
return navcon.visibleViewController
} else {
return self
}
}
}
… it can be used to clean up prepareForSegue code …
var destination: UIViewController? = segue.destinationViewController
if let navcon = destination as? UINavigationController {
destination = navcon.visibleViewController
}
if let myvc = destination as? MyVC { … }
CS193p
Spring 2016
Extensions
Extending existing data structures
You can add methods/properties to a class/struct/enum (even if you don’t have the source).
For example, this adds a method contentViewController to UIViewController …
extension UIViewController {
var contentViewController: UIViewController {
if let navcon = self as? UINavigationController {
return navcon.visibleViewController
} else {
return self
}
}
}
… it can be used to clean up prepareForSegue code …
Spring 2016
Extensions
Extending existing data structures
You can add methods/properties to a class/struct/enum (even if you don’t have the source).
For example, this adds a method contentViewController to UIViewController …
extension UIViewController {
var contentViewController: UIViewController {
if let navcon = self as? UINavigationController {
return navcon.visibleViewController
} else {
return self
}
}
}
Notice that when it refers to self, it means the thing it is extending (UIViewController).
CS193p
Spring 2016
Extensions
Extending existing data structures
You can add methods/properties to a class/struct/enum (even if you don’t have the source).
CS193p
Spring 2016
Protocols
Protocols are a way to express an API more concisely
Instead of forcing the caller of an API to pass a specific class, struct, or enum,
an API can let callers pass any class/struct/enum that the caller wants
but can require that they implement certain methods and/or properties that the API wants.
To specify which methods and properties the API wants, the API is expressed using a Protocol.
A Protocol is simply a collection of method and property declarations.
A Protocol is a TYPE
It can be used almost anywhere any other type is used: vars, function parameters, etc.
Spring 2016
Protocols
There are four aspects to a protocol
1. the protocol declaration (what properties and methods are in the protocol)
2. the declaration where a class, struct or enum claims that it implements a protocol
3. the code that implements the protocol in said class, struct or enum
4. optionally, an extension to the protocol which provides some default implementation
CS193p
Spring 2016
Protocols
Declaration of the protocol itself
protocol SomeProtocol : InheritedProtocol1, InheritedProtocol2 {
var someProperty: Int { get set }
func aMethod(arg1: Double, anotherArgument: String) -> SomeType
mutating func changeIt()
init(arg: Type)
}
CS193p
Spring 2016
Protocols
Declaration of the protocol itself
protocol SomeProtocol : InheritedProtocol1, InheritedProtocol2 {
var someProperty: Int { get set }
func aMethod(arg1: Double, anotherArgument: String) -> SomeType
mutating func changeIt()
init(arg: Type)
}
Anyone that implements SomeProtocol must also implement InheritedProtocol1 and 2
CS193p
Spring 2016
Protocols
Declaration of the protocol itself
protocol SomeProtocol : InheritedProtocol1, InheritedProtocol2 {
var someProperty: Int { get set }
func aMethod(arg1: Double, anotherArgument: String) -> SomeType
mutating func changeIt()
init(arg: Type)
}
Anyone that implements SomeProtocol must also implement InheritedProtocol1 and 2
You must specify whether a property is get only or both get and set
CS193p
Spring 2016
Protocols
Declaration of the protocol itself
protocol SomeProtocol : InheritedProtocol1, InheritedProtocol2 {
var someProperty: Int { get set }
func aMethod(arg1: Double, anotherArgument: String) -> SomeType
mutating func changeIt()
init(arg: Type)
}
Anyone that implements SomeProtocol must also implement InheritedProtocol1 and 2
You must specify whether a property is get only or both get and set
Any functions that are expected to mutate the receiver should be marked mutating
CS193p
Spring 2016
Protocols
Declaration of the protocol itself
protocol SomeProtocol : class, InheritedProtocol1, InheritedProtocol2 {
var someProperty: Int { get set }
func aMethod(arg1: Double, anotherArgument: String) -> SomeType
mutating func changeIt()
init(arg: Type)
}
Anyone that implements SomeProtocol must also implement InheritedProtocol1 and 2
You must specify whether a property is get only or both get and set
Any functions that are expected to mutate the receiver should be marked mutating
(unless you are going to restrict your protocol to class implementers only with class keyword)
CS193p
Spring 2016
Protocols
Declaration of the protocol itself
protocol SomeProtocol : InheritedProtocol1, InheritedProtocol2 {
var someProperty: Int { get set }
func aMethod(arg1: Double, anotherArgument: String) -> SomeType
mutating func changeIt()
init(arg: Type)
}
Anyone that implements SomeProtocol must also implement InheritedProtocol1 and 2
You must specify whether a property is get only or both get and set
Any functions that are expected to mutate the receiver should be marked mutating
(unless you are going to restrict your protocol to class implementers only with class keyword)
You can even specify that implementers must implement a given initializer
CS193p
Spring 2016
Protocols
How an implementer says “I implement that protocol”
class SomeClass : SuperclassOfSomeClass, SomeProtocol, AnotherProtocol {
// which must include all the properties and methods in SomeProtocol & AnotherProtocol
}
Claims of conformance to protocols are listed after the superclass for a class
CS193p
Spring 2016
Protocols
How an implementer says “I implement that protocol”
enum SomeEnum : SomeProtocol, AnotherProtocol {
// which must include all the properties and methods in SomeProtocol & AnotherProtocol
}
Claims of conformance to protocols are listed after the superclass for a class
Obviously, enums and structs would not have the superclass part
CS193p
Spring 2016
Protocols
How an implementer says “I implement that protocol”
struct SomeStruct : SomeProtocol, AnotherProtocol {
// which must include all the properties and methods in SomeProtocol & AnotherProtocol
}
Claims of conformance to protocols are listed after the superclass for a class
Obviously, enums and structs would not have the superclass part
CS193p
Spring 2016
Protocols
How an implementer says “I implement that protocol”
struct SomeStruct : SomeProtocol, AnotherProtocol {
// which must include all the properties and methods in SomeProtocol & AnotherProtocol
}
Claims of conformance to protocols are listed after the superclass for a class
Obviously, enums and structs would not have the superclass part
Any number of protocols can be implemented by a given class, struct or enum
CS193p
Spring 2016
Protocols
How an implementer says “I implement that protocol”
class SomeClass : SuperclassOfSomeClass, SomeProtocol, AnotherProtocol {
required init(…)
}
Claims of conformance to protocols are listed after the superclass for a class
Obviously, enums and structs would not have the superclass part
Any number of protocols can be implemented by a given class, struct or enum
In a class, inits must be marked required (or otherwise a subclass might not conform)
CS193p
Spring 2016
Protocols
How an implementer says “I implement that protocol”
extension Something : SomeProtocol {
}
Claims of conformance to protocols are listed after the superclass for a class
Obviously, enums and structs would not have the superclass part
Any number of protocols can be implemented by a given class, struct or enum
In a class, inits must be marked required (or otherwise a subclass might not conform)
You are allowed to add protocol conformance via an extension
CS193p
Spring 2016
Protocols
Using protocols like the type that they are!
protocol Moveable {
mutating func moveTo(p: CGPoint)
}
class Car : Moveable {
func moveTo(p: CGPoint) { … }
func changeOil()
}
struct Shape : Moveable {
mutating func moveTo(p: CGPoint) { … }
func draw()
}
CS193p
Spring 2016
Protocols
Using protocols like the type that they are!
protocol Moveable {
mutating func moveTo(p: CGPoint)
}
class Car : Moveable {
func moveTo(p: CGPoint) { … }
func changeOil()
}
struct Shape : Moveable {
mutating func moveTo(p: CGPoint) { … }
func draw()
}
CS193p
Spring 2016
Protocols
Using protocols like the type that they are!
protocol Moveable { var thingToMove: Moveable = prius
mutating func moveTo(p: CGPoint) thingToMove.moveTo(…)
} thingToMove.changeOil()
class Car : Moveable { thingToMove = square
func moveTo(p: CGPoint) { … } let thingsToMove: [Moveable] = [prius, square]
func changeOil()
} func slide(slider: Moveable) {
struct Shape : Moveable { let positionToSlideTo = …
mutating func moveTo(p: CGPoint) { … } slider.moveTo(positionToSlideTo)
func draw() }
} slide(prius)
slide(square)
let prius: Car = Car() func slipAndSlide(x: protocol<Slippery,Moveable>)
let square: Shape = Shape() slipAndSlide(prius)
CS193p
Spring 2016
Delegation
A very important use of protocols
It’s a way to implement “blind communication” between a View and its Controller
should
will did
Controller
data
count
at
da
de
ta
le
g
so
at
u
e
rc
e
View
CS193p
Spring 2016
Delegation
A very important use of protocols
It’s a way to implement “blind communication” between a View and its Controller
Spring 2016
Delegation
Example
UIScrollView (which we’ll talk about in a moment) has a delegate property …
weak var delegate: UIScrollViewDelegate?
The UIScrollViewDelegate protocol looks like this …
@objc protocol UIScrollViewDelegate {
optional func scrollViewDidScroll(scrollView: UIScrollView)
optional func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView
… and many more …
}
CS193p
Spring 2016
UIScrollView
CS193p
Spring 2016
Adding subviews to a normal UIView ...
logo.frame = CGRect(x: 300, y: 50, width: 120, height: 180)
view.addSubview(logo)
CS193p
Spring 2016
Adding subviews to a UIScrollView ...
scrollView.contentSize = CGSize(width: 3000, height: 2000)
logo.frame = CGRect(x: 2700, y: 50, width: 120, height: 180)
scrollView.addSubview(logo)
CS193p
Spring 2016
Adding subviews to a UIScrollView ...
scrollView.contentSize = CGSize(width: 3000, height: 2000)
aerial.frame = CGRect(x: 150, y: 200, width: 2500, height: 1600)
scrollView.addSubview(aerial)
CS193p
Spring 2016
Adding subviews to a UIScrollView ...
scrollView.contentSize = CGSize(width: 3000, height: 2000)
aerial.frame = CGRect(x: 150, y: 200, width: 2500, height: 1600)
scrollView.addSubview(aerial)
CS193p
Spring 2016
Scrolling in a UIScrollView ...
CS193p
Spring 2016
Scrolling in a UIScrollView ...
CS193p
Spring 2016
Scrolling in a UIScrollView ...
CS193p
Spring 2016
Scrolling in a UIScrollView ...
CS193p
Spring 2016
Positioning subviews in a UIScrollView ...
aerial.frame = CGRect(x: 0, y: 0, width: 2500, height: 1600)
CS193p
Spring 2016
Positioning subviews in a UIScrollView ...
aerial.frame = CGRect(x: 0, y: 0, width: 2500, height: 1600)
logo.frame = CGRect(x: 2300, y: 50, width: 120, height: 180)
CS193p
Spring 2016
Positioning subviews in a UIScrollView ...
aerial.frame = CGRect(x: 0, y: 0, width: 2500, height: 1600)
logo.frame = CGRect(x: 2300, y: 50, width: 120, height: 180)
scrollView.contentSize = CGSize(width: 2500, height: 1600)
CS193p
Spring 2016
That’s it!
CS193p
Spring 2016
That’s it!
CS193p
Spring 2016
That’s it!
CS193p
Spring 2016
That’s it!
CS193p
Spring 2016
That’s it!
CS193p
Spring 2016
Where in the content is the scroll view currently positioned?
let upperLeftOfVisible: CGPoint = scrollView.contentOffset
In the content area’s coordinate system.
contentOffset.y
contentOffset.x
CS193p
Spring 2016
What area in a subview is currently visible?
let visibleRect: CGRect = aerial.convertRect(scrollView.bounds, fromView: scrollView)
Why the convertRect? Because the scrollView’s bounds are in the scrollView’s coordinate system.
And there might be zooming going on inside the scrollView too …
CS193p
Spring 2016
UIScrollView
How do you create one?
Just like any other UIView. Drag out in a storyboard or use UIScrollView(frame:).
Or select a UIView in your storyboard and choose “Embed In -> Scroll View” from Editor menu.
CS193p
Spring 2016
UIScrollView
Scrolling programmatically
func scrollRectToVisible(CGRect, animated: Bool)
CS193p
Spring 2016
UIScrollView
Zooming
All UIView’s have a property (transform) which is an affine transform (translate, scale, rotate).
Scroll view simply modifies this transform when you zoom.
Zooming is also going to affect the scroll view’s contentSize and contentOffset.
Zooming programatically
var zoomScale: CGFloat
func setZoomScale(CGFloat, animated: Bool)
func zoomToRect(CGRect, animated: Bool)
CS193p
Spring 2016
scrollView.zoomScale = 1.2
CS193p
Spring 2016
scrollView.zoomScale = 1.0
CS193p
Spring 2016
scrollView.zoomScale = 1.2
CS193p
Spring 2016
zoomToRect(CGRect, animated: Bool)
CS193p
Spring 2016
zoomToRect(CGRect, animated: Bool)
CS193p
Spring 2016
zoomToRect(CGRect, animated: Bool)
CS193p
Spring 2016
zoomToRect(CGRect, animated: Bool)
CS193p
Spring 2016
UIScrollView
Lots and lots of delegate methods!
The scroll view will keep you up to date with what’s going on.
CS193p
Spring 2016