51. UnsafePointer

By design, Swift is a very secure language, and with this high level of security, Swift has correspondingly lost some of its flexibility.

To work with the C API, Swift defines a set of methods for accessing and converting C Pointers, UnsafePointer, and a number of variations.

If you take a memory address as an argument using the C API, or return a memory address, Swift converts it to an UnsafePointer.

// If an API is in C:
void method(const int *num) {
    printf("%d".*num);
}
// The corresponding Swift method:
func method(_ num: UnsafePointer<CInt>) {
    print(num.pointee)
}
Copy the code

UnsafePointer is a pointer conversion in Swift. For other basic C types, the Swift types follow the same naming rules: add a C and capitalize the first letter: for example, int, bool, and char are CInt, CBool, and CChar, respectively. In the preceding C method, which takes an int pointer, the conversion to Swift corresponds to a CInt UnsafePointer. The original C API indicated that the input num pointer was const, so in Swift we correspond to UnsafePointer, which is an immutable version. UnsafeMutablePointer (); UnsafeMutablePointer ();

C API Swift API
const Type * UnsafePointer
Type * UnsafeMutablePointer

In C, the value of a pointer is *, while in Swift the memory attribute can be used to read the contents of the corresponding memory. Methods are called by passing in the address of the pointer, and are preceded by an ampersand (&). Versions of C and Swift differ only when declaring variables:

// C
int a = 123;
method(&a); / / output 123
// Swift
var a: CInt = 123 method(&a) / / output 123
Copy the code

How do I convert the contents of a pointer to the actual value? For example, if for some reason it is necessary to directly use CFArray’s methods to retrieve the elements of an array, this method is used:

 func CFArrayGetValueAtIndex(theArray: CFArray! .idx: CFIndex)
                                            -> UnsafePointer<Void>
Copy the code

Since CFArray can hold any object, the return is a pointer to any object, equivalent to void star in C. This is clearly not something you want. Swift provides a cast method, unsafeBitCast.

How to use an API like this to force bitwise conversion of a pointer to an object of the desired type:

let arr = NSArray(object: "meow")
let str = unsafeBitCast(CFArrayGetValueAtIndex(arr, 0), to: CFString.self)
// str = "meow"
Copy the code

UnsafeBitCast converts the contents of the first argument to the type of the second argument. This is where UnsafePointer is unsafe, because you don’t have to comply with the cast checks, and you have the opportunity to manipulate memory directly at the pointer level.

52. C Pointer memory management

Another reason C Pointers are called unsafe in Swift is that they cannot be automatically memory-managed. Working with a pointer to the Unsafe class requires manually requesting and freeing memory, as in the pre-Arc era, to ensure that the program doesn’t leak or crash by accessing the freed memory.

If you want to declare, initialize, and then use a pointer, the complete approach is to use allocate and initialize to create.

// Error code
class MyClass {
    var a = 1
    deinit {
        print("deinit")}}var pointer: UnsafeMutablePointer<MyClass>!
pointer = UnsafeMutablePointer<MyClass>.allocate(capacity: 1)
pointer.initialize(to: MyClass())
print(pointer.pointee.a)  / / 1
pointer = nil
Copy the code

The pointer is finally set to nil, but because UnsafeMutablePointer does not automatically manage memory, the memory that the pointer points to is not freed or reclaimed. The correct approach is to add deinitialize and deallocate to pointer, which will free the memory object to which the pointer points and the pointer itself, respectively:

var pointer: UnsafeMutablePointer<MyClass>!
pointer = UnsafeMutablePointer<MyClass>.allocate(capacity: 1)
pointer.initialize(to: MyClass())
print(pointer.pointee.a)
pointer.deinitialize()
pointer.deallocate(capacity: 1)
pointer = nil
// Output: // 1
// deinit
Copy the code

It crashes if you call pointer after deallocate or call deallocate again.

One of the basic principles to follow when handling memory management of such Pointers manually is that the one who created them is the one who released them. Deallocate and deinitialize should be paired with allocate and Initialize. If you do not create a pointer, you generally do not need to release it.

A pointer can also be allocated with malloc or calloc, in which case free should be used instead of deallocate.

53. COpaquePointer and C Convention

There is a class of Pointers in C where you can’t find the specific definition in the header file, only the name of the type, and all the implementation details are hidden. This type of Pointer is called an Opaque Pointer in C or C++.

The type corresponding to this type of opaque pointer in Swift is COpaquePointer, which is used to represent C Pointers that cannot be type described in Swift. Pointers that can be typed represent memory that can be described by a type in a Swift, and therefore use the more accurate UnsafePointer. As for the other Pointers that Swift cannot express, they are written as COpaquePointer as a supplement.

During the early beta of Swift, there were a number of apis that returned or received the COpaquePointer type. However, with the gradual improvement of Swift, the COpaquePointer in most apis involving Pointers was correctly classified into the appropriate Unsafe pointer, so it is now possible to rarely see COpaquePointer in development. Probably the most common use case is the conversion between COpaquePointer and a particular Unsafe, where you can use the initializer methods of each type to force a pointer conversion from one type to the other.

COpaquePointer acts as a middleman for pointer conversions in Swift and can be converted between different pointer types through this type. Of course, these conversions are not safe.

Another important form of pointer, which is not uncommon in C, is a pointer to a function, which is a block of memory that holds the actual location of a function. Starting with Swift 2.0, Pointers like this can be converted to closures, but unlike other normal closures, you need to add the @convention annotation to them.

// c
int cFunction(int (callback)(int x, int y)) {
    return callback(1.2);
}

// swift
let callback: @convention(c) (Int32.Int32) - >Int32 = {
    (x, y) -> Int32 in
    return x + y
}
let result = cFunction(callback) print(result)
/ / output:
/ / 3

// If there is no ambiguity, we can omit this annotation and pass it directly to C as a Swift closure:
let result = cFunction {
    (x, y) -> Int32 in
    return x + y
} print(result) / / output:
/ / 3
Copy the code

54. GCD and delay calls

GCD is a very convenient way to use multiple threads. By using GCD, you can do flexible multithreaded programming while keeping the syntax as simple as possible.

The GCD API is seamless to use in Swift, and thanks to the closure feature, it’s much easier to use than it was in Objective-C.

// Create the target queue
let workingQueue = DispatchQueue(label: "my_queue")
// Assign to the newly created queue. GCD will be responsible for thread scheduling workingQueue.async {
    // Execute asynchronously in the workingQueue
    print("Work hard")
    Thread.sleep(forTimeInterval: 2) // Simulate the execution time of two seconds
        DispatchQueue.main.async {
            // Return to the main thread to update the UI
            print("Finish work, update UI.")}}Copy the code

Because UIKit can only work on the main thread, and if you do heavy work on the main thread, you’re going to cause your app to get stuck. Processing that is heavy (like filtering images) or takes a long time (like downloading images from the network) should be done in a background thread so that the UI is still interactive from the user’s point of view and doesn’t get stuck. After the work is done, if you need to update the UI, you must go back to the main thread.

The GCD has a nice delayed call that can be used to write nice methods, and that’s asyncAfter. The simplest way to use it looks like this:

let time: TimeInterval = 2.0
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + time) {
    print("Output after 2 seconds")}Copy the code

GCD has evolved in iOS 8, and it is now possible to cancel a block that is awaiting execution by encapsulating a closure into a DispatchWorkItem object and then sending a cancel to it. Canceling a task feature, which was previously the exclusive domain of NSOperation, can now be used to do the same with GCD. Instead of doing this, the delay call is cancelled by capturing a cancel identifier variable.

`

import Foundation
typealias Task = (_ cancel : Bool) - >Void
func delay(_ time: TimeInterval.task: @escaping() - > ()) ->  Task? {
    func dispatch_later(block: @escaping() - > ()) {
        let t = DispatchTime.now() + time
        DispatchQueue.main.asyncAfter(deadline: t, execute: block)
    }
    var closure: (()->Void)? = task
    var result: Task?
    let delayedClosure: Task = {
        cancel in
        if let internalClosure = closure {
            if (cancel = = false) {
                DispatchQueue.main.async(execute: internalClosure)
            }
        }
        closure = nil
        result = nil
}
    result = delayedClosure
    dispatch_later {
        if let delayedClosure = result {
            delayedClosure(false)}}return result;
}
func cancel(_ task: Task?). {
    task?(true)}let task = delay(5) { print("111")}/ / cancel
cancel(task)
Copy the code

55. Obtain the object type

In Swift, there’s no class() method to get the type, whether it’s a pure Swift class or a subclass of NSObject. For a subclass of NSObject, since nothing much has changed about how the class information is stored, we can turn to the Objective-C runtime to take the class and transform it exactly the way it was:

let date = NSDate(a)let name: AnyClass! = object_getClass(date) print(name)
/ / output:
// __NSDate
Copy the code

Object_getClass is a method defined in OC runtime that accepts AnyObject! And return its type AnyClass! Note the exclamation mark, you can type nil and expect it to return nil. There’s actually a nicer way to write this call in Swift to get the actual type of an object that’s an NSObject or one of its subclasses, which is type(of:). The above code, translated in a more “Swift” language, would look like this:

let date = NSDate(a)let name = type(of: date) print(name)
/ / output:
// __NSDate
Copy the code

This is also possible for native types of Swift.

56. Introspection

Asking an object to determine whether it belongs to a class is called introspection. In Objective-C, it’s often necessary to ask an object if it belongs to a class because of the existence of Pointers such as ids that can point to any object. Common methods fall into the following two categories

[obj1 isKindOfClass:[ClassA class]]. / / determineobj1Whether it isClassAOr an instance object of its subclass [obj2 isMemberOfClass: [ClassB class]]. If and only ifobj2The type ofClassBReturns true whenCopy the code

These two methods are methods of NSObject, so in Swift if you’re writing subclasses of NSObject, there’s no problem using these methods directly.

class ClassA: NSObject {}class ClassB: ClassA {}let obj1: NSObject = ClassB(a)let obj2: NSObject = ClassB()
obj1.isKind(of: ClassA.self)    // true
obj2.isMember(of: ClassA.self)  // false
Copy the code

In Swift, the preference should be to choose native Swift types that are not NSObject subclasses whenever possible for performance reasons. How do you type classes that are not NSObject?

With generics support, Swift is complete in its inferring and recording of types. Therefore, in most cases, the Swift type used should be determined at compile time. If you constantly have to check and determine what class AnyObject is in your code, it almost always means that your code is not designed correctly (or that you are writing code that is full of “tricks”.

class ClassA {}class ClassB: ClassA {}let obj1: AnyObject = ClassB(a)let obj2: AnyObject = ClassB()
obj1.isKind(of: ClassA.self)    // true
obj2.isMember(of: ClassA.self)  // false
Copy the code

The most common use of AnyObject in Swift is probably in the old Cocoa apis that return ids.

For quick typing, Swift provides a neat way to write: For an indeterminate type, it is now possible to use is. Is is the functional equivalent of isKindOfClass, which checks whether an object belongs to a type or its subtypes. The difference between “is” and “IS” is mainly on the bright side. First, it can be used not only for class types, but also for other types of Swift such as struct or enum. This is how it works:

class ClassA {}class ClassB: ClassA {}let obj: AnyObject = ClassB(a)if (obj is ClassA) { print("Belongs to ClassA")}if (obj is ClassB) { print("Belongs to the ClassB")}Copy the code

57. The KeyPath and KVO

KVO (key-value Observing) is recognized as one of the most powerful features in Cocoa. Unlike property observation, the purpose of KVO is not to provide a hook method for a property of the current class, but to be used by different instances listening to a property (strictly a keypath) of the current class. The other instance can act as a subscriber who is notified when the property being listened on changes.

This is a powerful property, and KVO implements a lot of loosely-coupled structure, making the code more flexible and powerful: things like automatically updating UI bindings by listening for model values are mostly done on KVO.

KVO is also available in Swift, and with KeyPath, Apple provides a nice new API. But KVO is limited to subclasses of NSObject, which is understandable, because KVO is based on KVC (key-value Coding) and dynamic dispatching, which are all objective-C runtime concepts. In addition, since Swift disables dynamic dispatch by default for efficiency, the additional work required to implement KVO with Swift is to mark the objects you want to observe as dynamic and @objc.

class MyClass: NSObject {
    @objc dynamic var date = Date()}private var myContext = 0
class Class: NSObject {
    var myObject: MyClass!
    override init(a) {
        super.init()
        myObject = MyClass(a)print("Initialize MyClass with the current date:\(myObject.date)") myObject.addObserver(self,
            forKeyPath: "date",
            options: .new,
            context: &myContext)
        delay(3) {
            self.myObject.date = Date()}}override func observeValue(forKeyPath keyPath: String? .of object: Any?.change: [NSKeyValueChangeKey : Any]?.context: UnsafeMutableRawPointer?).
    {
        if let change = change, context = = &myContext {
            if let newDate = change[.newKey] as? Date { 
                print("MyClass date changed\(newDate)")}}}}let obj = Class(a)// Initialize Class. Current date: 2017-08-18 02:50:24 +0000
// Class date changed 2017-08-18 02:50:25 +0000
Copy the code

The new value is taken from the dictionary. It is possible to verify that the dictionary has an Optional Binding.

In Swift 4, Apple introduced a new expression for KeyPath. Now, for the variable bar: bar in type Foo, the corresponding KeyPath can be written as \ foo.bar. In this representation, the KeyPath will be generically typed with type information, such as KeyPath<Foo, Bar> on the KeyPath. With this information, Apple added a block-based KVO API to NSObject. The above example can be rewritten as:

class AnotherClass: NSObject {
    var myObject: MyClass!
    var observation: NSKeyValueObservation?
    override init(a) {
        super.init()
        myObject = MyClass(a)print("Initialize AnotherClass with the current date:\(myObject.date)"
        observation = myObject.observe(\MyClass.date, options: [.new]) { (_, change) in
            if let newDate = change.newValue {
                print("AnotherClass date changed\(newDate)") 
            }
        }
        delay(1) { self.myObject.date = Date()}}}Copy the code

The benefits of using Swift KeyPath over the original Objective-C processing are obvious. First, the code that sets the observations and handles them is put together, making code maintenance easier. Second, when processing, we get type-safe results, not values from a dictionary; Finally, there is no need to use context to tell which observations have changed, and using Observation to hold the observer frees it from the hassle of memory management. The observer’s life cycle ends with the release of the AnotherClass. In Class, by contrast, you also need to find a good time to stop observing when the instance is finished, otherwise you will cause a memory leak.

However, it is clear that Swift’s KVO relies on more than it used to. In Objective-C you can listen almost without restriction on all properties that satisfy KVC, and now you need properties decorated with Dynamic and @objc. Most of the time, the class you want to observe contains both of these modifiers, and sometimes you probably can’t modify the source code of the class you want to observe. In such a case, one possible solution is to inherit the class and override the observed properties using Dynamic and @objc. For example, MyChildClass might need a new MyChildClass if there was no date tag in MyClass:

class MyClass: NSObject {
    var date = Date()}class MyChildClass: MyClass {
    @objc dynamic override var date: Date {
        get { return super.date }
        set { super.date = newValue }
    }
}
Copy the code

Another big question is what to do with Swift types that are not NSObject. Since Swift types are not implemented through KVC, let alone KVO on attributes. For Swift types, there is currently no native KVO-like observation mechanism in the language. You may have to implement your own set of similar alternatives through property observation. Another possible idea is to wrap the Swift type by reference and then introduce the observation mechanism using the KeyPath display that can also be used on the Swift type.

58. The local scope

In C, it is possible to add pairs of curly braces ({}) inside methods to limit the scope of the code. There are two general benefits to doing this. The first is that temporary variables become invalid beyond the scope. This not only makes naming within the method easier, but also makes it easier to recycle unwanted references, which makes the code a little more efficient. In addition, the insertion of parentheses in the appropriate place also facilitates the sorting of methods. For code that is not easy to extract as a single method, but should make some distinction from the rest of the current method, using curly braces provides a relatively natural division of such structures.

In Swift, the use of curly braces is not supported because it conflicts with the definition of closures. If you want to similarly use local scope to separate code, a good option is to define a global method that accepts ()->() as a function and then execute it:

func local(_ closure: () - > ()) {
    closure()
}
Copy the code

In use, you can simulate local scope using the trailing closure feature:

 override func loadView(a) {
    let view = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 480))
    view.backgroundColor = .white
    local {
        let titleLabel = UILabel(frame: CGRect(x: 150, y: 30, width: 200, height: 40))
        titleLabel.textColor = .red
        titleLabel.text = "Title"
        view.addSubview(titleLabel)
}
    local {
        let textLabel = UILabel(frame: CGRect(x: 150, y: 80, width: 200, height: 40))
        textLabel.textColor = .red
        textLabel.text = "Text"
        view.addSubview(textLabel)
}
    self.view = view
}
Copy the code

In Swift 2.0, however, to handle exceptions, Apple added the do keyword as the scope for catching exceptions. Just in time to provide a perfect local scope, you can now simply use do to separate code:

do {
    let textLabel = UILabel(frame: CGRect(x: 150, y: 80, width: 200, height: 40))
    //...
}
Copy the code

Another nice trick in Objective-C is to use GNU C’s declaration extension to restrict local scope while simultaneously assigning values, which, when used properly, can make your code more compact and clean. For example, the titleLabel above, if you want to keep a reference, can be written in Objective-C as:

self.titleLabel = ({
    UILabel *label = [[UILabel alloc]
            initWithFrame:CGRectMake(150.30.20.40)];
    label.textColor = [UIColor redColor];
    label.text = @"Title";
    [view addSubview:label];
    label; 
});
Copy the code

There is of course no GNU C extension in Swift, but using anonymous closures, write something like:

let titleLabel: UILabel = {
    let label = UILabel(frame: CGRect(x: 150, y: 30, width: 200, height: 40))
    label.textColor = .red
    label.text = "Title"
    return label
}()
Copy the code

59. Sentence etc

The content of a Swift string is evaluated using the == operator

There is a huge difference between Swift’s behavior and Objective-C’s in terms of execution. In Objective-C, the notation equals equals means to determine whether or not two objects point to the same memory location. In fact, many times this is not often expected to be equal, the content of the object is often concerned with the same, and this sense of equality even if the two objects reference not the same block of memory address, but also can be done.

In Objective-C, it’s common to override -isEqual: or to implement a method with type information like -isequalToString: -isequaltoClass:. If you don’t override -is equal: in any subclass, then when you call this method, you’re going to use the version in NSObject directly, and make the objective-C == judgment.

This is quite different in Swift, where == is a declaration of the operator, and Equatable specifies the protocol method for this operator:

protocol Equatable {
    func = =(lhs: Self.rhs: Self) -> Bool
}
Copy the code

Types that implement this protocol need to define the == operator that is appropriate for their type and should return true if they think the two inputs are equal.

If you implement the type Equatable, you can use == and! The = operator is used to determine equality. (Implement only ==,! = by the library automatically take the reverse implementation). This is very similar to the original objective-c isEqual: behavior.

For the == implementation, we do not place it in the corresponding extension as we do for some other protocols. Instead, we place it in the global scope. This is reasonable because == should be available globally. In fact, Swift operators are all global,

The basic types of Swift override their own version of ==, and a subclass of NSObject that uses == and does not override that subclass will instead call the -isEqual: method of that class. So if this NSObject subclass already implements -is equal:, using == doesn’t cause it to behave differently from Swift; But if a suitable override cannot be found, this method will roll back to the original implementation in NSObject, making a direct comparison of the referenced object address. So you have two choices for an NSObject subclass, either override ==, or override -is equal:. If you only use your class in Swift, the two approaches are equivalent; But if you still need to use this class in Objective-C, because Objective-C does not accept operator overloading and can only use -is equal:, then you should consider using the second option.

Instead of using == in Objective-C to determine object Pointers, Swift provides another operator ===. In Swift === there is only one overload

 func = = =(lhs: AnyObject? .rhs: AnyObject?). -> Bool
Copy the code

It is used to determine whether two AnyObjects are the same reference.

60. Hash

A hash table, or hash table, is an underlying data structure in the program world that needs to provide the same hash value for the same object in order to ensure deterministic and performance when used as the key of a dictionary.

Swift types provide hashing support by implementing a protocol called Hashable:

protocol Hashable : Equatable {
    var hashValue: Int { get}}Copy the code

In Swift’s native Dictionary, the key must be the Hashable protocol type to be implemented. The underlying Swift types, such as Int or String, already implement this protocol and can therefore be used as keys. For example, the hashValue of an Int is itself.

There’s a -hash method in NSObject. When we override -isEqual: for a subclass of NSObject, we generally override -hash to provide a method that returns the same hash if equality is true. In Swift, NSObject also implements Hashable by default, and just like in equalization, access to the hashValue property of an NSObject returns its corresponding -hash value.

So the strategy for overriding a hash method is similar to the strategy for equalizing: for non-Nsobject classes, you need to follow Hashable and give a hash algorithm based on the content of the == operator; In the case of NSObject subclasses, you have to choose the appropriate override, depending on whether you want to access it in Objective-C, to implement Hashable’s hashValue or override NSObject’s -hash method directly.