domingo, 23 de septiembre de 2018

iOS: Resolving strong reference cycles between class instances

Resolving strong reference cycles between class instances

Swift provides two ways to resolve strong reference cycles when you work with properties of class type: weak references and unowned references.

Weak and unowned references enable one instance in a reference cycle to refer to the other instance without keeping a strong hold on it. The instances can then refer to each other without creating a strong reference cycle.


Use a weak reference when the other instance has a shorter lifetime. In contrast, use an unowned reference when the other instance has the same lifetime or a longer lifetime.

Weak References

weak reference is a reference that does not keep a strong hold on the instance it refers to, and so does not stop ARC from disposing of the referenced instance. You indicate a weak reference by placing the weak keyword before a property or variable declaration.
ARC automatically sets a weak reference to nil when the instance that it refers to is deallocated. 
NOTE
Property observers aren’t called when ARC sets a weak reference to nil.

Unowned References

You indicate an unowned reference by placing the unowned keyword before a property or variable declaration.
An unowned reference is expected to always have a value. As a result, ARC never sets an unowned reference’s value to nil, which means that unowned references are defined using nonoptional types.
IMPORTANT
Use an unowned reference only when you are sure that the reference always refers to an instance that has not been deallocated.
If you try to access the value of an unowned reference after that instance has been deallocated, you’ll get a runtime error.
NOTE
The examples above show how to use safe unowned references. Swift also provides unsafeunowned references for cases where you need to disable runtime safety checks—for example, for performance reasons. As with all unsafe operations, you take on the responsibility for checking that code for safety.
You indicate an unsafe unowned reference by writing unowned(unsafe). If you try to access an unsafe unowned reference after the instance that it refers to is deallocated, your program will try to access the memory location where the instance used to be, which is an unsafe operation.

Unowned References and Implicitly Unwrapped Optional Properties

The Person and Apartment example shows a situation where two properties, both of which are allowed to be nil, have the potential to cause a strong reference cycle. This scenario is best resolved with a weak reference.
The Customer and CreditCard example shows a situation where one property that is allowed to be nil and another property that cannot be nil have the potential to cause a strong reference cycle. This scenario is best resolved with an unowned reference.
However, there is a third scenario, in which both properties should always have a value, and neither property should ever be nil once initialization is complete. In this scenario, it’s useful to combine an unowned property on one class with an implicitly unwrapped optional property on the other class.
This enables both properties to be accessed directly (without optional unwrapping) once initialization is complete, while still avoiding a reference cycle. This section shows you how to set up such a relationship.
The example below defines two classes, Country and City, each of which stores an instance of the other class as a property. In this data model, every country must always have a capital city, and every city must always belong to a country. To represent this, the Country class has a an implicitly unwrapped optional capitalCity property, and the City class has a unowned country property.

Strong Reference Cycles for Closures

A strong reference cycle can also occur if you assign a closure to a property of a class instance, and the body of that closure captures the instance. This capture might occur because the closure’s body accesses a property of the instance, such as self.someProperty, or because the closure calls a method on the instance, such as self.someMethod(). In either case, these accesses cause the closure to “capture” self, creating a strong reference cycle.
This strong reference cycle occurs because closures, like classes, are reference types. When you assign a closure to a property, you are assigning a reference to that closure. In essence, it’s the same problem as above.
Swift provides an elegant solution to this problem, known as a closure capture list.
The example below shows how you can create a strong reference cycle when using a closure that references self. This example defines a class called HTMLElement, which provides a simple model for an individual element within an HTML document:
  1. class HTMLElement {
  2. let name: String
  3. let text: String?
  4. lazy var asHTML: () -> String = {
  5. if let text = self.text {
  6. return "<\(self.name)>\(text)</\(self.name)>"
  7. } else {
  8. return "<\(self.name) />"
  9. }
  10. }
  11. init(name: String, text: String? = nil) {
  12. self.name = name
  13. self.text = text
  14. }
  15. deinit {
  16. print("\(name) is being deinitialized")
  17. }
  18. }

Resolving Strong Reference Cycles for Closures

You resolve a strong reference cycle between a closure and a class instance by defining a capture list as part of the closure’s definition. A capture list defines the rules to use when capturing one or more reference types within the closure’s body. As with strong reference cycles between two class instances, you declare each captured reference to be a weak or unowned reference rather than a strong reference. The appropriate choice of weak or unowned depends on the relationships between the different parts of your code.
Place the capture list before a closure’s parameter list and return type if they are provided:
  1. lazy var someClosure: (Int, String) -> String = {
  2. [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
  3. // closure body goes here
  4. }
If a closure does not specify a parameter list or return type because they can be inferred from context, place the capture list at the very start of the closure, followed by the in keyword:
  1. lazy var someClosure: () -> String = {
  2. [unowned self, weak delegate = self.delegate!] in
  3. // closure body goes here
  4. }

iOS: Interview questions and answers, and exercises

Interview questions and topics

  1. ARC and Retain cycles
  2. Communication patterns
  3. Viewcontroller lifecycle
  4. Favorite framework and why
  5. Classes vs Structs
  6. Filter, map, reduce in Collections
  7. Testing
  8. 3rd party libraries
  9. Doing an exercise with Gesture Recognizers
  10. Networking, doing a request
  11. Debugging, look at code and find bugs.
  12. Take home projects

ARC and Retain cycles

ARC tracks how many properties, constants, and variables are currently referring to each class instance. ARC will not deallocate an instance as long as at least one active reference to that instance still exists.
Whenever you assign a class instance to a property, constant, or variable, that property, constant, or variable makes a strong reference to the instance. The reference is called a strong reference when it does not allow it to be deallocated for as long as that reference remains.
Resolving retain cycles.

Communication patterns

Notifications

Its a one to many communication pattern.

Observer

Its a one to many communication pattern.

Delegate

Delegation is a simple and powerful pattern in which one object in a program acts on behalf of, or in coordination with, another object. The delegating object keeps a reference to the other object—the delegate—and at the appropriate time sends a message to it. The message informs the delegate (e.g.: UITableViewDelegate) of an event that the delegating object (e.g.: UITableView) is about to handle or has just handled. The delegate may respond to the message by updating the appearance or state of itself or other objects in the application, and in some cases it can return a value that affects how an impending event is handled. The main value of delegation is that it allows you to easily customize the behavior of several objects in one central object.

Its a one to one communication pattern.

Protocol

Its a one to one communication pattern.

ViewController lifecycle

loadView
loadViewIfNeeded
viewDidLoad
viewWillAppear
viewWillLayoutSubviews
viewdidLayoutSubviews
viewDidAppear
viewWillDisappear
viewDidDisappear

Favorite framework

CoreLocation, MapKit, UIKit, etc

Classes vs Structs

Structures and classes in Swift have many things in common. Both can:
  • Define properties to store values
  • Define methods to provide functionality
  • Define subscripts to provide access to their values using subscript syntax
  • Define initializers to set up their initial state
  • Be extended to expand their functionality beyond a default implementation
  • Conform to protocols to provide standard functionality of a certain kind
Classes have additional capabilities that structures don’t have:
  • Inheritance enables one class to inherit the characteristics of another.
  • Type casting enables you to check and interpret the type of a class instance at runtime.
  • Deinitializers enable an instance of a class to free up any resources it has assigned.
  • Reference counting allows more than one reference to a class instance.
value type is a type whose value is copied when it’s assigned to a variable or constant, or when it’s passed to a function.
In fact, all of the basic types in Swift—integers, floating-point numbers, Booleans, strings, arrays and dictionaries—are value types, and are implemented as structures behind the scenes.
All structures and enumerations are value types in Swift.
Unlike value types, reference types are not copied when they are assigned to a variable or constant, or when they are passed to a function. Rather than a copy, a reference to the same existing instance is used.
Classes are reference types.

Filter, map, reduce in Collections

filter
Returns an array containing, in order, the elements of the sequence that satisfy the given predicate.




let cast = ["Vivien", "Marlon", "Kim", "Karl"]
let shortNames = cast.filter { $0.count < 5 }
print(shortNames)
// Prints "["Kim", "Karl"]"

map
Returns an array containing the results of mapping the given closure over the sequence’s elements.




let cast = ["Vivien", "Marlon", "Kim", "Karl"]
let lowercaseNames = cast.map { $0.lowercased() }
// 'lowercaseNames' == ["vivien", "marlon", "kim", "karl"]
let letterCounts = cast.map { $0.count }
// 'letterCounts' == [6, 6, 3, 4]

flatMap
Returns an array containing the concatenated results of calling the given transformation with each element of this sequence.




let numbers = [1, 2, 3, 4]

let mapped = numbers.map { Array(repeating: $0, count: $0) }
// [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]]

let flatMapped = numbers.flatMap { Array(repeating: $0, count: $0) }
// [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]

compactMap
Returns an array containing the non-nil results of calling the given transformation with each element of this sequence.








let possibleNumbers = ["1", "2", "three", "///4///", "5"]

let mapped: [Int?] = possibleNumbers.map { str in Int(str) }
// [1, 2, nil, nil, 5]

let compactMapped: [Int] = possibleNumbers.compactMap { str in Int(str) }
// [1, 2, 5]

reduce
Returns the result of combining the elements of the sequence using the given closure.




let numbers = [1, 2, 3, 4]
let numberSum = numbers.reduce(0, { x, y in
    x + y
})
// numberSum == 10

Testing

Creating a Unit Test Target



  • Don't forget to add @testable import <Your Target>
  • Use XCTAsset to test models
  • Use XCTTestExpectation to test asynchronous operations

// Asynchronous test: success fast, failure slow
func testValidCallToiTunesGetsHTTPStatusCode200() {
  // given
  let url = URL(string: "https://itunes.apple.com/search?media=music&entity=song&term=abba")
  let promise = expectation(description: "Status code: 200")
  
  // when
  let dataTask = sessionUnderTest.dataTask(with: url!) { data, response, error in
    // then
    if let error = error {
      XCTFail("Error: \(error.localizedDescription)")
      return
    } else if let statusCode = (response as? HTTPURLResponse)?.statusCode {
      if statusCode == 200 {
        promise.fulfill()
      } else {
        XCTFail("Status code: \(statusCode)")
      }
    }
  }
  dataTask.resume()
  waitForExpectations(timeout: 5, handler: nil)
}

Code Coverage




3rd party libraries

Cocoapods, Carthage, etc

Doing an exercise with Gesture Recognizers

Moving a view around with a gesture


class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let rectView = UIView()
        rectView.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
        rectView.backgroundColor = UIColor.green
        
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
        rectView.addGestureRecognizer(panGesture)
        
        let rotationGestureRecognizer = UIRotationGestureRecognizer(target: self, action: #selector(handleRotation(_:)))
        rectView.addGestureRecognizer(rotationGestureRecognizer)
        
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
        rectView.addGestureRecognizer(tapGestureRecognizer)
        
        view.addSubview(rectView)
    }

    @objc func handlePan(_ panGesture: UIPanGestureRecognizer) {
        let translation = panGesture.translation(in: view)
        let rectView = panGesture.view
        rectView?.frame = rectView?.frame.offsetBy(dx: translation.x, dy: translation.y) ?? CGRect.zero
        panGesture.setTranslation(CGPoint.zero, in: rectView)
    }
    
    @objc func handleRotation(_ rotationGesture: UIRotationGestureRecognizer) {
        let rotation = rotationGesture.rotation
        let rectView = rotationGesture.view
        rectView?.transform = CGAffineTransform.identity.rotated(by: rotation)
    }
    
    @objc func handleTap(_ tapGesture: UITapGestureRecognizer) {
        let rectView = tapGesture.view
        
        UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: {
            rectView?.center.y -= 100 // If you use 'frame' rather than 'center' here if you 
            // rotate the rect first, if will get crashed
            
            UIView.animate(withDuration: 0.5, delay: 1, options: .curveEaseOut, animations: {
                rectView?.center.y += 100
            }) { (completed) in
                
            }
        }) { (completed) in
            
        }
    }
}

zoom


class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let rectView = UIView()
        rectView.frame = CGRect()
        rectView.backgroundColor = UIColor.green
        rectView.translatesAutoresizingMaskIntoConstraints = false
        
        let zoomGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(handleZoom(_:)))
        rectView.addGestureRecognizer(zoomGestureRecognizer)
        
        view.addSubview(rectView)
        
        NSLayoutConstraint.activate([
            rectView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0),
            rectView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0),
            rectView.widthAnchor.constraint(equalToConstant: 100),
            rectView.heightAnchor.constraint(equalToConstant: 100)
            ])
    }

    @objc func handleZoom(_ zoomGesture: UIPinchGestureRecognizer) {
        let scale = zoomGesture.scale
        let rectView = zoomGesture.view
        rectView?.transform = CGAffineTransform.identity.scaledBy(x: scale, y: scale)
    }
}

Networking, doing a request


class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // GET
        let getUrl = URL(string: "https://www.apple.com/")!
        
        let session = URLSession.shared
        let getTask = session.dataTask(with: getUrl) { (data, response, error) in
            
            if let error = error {
                print("GET Error: ", error)
                return
            }
            
            if let httpResponse = response as? HTTPURLResponse {
                if httpResponse.statusCode == 200 {
                    print("GET Success!")
                } else {
                    print("GET Status Code: \(httpResponse.statusCode)")
                }
            }
        }
        getTask.resume()
        
        // POST
        let postUrl = URL(string: "https://httpbin.org/post")!
        
        let dictionary = ["someParameter": "value"]
        
        let request = NSMutableURLRequest(url: postUrl)
        request.httpMethod = "POST"
        request.httpBody = try? JSONSerialization.data(withJSONObject: dictionary, options: .prettyPrinted)
        
        let postTask = session.dataTask(with: request as URLRequest) { (data, response, error) in
            
            if let error = error {
                print("POST Error: ", error)
                return
            }
            
            if let httpResponse = response as? HTTPURLResponse {
                if httpResponse.statusCode == 200 {
                    print("POST Success!")
                } else {
                    print("POST Status Code: \(httpResponse.statusCode)")
                }
            }
        }
        postTask.resume()
    }
}

Debugging, look at code and find bugs

For instance, optional being unwrapped inappropriately, raise conditions in network calls, retain cycles, memory leaks, ui not updated on the main thread.

Take home projects

Networking, tableview, building a ui to a spec pixel perfect, persistance, mapkit, animations, etc. Probably will have to build the UI entirely in code.