Objective-C
中有一些很冷僻
但是在特定情况下会很有用
的关键字
,比如说通过类型获取对应编码的@encode
就是其中之一。
在Objective-C
中@encode
使用起来很简单,通过传入一个类型,我们就可以获取代表这个类型的编码C 字符串
:
char *typeChar1 = @encode(int32_t);
char *typeChar2 = @encode(NSArray);
NSLog(@"%s", typeChar1); // i
NSLog(@"%s", typeChar2); // {NSArray=#}
我们可以对任意的类型进行这样的操作。这个关键字最常用
的地方是在Objective-C 运行时
的消息发送机制
中,在传递参数时,由于类型信息的缺失,需要类型编码
进行辅助
以保证
类型信息
也能够被传递。在实际的应用开发中,其实使用案例比较少:某些API 中Apple 建议使用NSValue
的valueWithBytes:objCType:
来获取值(比如CIAffineClamp ),这时objCType
就需要类型的编码值
;另外就是在类型信息丢失时我们可能需要用到这个特性,我们稍后会举一个这方面的例子。
Swift 使用了自己的Metatype
来处理类型,并且在运行时保留了这些类型的信息,所以Swift 并没有
必要保留
这个关键字
。我们现在不能获取任意类型的类型编码了,但是在Cocoa 中我们还是可以通过NSValue
的objcType
属性来获取对应值的类型指针:
open class NSValue : NSObject, NSCopying, NSSecureCoding {
//...
open var objCType: UnsafePointer<Int8> { get }
//...
}
比如我们如果想要获取某个Swift 类型的“等效的”
类型编码的话,我们需要先将它转换为NSNumber
(NSNumber 是NSValue 的子类),然后获取类型:
let int: Int = 0
let float: Float = 0.0
let double: Double = 0.0
let intNumber: NSNumber = NSNumber(value: int)
let floatNumber: NSNumber = NSNumber(value: float)
let doubleNumber: NSNumber = NSNumber(value: double)
let intString = String.init(validatingUTF8: intNumber.objCType)
let floatString = String.init(validatingUTF8: floatNumber.objCType)
let doubleString = String.init(validatingUTF8: doubleNumber.objCType)
print(intString)
print(floatString)
print(doubleString)
// 输出
// Optional("q")
// Optional("f")
// Optional("d")
对于像其他一些可以转换
为NSValue
的类型,我们也可以通过同样的方式获取类型编码,一般来说这些类型会是某些struct
,因为NSValue 设计的初衷就是被作为那些不能直接放入NSArray 的值的容器来使用的:
let p = NSValue.init(cgPoint: CGPoint.init(x: 3, y: 3))
let string = String.init(validatingUTF8: p.objCType)
print(string) // Optional("{CGPoint=dd}")
let t = NSValue.init(cgAffineTransform: CGAffineTransform.identity)
let str = String.init(validatingUTF8: t.objCType)
print(str) // Optional("{CGAffineTransform=dddddd}")
有了这些信息之后,我们就能够在这种类型信息可能损失的时候构建起准确的类型转换和还原机制了。
举例来说,我们如果想要在UserDefaults
中存储一些不同类型
的数字
,然后读取时需要准确
的还原
为之前的类型
的话,最容易想到的应该是使用类簇来获取这些数字转为NSNumber
后真正的类型
,然后存储。但是NSNumber 的类簇子类都是私有的,我们如果想要由此判定的话,就不得不使用私有API,这是不可接受
的。变通的方法就是在存储时使用objCType 获取类型
,然后将数字本身
和类型的字符串
一起存储。在读取时就可以通过匹配类型字符串和类型的编码,确定数字本来所属的类型,从而直接得到像Int
或者Double
这样的类型明确的量。