This is the 17th day of my participation in Gwen Challenge

SwiftyJSON is a Swift library for better handling JSON data. Its core code + comments is only about 1400 lines.

Here’s what I learned from reading the source code.

Realize the principle of

At the heart of SwiftyJSON is a struct: JSON. JSON defines all types as:

public enum Type: Int {
    case number
    case string
    case bool
    case array
    case dictionary
    case null
    case unknown
}
Copy the code

For all supported types, a property of the corresponding type is encapsulated inside JSON to hold the value. When a value is modified, the corresponding type of the value is also saved. When you get the value, different properties are read for different types.

JSON implements the swift. Collection protocol, so we can use JSON like collections. Inside it is a method that calls the corresponding property (array or dictionary), depending on the type.

extension JSON: Swift.Collection {
    // Call the method of the corresponding property (rawArray or rawDictionary) depending on the type
}
Copy the code

Determine the specific type of Any

If let data = object as? Data {}, there are multiple if lets when there are multiple types. Switch Case is used in SwiftyJSON, which makes it more clear.

public init(_ object: Any) {
    switch object {
    case let object as Data:
        // Object is of the Data type
    default:
        self.init(jsonObject: object)
    }
}
Copy the code

Is NSNumber created by Bool or numeric value?

Nsnumbers can be created from Bool values, or they can be created from various types of values. So once you’ve created it, how do you know if NSNumber is created by Bool or numeric value?

I haven’t thought about this before, but I always convert Bool to Bool when I need Bool, I always convert value to value when I need value, and I don’t really know what kind of data NSNumber is used to represent.

There is a code in SwiftyJSON to determine if NSNumber is Bool:

private let trueNumber = NSNumber(value: true)
private let falseNumber = NSNumber(value: false)
private let trueObjCType = String(cString: trueNumber.objCType)
private let falseObjCType = String(cString: falseNumber.objCType)

// MARK: - NSNumber: Comparable

extension NSNumber {
    fileprivate var isBool: Bool {
        let objCType = String(cString: self.objCType)
        if (self.compare(trueNumber) = = .orderedSame && objCType = = trueObjCType) || (self.compare(falseNumber) = = .orderedSame && objCType = = falseObjCType) {
            return true
        } else {
            return false}}}Copy the code

Compare () to the two NSNumber constants created by Bool, and then to the objCType.

The objCType property of NSNumber returns a C string containing the Objective-C type of the data contained in the NSNumber object. This string is encoded by the @encode() compiler directive.

Let’s print out a few objCTypes.

let trueObjCType = String(cString: NSNumber(value: true).objCType)
let falseObjCType = String(cString: NSNumber(value: false).objCType)
let intObjCType = String(cString: NSNumber(value: 1).objCType)
let doubleObjCType = String(cString: NSNumber(value: 1.1).objCType)

debugPrint(trueObjCType)
/ / output "c"
debugPrint(falseObjCType)
/ / output "c"
debugPrint(intObjCType)
/ / output "q"
debugPrint(doubleObjCType)
/ / output "d"
Copy the code

In Objective-C, we can get the C string representing the encoding of this type by passing @encode a type:

char *typeChar1 = @encode(int32_t);
char *typeChar2 = @encode(NSArray);
// typeChar1 = "i", typeChar2 = "{NSArray=#}"
Copy the code

When we need to know the exact type of an NSNumber, we can use objCType to get the type and compare it with the corresponding encoding.

In addition, NSValue also has an objCType attribute, which helps us get the exact type.

reference

Type encoding @encode