Swift Runtime Exploration

1. Original code

// // main.swift // TestSwCmd // // import Foundation print("Hello, World!" ) class Teacher { var age : Int = 18 func teach() {print("a Teacher teach")}} let t = Teacher() func test() {// Obtain method list var methodCount:UInt32 = 0  let methodlist = class_copyMethodList(Teacher.self, &methodCount) if methodlist ! = nil { for i in 0.. <numericCast(methodCount) { if let method = methodlist? [i]{ let methodName = method_getName(method); Print (" describing: methodName) \(String(describing: methodName) ")}else{print("not found method"); Var count:UInt32 = 0 let proList = class_copyPropertyList(teach.self, &count) if proList ! = nil { for i in 0.. <numericCast(count) { if let property = proList? [i]{ let propertyName = property_getName(property); Print (" property member property :\(String(utf8String: propertyName)!) ")}else{print(" the attribute you want is not found "); }}else{print(" attribute - not available - not available ")}} test()Copy the code

The Log output

Hello, World! Program ended with exit code: 0Copy the code

Run this code and you’ll see that both our method list and our property list are currently empty at this time.

2. @objc flag, what happens if we add our current methods and properties to it?

At this point our code will output our current teach method and age property.

class Teacher {
    @objc var age : Int = 18
    
    @objc func teach()  {
        print("a Teacher teach")
    }
}
Copy the code
Log Output: Hello, World! List of methods :age List of methods :setAge: list of methods :teach Property Member property: Age Program ended with exit code: 0Copy the code

// But there is no way to use it for our OC at the moment:

The output of the class inherited from NSObject

class Teacher : NSObject{
     var age : Int = 18
    
     func teach()  {
        print("a Teacher teach")
    }
}
Copy the code

The output of the class that inherits from NSObject

Hello, World! Program ended with exit code: 0 could not be obtainedCopy the code

If all the methods and attributes were to be printed, @objc would also need to be added

4. A conclusion can be drawn:

  • For pure Swift classes, there are no dynamic properties. Methods and properties without any modifiers. In this case, we no longer have the Runtime feature, which is consistent with our method scheduling (V-table scheduling). Dynamic

  • For pure Swift classes, methods and properties with @objc identifiers are currently available via the Runtime API, but cannot be scheduled in our OC

  • For classes that inherit from NSObject, if we want to get the current properties and methods dynamically, we must add the @objc keyword before the declaration, and the method exchange: dynamic identifier. Otherwise there is no way to get it through the Runtime API

2, reflection

Reflection is the ability to dynamically retrieve type, member information, invoke methods, properties, and so on at run time. As we have seen above, a pure Swift class does not allow us to operate directly like OC; However, the Swift standard library still provides a reflection mechanism that allows us to access member information.

class Teacher { var age : Int = 18 func teach() { print("a Teacher teach") } } func testMirror() { let t = Teacher() let mirror = Mirror(reflecting: T) for pro in mirror.children{print("\(pro.label):\(pro.value)")}} testMirror() Log Optional("age"):18 Program ended with exit code: 0Copy the code

What can we do with the Mirror at this point? JSON parsing should be the first thing that comes to mind:

func jsonPare(_ obj : Any ) -> Any { let mirror = Mirror(reflecting: obj) guard ! mirror.children.isEmpty else { print("return: \(obj)") return obj } var keyValues : [String : Any] = [:] for children in mirror.children { if let keyName = children.label { keyValues[keyName] = JsonPare (children.value)}else {print("children.label is empty ")}} print("result: \(keyValues)") return keyValues } jsonPare(t)Copy the code
Log Output: Hello, World! return: 18 result: ["age": 18] Program ended with exit code: 0Copy the code

In the above code we have completed a simple JSON parsing Demo, but many errors are output, how to express the error in Swift professional that?

Error handling

Swift provides the Error protocol to identify the current application Error situation, Error is defined as follows:

 public protocol Error{ 
 }
Copy the code

So struct, Class, enum we can represent an error by following this protocol. Here we choose enum

enum JSONMapError: Error {
     case emptyKey 
     case notConformProtocol  
}
Copy the code

The correct way to throw an error is to use the throw keyword. At the same time, the compiler will tell us that our function is not declared as throws, so after modifying the code we get the result like this:

enum JSONMapError: Error { case emptyKey case notConformProtocol } protocol CustomJSONMap { func jsosnMap() thorws -> Any } extension CustomJSONMap{ func jsonMap() throws -> Any{ let mirror = Mirror(reflecting: self) guard ! mirror.children.isEmpty else {return self} var keyValue: [String: Any] = [:] for children in mirror.children{ if let value = children.value as? CustomJSONMap{ if let keyName = children.label { keyValue[keyName] = try value.jsonMap() }else{ throw JSONMapError.emptyKey } }else{ throw JSONMapError.notConformProtocol } } return keyValue } }Copy the code

Let’s take a look at the code we’ve written so far and see that the compiler requires us to use the try keyword to handle errors.

Here are some ways to handle errors in Swift:

The first, using the try keyword, is the easiest and our favorite: flip the pan

There are two caveats to using the try keyword: one or try? One is try!

try? : returns an optional type. There are two types of results: one is success, which returns a specific dictionary value; Error, but it doesn't matter what kind of error it is, it returns a nil try! That you have absolute confidence in this code, this line of code will never go wrong!Copy the code

The second way is to use do… catch

If you feel that using Error alone is not enough to express the Error information in the way you want, use the LocalError protocol, defined as follows:

public protocol LocalizedError : Error {
 /// A localized message describing what error occurred. 
 var errorDescription: String? { get }
 /// A localized message describing the reason for the failur e. 
 var failureReason: String? { get }
 /// A localized message describing how one might recover from the failure.
 var recoverySuggestion: String? { get }
 /// A localized message providing "help" text if the user req uests help.
 var helpAnchor: String? { get }
}
Copy the code

CustomError has three default properties:

a static errorDomain an errorCode integer an errorUserInfo dictionary

Iv. Other contents

1. numericCast

The method used in the code, method definition

Returns the given integer as the equivalent value in a different integer type.

Returns the given integer as equivalent in different integer types.

var methodCount:UInt32 = 0 let methodlist = class_copyMethodList(Teacher.self, &methodCount) for i in 0.. <numericCast(methodCount) { }Copy the code