猫·仁波切

我得把我那么多吐槽笔记都迁移过来别整到不靠谱的那堆上

广州实时工具App逆向

简记。用了 IDA Pro,安卓手机的 Remote 客户端。以及 apktool 等。

Github: guangzhou-realtime-bus

  • 生成 e=3 的 1024 位 RSA 密钥对
  • 公钥串用查表加密(byte 映射),然后 base64 封装发送给服务器
  • 服务器返回一串用公钥加密过的数据
  • 用本地私钥解密后,该数据包含未知96字节的一段数据和 DES Key
  • 从此通信用 DES 加密

base64封装过程:先打包字符串长度,然后是原始字符串(JSON),然后是0x10(md5字符串长度), 然后是 md5 校验值。整个二进制字符串用 base64 转码,POST 给服务器。

具体的登录注册过程还需要进一步抓包分析,不过暂时兴趣不在这里了。

Swift 2.0 的错误处理(Swift 2.0 Error Handling)

1
2
3
4
5
6
7
8
9
10
protocol ErrorType {
  var _domain: String { get }
  var _code: Int { get }
}

@asmname("swift_bridgeErrorTypeToNSError") func _bridgeErrorTypeToNSError(e: ErrorType) -> AnyObject

@asmname("swift_stdlib_getErrorCode") func _stdlib_getErrorCode<T : ErrorType>(x: UnsafePointer<T>) -> Int

@asmname("swift_stdlib_getErrorDomainNSString") func _stdlib_getErrorDomainNSString<T : ErrorType>(x: UnsafePointer<T>) -> AnyObject

Foundation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
protocol _ObjectiveCBridgeableErrorType : ErrorType {
  init?(_bridgedNSError: NSError)
}

struct NSCocoaError : RawRepresentable, _BridgedNSError, _ObjectiveCBridgeableErrorType, ErrorType, __BridgedNSError, Hashable, Equatable {
  let rawValue: Int
  init(rawValue: Int)
  static var _NSErrorDomain: String {
    get {}
  }
  typealias RawValue = Int
}


infix func ==(a: _GenericObjCError, b: _GenericObjCError) -> Bool
infix func ==(a: _GenericObjCError, b: _GenericObjCError) -> Bool
func ==<T : __BridgedNSError where T.RawValue : SignedIntegerType>(lhs: T, rhs: T) -> Bool

@available(OSX 10.11, iOS 9.0, *)
func resolveError(error: NSError?) throws

enum _GenericObjCError : ErrorType {
  case NilError
  var hashValue: Int {
    get {}
  }
  var _domain: String {
    get {}
  }
  var _code: Int {
    get {}
  }
}

@asmname("swift_stdlib_bridgeNSErrorToErrorType")
func _stdlib_bridgeNSErrorToErrorType<T : _ObjectiveCBridgeableErrorType>(error: NSError, out: UnsafeMutablePointer<T>) -> Bool

@asmname("swift_convertNSErrorToErrorType") func _convertNSErrorToErrorType(error: NSError?) -> ErrorType

@objc enum NSURLError : Int, _BridgedNSError, _ObjectiveCBridgeableErrorType, ErrorType, __BridgedNSError { ... }


protocol __BridgedNSError : RawRepresentable {
  static var _NSErrorDomain: String { get }
}
@asmname("swift_convertErrorTypeToNSError") func _convertErrorTypeToNSError(error: ErrorType) -> NSError
func ~=(match: NSCocoaError, error: ErrorType) -> Bool
protocol _BridgedNSError : __BridgedNSError, _ObjectiveCBridgeableErrorType, Hashable {
  static var _NSErrorDomain: String { get }
}

ErrorType 在 Swift 中表示。

1
2
3
4
5
6
7
8
extension NSError : ErrorType {
  @objc dynamic var _domain: String {
    @objc dynamic get {}
  }
  @objc dynamic var _code: Int {
    @objc dynamic get {}
  }
}

北京实时公交分析

361 条线路,705条单向线路。 aibang 负责数据服务。

每辆车,每15秒更新一次 GPS,

为第三方扩展创建 Swift 模块

本文提出了一种将第三方扩展引入到 Swift 标准库的方法。

以 Alamofire 为例,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cd Path-To-Alamofire-Src-Dir
mkdir -p 32 64

# 创建动态链接库,及对应 Swift 模块,32/64版本
xcrun swiftc -sdk $(xcrun --show-sdk-path --sdk iphoneos) Alamofire.swift -target arm64-apple-ios7.1 -target-cpu cyclone -emit-library -emit-module -module-name Alamofire -v -o libswiftAlamofire.dylib -module-link-name swiftAlamofire -Xlinker -install_name -Xlinker @rpath/libswiftAlamofire.dylib

mv Alamofire.swiftdoc Alamofire.swiftmodule libswiftAlamofire.dylib ./64

xcrun swiftc -sdk $(xcrun --show-sdk-path --sdk iphoneos) Alamofire.swift -target armv7-apple-ios7.1 -target-cpu cyclone -emit-library -emit-module -module-name Alamofire -v -o libswiftAlamofire.dylib -module-link-name swiftAlamofire -Xlinker -install_name -Xlinker @rpath/libswiftAlamofire.dylib

mv Alamofire.swiftdoc Alamofire.swiftmodule libswiftAlamofire.dylib ./64

# 创建 universal lib
lipo -create ./{32,64}/libswiftAlamofire.dylib  -output ./libswiftAlamofire.dylib

# 创建模拟器用 lib
xcrun swiftc -sdk $(xcrun --show-sdk-path --sdk iphonesimulator) Alamofire.swift -target i386-apple-ios7.1 -target-cpu yonah -emit-library -emit-module -module-name Alamofire -v -o libswiftAlamofire.dylib -module-link-name swiftAlamofire -Xlinker -install_name -Xlinker @rpath/libswiftAlamofire.dylib

其他相关 target

1
2
3
4
-target armv7-apple-ios7.1 -target-cpu cortex-a8
-target arm64-apple-ios7.1 -target-cpu cyclone
-target i386-apple-ios7.1 -target-cpu yonah
-target x86_64-apple-ios7.1 -target-cpu core2

其实你了解 Swift 模块结构的化,应该回想到,将第三方模块创建为 swiftmodule 应该是最靠谱的选择。不过实际操作发现, 编译命令无法很方便地调整,主要是因为 xcodebuild 系统,和编译命令不知道怎么导出。也是略纠结。

实际上,如果使用 Carthage 的话,即把第三方扩展作为 Framework 引入,会导致无法支持 iOS 7,但是 Swift 本身是支持 iOS 7 的, 在编译命令和生成的文件中检查发现,对于 iOS 7,Swift 使用了纯静态模块编译的方法。所以其实我们引入第三方扩展的时候也可以这样做。

以下是静态编译所需命令:

1
2
3
xcrun swift -sdk $(xcrun --show-sdk-path --sdk macosx) SwiftyJSON.swift -c -parse-as-library -module-name SwiftyJSON -v -o SwiftyJSON.o

ar rvs libswiftSwiftyJSON.a SwiftyJSON.o

如何使用?

将编译结果扔到:

1
2
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift_static

下对应目录。

然后在 Xcode 里,直接 import。

Swift Beta3 Changes ( Swift 在 Beta3 中的变化)

准确说是 beta2 Swift version 1.0 (swift-600.0.34.4.8) 到 beta3 Swift version 1.0 (swift-600.0.38.7) 的变化。

对了,补充下。 beta1 Swift version 1.0 (swift-600.0.34.4.5) 到 beta2 几乎没有什么变化。

语法

nil 成为关键字。

[KeyType : ValueType] 可以表示字典类型 Dictionary<KeyType, ValueType>

[Type] 用于表示原 Array 类型 Type[],等价 Array<T>,原用法会导致警告。

增加 @noinline 属性

.. 运算符改为 ..<,不容易和 ... 混淆。

函数、类型

sort() 改名为 sorted()。新增 sort() 函数,参数为 inout

Index 类型中的 .succ() 变为 .successor().pred() 变为 .predecessor()

C/ObjC 交互变化

增加 UnsafeMutableArray<T> 类型。

增加 CFunctionPointer<T> 类型。

删除 CConstVoidPointerCMutableVoidPointer。替换为 UnsafePointer<()>ConstUnsafePointer<Int32>

删除 CConstPointer<T>CMutablePointer<T>。替换为 UnsafePointer<T>ConstUnsafePointer<T>

这么一来指针操作简单了好多。原有会出现 COpaquePointer 的不合理情况,也都对应到适合的类型。

CString 可以从 UnsafePointer<UInt8>UnsafePointer<CChar> 两种类型构造获得,之前只支持 UInt8

module.map 中头文件声明转换为 Swift 声明不再使用 C 兼容类型,直接使用 Swift 相应类型。原有 CInt,现在成为 Int32

结构体会自动添加构造函数 init(field1:field2:...) 这样。

nil

去掉了 NilType,增加了 NilLiteralConvertiblenil 成为关键字。可以认为是 nil 常量。

1
2
3
protocol NilLiteralConvertible {
  class func convertFromNilLiteral() -> Self
}

除了 Optional 、上面所提到的指针类型外,RawOptionSet 也实现了该协议。

Array

去掉了 .copy()unshare() 方法。

增加了以下方法:

1
2
func makeUnique(inout buffer: ArrayBuffer<T>, e: T, index: Int)
func sorted(isOrderedBefore: (T, T) -> Bool) -> Array<T>

看起来 Array 对底层容器的引用有了更好的控制 ArrayBufferType 增加了判断方法 func isMutableAndUniquelyReferenced() -> Bool

Array 目前可以认为是真正的值类型。

指针

增加了 _Pointer protocol

1
2
3
4
protocol _Pointer {
  var value: RawPointer { get }
  init(_ value: RawPointer)
}

表示一个类型可以对应到原生指针。

同时成为内部桥接类型,编译器内部在转换时使用它(取出 RawPointer, 构造具体指针类型)。

模块

增加了 StdlibUnittest 模块。 声明代码。单元测试终于有了。

Use Swift Dynamic Framework (如何科学地引用第三方 Swift 库)

排名 16 了。啧啧。你看才刚出一个月。

目前已经有了很多非常棒的 Swift 第三方库, JSON 处理啊、 HTTP 访问啊、 UIView 插件啊等等。

如何科学地引用这些第三方库呢?

现状

CocoaPods 由于完全使用静态链接解决方法,过度依赖 Objective-C ,目前应该是官方 repo 有提到是 -Xlinker error , 这个问题之前我也遇到过,无解。除非手工执行 ar 不用 ldlibtool

小伙伴有用子目录的方法引用代码,貌似不错,还有就是直接用 git submodule,看起来维护性也可以。

简单解决方案

一个良好的第三方库应该实现为 Cocoa Touch Framework (实际内容为 Header + 动态链接库)。而不是直接把 Swift 代码 Copy 过来放入自己的项目。这里以一个简单项目为例,介绍如何科学使用。

目标描述

用 Swift 创建一个 Demo ,使用 SwiftyJSON 和 LTMorphingLabel 库。

项目的名字叫 DemoApp 。

创建 Workspace

创建一个 Workspace ,名字随意,位置能找到就好。这个 Workspace 主要用来管理我们的项目及其依赖的第三方库。

创建 DemoApp

在 Workspace 创建一个 App ,因为是测试所以我选了 Single View Application 。

引入 SwiftyJSON

SwiftyJSON 是一个 Cocoa Touch Framework ,可以直接使用, git clone 后,添加项目到 Workspace 即可。

尝试操作发现。。最容易最不会出错的方法就是直接从 Finder 里把 .xcodeproj 文件拖动到 Workspace 。

引入 LTMorphingLabel

LTMorphingLabel 是一个 App Deme 式项目。其中 Label View 的实现在一个子目录中。可以采用创建 Cocoa Touch Framework 的方法来引入这几个文件。

当然也可以直接把目录拖到我们的 DemoApp 里,不过太原始粗暴了。

为 App 添加依赖

在 DemoApp 的 Genral 选项卡中,添加 Linked Frameworks and Libraries 。选择 Workspace 中 SwiftyJSON 和 LTMorphingLabel 两个 .framework

如果是直接选择来自其他项目的 .framework 而不是同一 Workspace ,那么这里也许还要同时加入 Embedded Binaries

使用

添加好依赖后,就可以在 DemoApp 项目代码中 import SwiftyJSON 或者 import LTMorphingLabel 来使用对应的库。同时还可以用 Command + 鼠标点击的方法查看声明代码。

除错

比较坑爹的是,实际上按照以上方法, LTMorphingLabel 并不能正常使用,查看报错信息发现是自动生成的 LTMorphingLabel-Swift.h 有处语法无法被识别,编辑器找到 .h 文件,注释掉这行诡异代码即可。

看起来目前的 Bridge Header 和 -emit-objc-header 实现还是有问题的。小伙伴一定要淡定。

对于非 Workspace

如果不喜欢使用 Workspace ,也可以将第三方库的编译结果,一个 .framework 目录拖到项目文件里,然后添加 Embedded Binaries

评论

创建 Cocoa Touch Framework 选项中,可以使用 Swift 代码,此时编译结果(默认)会包含 module.modulemap 文件, 之前有介绍过它的作用,通过它, Swift 可以使用第三方模块。参考 Module System of Swift (简析 Swift 的模块系统)

实际上这个解决方案绕了一大圈,通过 Swift 文件导出 ProjName-Swift.h、然后 module.modulemap 模块描述文件引入、然后再由 Swift 导入。

其实 .framework 同时也包含了 ProjName.swiftmodule/[ARCH].swiftmodule 不过看起来没有使用到,而且默认在 IDE 下也不支持 Swift 从 .swiftmodule 文件导入,比较坑。希望以后版本能加入支持。

.framework 包含了所有 Swift 标准库的动态链接库,小伙伴可能会以为这会导致编译后的 App 变大。其实大可放心,任何 Swift 语言的 App 都会包含这些动态链接库,而且只会包含一个副本。此方法对 App 最终的大小几乎无影响。

注: 个人测试了下,发现这个 .swiftmodule 是可以通过其他方法使用的,绕过 module.modulemap,应该是更佳的解决方案,但是需要控制命令行参数。

至于静态链接库,过时了。抛弃吧。

参考

Swift Undocumented Grammar (Swift 黑语法)

本文介绍 Swift 的 Undocumented 语法特性。

电子书上介绍的 default function parameter 这里都不好意思拿出来写。

咳咳。持续更新。

用关键字当变量名

Keywards as variable name.

1
2
3
// escaped variable name
let `let` = 1000
dump(`let`, name: "variable named let")

new 关键字

The new keyword.

快速初始化数组。

1
let an_array_with_100_zero = new(Int)[100]

protocol type

use protocol<Protocol1, Protocol2, ...> as a type.

How I find it?

瞎试出来的。

Cocoa Extensions in Swift ( Cocoa 在 Swift 中所添加的扩展)

最近看到了 Swift Style Guide 个人觉得内容太少, Swift 本身作为一门庞大的语言,语素众多。本文就 Swift 本身对 Cocoa 的扩展,看看对日常 Cocoa 风格有什么影响。

Swift 本身的特性,导致它在一些用法上和 Objective-C 上有所不同,比如 ObjC 的 struct 单纯和 C 的一样,但是在 Swift 中的 struct 则要强大得多。

个人认为比如 CGPointMake 这样的函数,理论上不应该出现在 Swift 代码中。而是应该用 CGPoint(x:y:)

本文可以作为参考手册使用。

标准库扩展

ObjectiveC

值得注意的是 Selector 相关方法,实现了 StringLiteralConvertible。也可以从 nil 获得。

Foundation

这里忽略之前介绍过的 _BridgedToObjectiveC 相关内容。

协议附加

Sequence 协议

1
NSMutableArray NSSet NSArray NSMutableDictionary NSMutableSet NSDictionary

所有以上这些类型都可以通过 for-in 操作。

*LiteralConvertible

1
NSNumber NSString NSArray NSDictionary

隐式类型转换

CF 几乎都对应到了 NS 类型。这里略去

  • NilType –> NSZone
  • Dictionary<KeyType: Hashable, ValueType> –> NSDictionary
  • NSDictionary –> Dictionary<NSObject, AnyObject>
  • String <–> NSString
  • NSArray –> AnyObject[]
  • A[] –> NSArray
  • Float Double Int UInt Bool –> NSNumber
  • NSRange –> Range<Int> // 比较有意思的一个

方法扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// let s = NSSet(objects: 12, 32, 23, 12)
extension NSSet {
  convenience init(objects elements: AnyObject...)
}
extension NSOrderedSet {
  convenience init(objects elements: AnyObject...)
}
// 这里注意,NSRange 和 Swift Range 对 range 结束的表述方法不同
// NSRange 保存 range 元素个数
// Swift Range 保存的是结束元素
// let r = NSRange(0..20)
extension NSRange {
  init(_ x: Range<Int>)
}
// let prop = NSDictionary(objectsAndKeys: "Feather", "name", "Programming", "hobby")
extension NSDictionary {
  convenience init(objectsAndKeys objects: AnyObject...)
}
extension NSObject : CVarArg {
  @objc func encode() -> Word[]
}

字符串的扩展方法非常多。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
  static func availableStringEncodings() -> NSStringEncoding[]
  static func defaultCStringEncoding() -> NSStringEncoding
  static func localizedNameOfStringEncoding(encoding: NSStringEncoding) -> String
  static func localizedStringWithFormat(format: String, _ arguments: CVarArg...) -> String
  static func pathWithComponents(components: String[]) -> String
  static func stringWithContentsOfFile(path: String, encoding enc: NSStringEncoding, error: NSErrorPointer = default) -> String?
  static func stringWithContentsOfFile(path: String, usedEncoding: CMutablePointer<NSStringEncoding> = default, error: NSErrorPointer = default) -> String?
  static func stringWithContentsOfURL(url: NSURL, encoding enc: NSStringEncoding, error: NSErrorPointer = default) -> String?
  static func stringWithContentsOfURL(url: NSURL, usedEncoding enc: CMutablePointer<NSStringEncoding> = default, error: NSErrorPointer = default) -> String?
  static func stringWithCString(cString: CString, encoding enc: NSStringEncoding) -> String?
  static func stringWithUTF8String(bytes: CString) -> String?
  func canBeConvertedToEncoding(encoding: NSStringEncoding) -> Bool
  var capitalizedString: String { get }
  func capitalizedStringWithLocale(locale: NSLocale) -> String
  func caseInsensitiveCompare(aString: String) -> NSComparisonResult
  func commonPrefixWithString(aString: String, options: NSStringCompareOptions) -> String
  func compare(aString: String, options mask: NSStringCompareOptions = default, range: Range<String.Index>? = default, locale: NSLocale? = default) -> NSComparisonResult
  func completePathIntoString(_ outputName: CMutablePointer<String> = default, caseSensitive: Bool, matchesIntoArray: CMutablePointer<String[]> = default, filterTypes: String[]? = default) -> Int
  func componentsSeparatedByCharactersInSet(separator: NSCharacterSet) -> String[]
  func componentsSeparatedByString(separator: String) -> String[]
  func cStringUsingEncoding(encoding: NSStringEncoding) -> CChar[]?
  func dataUsingEncoding(encoding: NSStringEncoding, allowLossyConversion: Bool = default) -> NSData
  var decomposedStringWithCanonicalMapping: String { get }
  var decomposedStringWithCompatibilityMapping: String { get }
  func enumerateLines(body: (line: String, inout stop: Bool) -> ())
  func enumerateLinguisticTagsInRange(range: Range<String.Index>, scheme tagScheme: String, options opts: NSLinguisticTaggerOptions, orthography: NSOrthography?, _ body: (String, Range<String.Index>, Range<String.Index>, inout Bool) -> ())
  func enumerateSubstringsInRange(range: Range<String.Index>, options opts: NSStringEnumerationOptions, _ body: (substring: String, substringRange: Range<String.Index>, enclosingRange: Range<String.Index>, inout Bool) -> ())
  var fastestEncoding: NSStringEncoding { get }
  func fileSystemRepresentation() -> CChar[]
  func getBytes(inout buffer: UInt8[], maxLength: Int, usedLength: CMutablePointer<Int>, encoding: NSStringEncoding, options: NSStringEncodingConversionOptions, range: Range<String.Index>, remainingRange: CMutablePointer<Range<String.Index>>) -> Bool
  func getCString(inout buffer: CChar[], maxLength: Int, encoding: NSStringEncoding) -> Bool
  func getFileSystemRepresentation(inout buffer: CChar[], maxLength: Int) -> Bool
  func getLineStart(start: CMutablePointer<String.Index>, end: CMutablePointer<String.Index>, contentsEnd: CMutablePointer<String.Index>, forRange: Range<String.Index>)
  func getParagraphStart(start: CMutablePointer<String.Index>, end: CMutablePointer<String.Index>, contentsEnd: CMutablePointer<String.Index>, forRange: Range<String.Index>)
  var hash: Int { get }
  static func stringWithBytes(bytes: UInt8[], length: Int, encoding: NSStringEncoding) -> String?
  static func stringWithBytesNoCopy(bytes: CMutableVoidPointer, length: Int, encoding: NSStringEncoding, freeWhenDone flag: Bool) -> String?
  init(utf16CodeUnits: CConstPointer<unichar>, count: Int)
  init(utf16CodeUnitsNoCopy: CConstPointer<unichar>, count: Int, freeWhenDone flag: Bool)
  init(format: String, _ _arguments: CVarArg...)
  init(format: String, arguments: CVarArg[])
  init(format: String, locale: NSLocale?, _ args: CVarArg...)
  init(format: String, locale: NSLocale?, arguments: CVarArg[])
  var lastPathComponent: String { get }
  var utf16count: Int { get }
  func lengthOfBytesUsingEncoding(encoding: NSStringEncoding) -> Int
  func lineRangeForRange(aRange: Range<String.Index>) -> Range<String.Index>
  func linguisticTagsInRange(range: Range<String.Index>, scheme tagScheme: String, options opts: NSLinguisticTaggerOptions = default, orthography: NSOrthography? = default, tokenRanges: CMutablePointer<Range<String.Index>[]> = default) -> String[]
  func localizedCaseInsensitiveCompare(aString: String) -> NSComparisonResult
  func localizedCompare(aString: String) -> NSComparisonResult
  func localizedStandardCompare(string: String) -> NSComparisonResult
  func lowercaseStringWithLocale(locale: NSLocale) -> String
  func maximumLengthOfBytesUsingEncoding(encoding: NSStringEncoding) -> Int
  func paragraphRangeForRange(aRange: Range<String.Index>) -> Range<String.Index>
  var pathComponents: String[] { get }
  var pathExtension: String { get }
  var precomposedStringWithCanonicalMapping: String { get }
  var precomposedStringWithCompatibilityMapping: String { get }
  func propertyList() -> AnyObject
  func propertyListFromStringsFileFormat() -> Dictionary<String, String>
  func rangeOfCharacterFromSet(aSet: NSCharacterSet, options mask: NSStringCompareOptions = default, range aRange: Range<String.Index>? = default) -> Range<String.Index>
  func rangeOfComposedCharacterSequenceAtIndex(anIndex: String.Index) -> Range<String.Index>
  func rangeOfComposedCharacterSequencesForRange(range: Range<String.Index>) -> Range<String.Index>
  func rangeOfString(aString: String, options mask: NSStringCompareOptions = default, range searchRange: Range<String.Index>? = default, locale: NSLocale? = default) -> Range<String.Index>
  var smallestEncoding: NSStringEncoding { get }
  func stringByAbbreviatingWithTildeInPath() -> String
  func stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacters: NSCharacterSet) -> String
  func stringByAddingPercentEscapesUsingEncoding(encoding: NSStringEncoding) -> String
  func stringByAppendingFormat(format: String, _ arguments: CVarArg...) -> String
  func stringByAppendingPathComponent(aString: String) -> String
  func stringByAppendingPathExtension(ext: String) -> String
  func stringByAppendingString(aString: String) -> String
  var stringByDeletingLastPathComponent: String { get }
  var stringByDeletingPathExtension: String { get }
  var stringByExpandingTildeInPath: String { get }
  func stringByFoldingWithOptions(options: NSStringCompareOptions, locale: NSLocale) -> String
  func stringByPaddingToLength(newLength: Int, withString padString: String, startingAtIndex padIndex: Int) -> String
  var stringByRemovingPercentEncoding: String { get }
  func stringByReplacingCharactersInRange(range: Range<String.Index>, withString replacement: String) -> String
  func stringByReplacingOccurrencesOfString(target: String, withString replacement: String, options: NSStringCompareOptions = default, range searchRange: Range<String.Index>? = default) -> String
  func stringByReplacingPercentEscapesUsingEncoding(encoding: NSStringEncoding) -> String
  var stringByResolvingSymlinksInPath: String { get }
  var stringByStandardizingPath: String { get }
  func stringByTrimmingCharactersInSet(set: NSCharacterSet) -> String
  func stringsByAppendingPaths(paths: String[]) -> String[]
  func substringFromIndex(index: Int) -> String
  func substringToIndex(index: Int) -> String
  func substringWithRange(aRange: Range<String.Index>) -> String
  func uppercaseStringWithLocale(locale: NSLocale) -> String
  func writeToFile(path: String, atomically useAuxiliaryFile: Bool, encoding enc: NSStringEncoding, error: NSErrorPointer = default) -> Bool
  func writeToURL(url: NSURL, atomically useAuxiliaryFile: Bool, encoding enc: NSStringEncoding, error: NSErrorPointer = default) -> Bool

CoreGraphics

几个常用基本类型都有了 Swift-style 的构造函数。其中 CGRect 有很多的相关运算都被封装为方法,很不错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
extension CGPoint : Equatable {
  static var zeroPoint: CGPoint
  init()
  init(x: Int, y: Int)
}
extension CGSize {
  static var zeroSize: CGSize
  init()
  init(width: Int, height: Int)
}
extension CGVector {
  static var zeroVector: CGVector
  init(_ dx: CGFloat, _ dy: CGFloat)
  init(_ dx: Int, _ dy: Int)
}
extension CGRect : Equatable {
  // 全为 0
  static var zeroRect: CGRect
  // 原点为无穷大,表示空
  static var nullRect: CGRect
  // 原点无穷小,宽高无穷大
  static var infiniteRect: CGRect
  init()
  init(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat)
  init(x: Int, y: Int, width: Int, height: Int)
  var width: CGFloat
  var height: CGFloat
  var minX: CGFloat
  var minY: CGFloat
  // 中点
  var midX: CGFloat
  var midY: CGFloat
  var maxX: CGFloat
  var maxY: CGFloat
  var isNull: Bool
  var isEmpty: Bool
  var isInfinite: Bool
  var standardizedRect: CGRect
  func standardize()
  var integerRect: CGRect
  func integerize()
  func rectByInsetting(#dx: CGFloat, dy: CGFloat) -> CGRect
  func inset(#dx: CGFloat, dy: CGFloat)
  func rectByOffsetting(#dx: CGFloat, dy: CGFloat) -> CGRect
  func offset(#dx: CGFloat, dy: CGFloat)
  func rectByUnion(withRect: CGRect) -> CGRect
  func union(withRect: CGRect)
  func rectByIntersecting(withRect: CGRect) -> CGRect
  func intersect(withRect: CGRect)
  func rectsByDividing(atDistance: CGFloat, fromEdge: CGRectEdge) -> (slice: CGRect, remainder: CGRect)
  func contains(rect: CGRect) -> Bool
  func contains(point: CGPoint) -> Bool
  func intersects(rect: CGRect) -> Bool
}

AppKit

1
2
3
extension NSGradient {
  convenience init(colorsAndLocations objects: (AnyObject, CGFloat)...)
}

UIKit

1
2
3
4
5
6
7
8
extension UIDeviceOrientation {
  var isPortrait: Bool
  // also isLandscape isValidInterfaceOrientation isFlat 
}
extension UIInterfaceOrientation {
  var isPortrait: Bool
  var isLandscape: Bool
}

这个模块是交叉编译的。。不太容易获得信息。不过好在扩展内容不多。

SpriteKit

1
2
3
extension SKNode {
  @objc subscript (name: String) -> SKNode[] { get }
}

特殊 Mirror 实现

1
2
3
4
5
NSSet NSDate NSArray NSRange NSURL NSDictionary NSString
CGPoint CGRect CGSize
NSView
UIView
SKTextureAtlas SKTexture SKSpriteNode SKShapeNode

单独添加了自己的 Mirror 类型,单独实现。

Mirror 类型其实是为 QuickLookObject 准备的,也就是在 Xcode Playground 中快速查看。

Swift Type Hierarchy ( Swift 类型层次结构 )

声明: 转载请注明,方便的情况下请知会本人. weibo

本文主要介绍 Swift 所有标准库类型的层次结构,及所有标准类型。本文可作为参考手册使用。

本人不保证内容及时性和正确性,请善于怀疑并反馈。谢谢。

本文探索 Swift 所有基础类型和高级类型,以及所有协议和他们之间的继承关系。

为了简化问题,某些类型略去了中间的过渡类型,人肉保证不歧义。

Swift 基础类型

数值类型

Bit

只有一位,实现为 enum.zero.one。简单明了。

协议: RandomAccessIndex IntegerArithmetic

整型

有符号:

1
Int Int8 Int16 Int32 Int64

协议:SignedInteger RandomAccessIndex BitwiseOperations SignedNumber CVarArg

无符号:

1
UInt UInt8 UInt16 UInt32 UInt64

协议:UnsignedInteger RandomAccessIndex BitwiseOperations

别名:

1
2
3
4
5
IntMax = Int64
UIntMax = UInt64
IntegerLiteralType = Int
Word = Int // 字长
UWord = UInt

浮点型

1
Float Double Float80

别名:

1
2
FloatLiteralType = Double
Float64 = Double

协议:FloatingPointNumber

逻辑型

只有一个 Bool

实例: truefalse

协议:LogicValue

只有一个 NilType

唯一实例 nil

字符(串)类型

  • String
  • Character Unicode 字符
  • UnicodeScalar 相当于 C 中的 wchar_t
  • CString 用于表示 C 中的 const char *,请参考相关文章
  • StaticString 静态字符串,内部使用,例如 fatalError

别名:

1
2
StringLiteralType = String
ExtendedGraphemeClusterType = String

官方文档

Character represents some Unicode grapheme cluster as defined by a canonical, localized, or otherwise tailored segmentation algorithm.

String 实现协议:Collection ExtensibleCollection OutputStream TargetStream

Array 类型

  • Array<T>
  • ContiguousArray<T>

实现协议 ArrayType

内部容器:

  • ArrayBuffer<T>
  • ContiguousArrayBuffer<T>

这两个类型看起来是 Array 的内部容器,一般不应该直接使用。

字典类型

Dictionary<KeyType : Hashable, ValueType>

只实现了 Collection

元祖类型

除正常元祖外,还有个特殊的别名

Void = ()

其实很多语言都这么定义的,比如 Haskell 。

Optional 类型

  • Optional<T>T?
  • ImplicitlyUnwrappedOptional<T>T!

实现协议: LogicValue,行为是判断是否为 .None

另外 Swift 的隐式类型转换 有提到,为什么 nil 可以给 Optional 类型赋值的问题。

C/ObjC 兼容类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CBool = Bool
CFloat = Float
CDouble = Double
CChar = Int8
CSignedChar = Int8
CUnsignedChar = UInt8
CChar16 = UInt16
CWideChar = UnicodeScalar
CChar32 = UnicodeScalar
CInt = Int32
CUnsignedInt = UInt32
CShort = Int16
CUnsignedShort = UInt16
CLong = Int
CUnsignedLong = UInt
CLongLong = Int64
CUnsignedLongLong = UInt64

具体使用参考 C 交互的几篇文章,基本没区别。

Any 类型

1
2
3
4
AnyObject
// 别名
Any = protocol<>
AnyClass = AnyObject.Type

还有个用在函数定义的类型签名上, Any.Type

顺便这里看到一个奇异的语法 protocol<>,这个也是 Swift 一种用来表示类型限制的方法,可以用在类型的位置,尖括号里可以是协议的列表。

指针类型

1
2
3
4
5
6
7
8
UnsafePointer<T>
CMutableVoidPointer
CConstVoidPointer
COpaquePointer
CConstPointer<T>
AutoreleasingUnsafePointer<T>
CVaListPointer
CMutablePointer<T>

参考 C 交互文章。

其他辅助类型

多了去了。比如 for-in 实现时候的 Generator 、比如反射时候用的 *Mirror、比如切片操作用的 Range<T>。比如内部储存类。

还有储存辅助类 OnHeap<T> 等等。以后有机会再探索。

Swift 标准库协议

打印相关 Printable DebugPrintable

1
2
3
4
5
6
protocol Printable {
  var description: String { get }
}
protocol DebugPrintable {
  var debugDescription: String { get }
}

用于打印和字符串的 Interpolation 。

*LiteralConvertible

从字面常量获取。

1
2
3
4
5
6
7
ArrayLiteralConvertible
IntegerLiteralConvertible
DictionaryLiteralConvertible
CharacterLiteralConvertible
FloatLiteralConvertible
ExtendedGraphemeClusterLiteralConvertible
StringLiteralConvertible

其中字符串和字符的字面常量表示有所重合,也就是说 "a" 可以是字符串也可以是字符。简析 Swift 中的 Pattern Match 一文中就是遇到了类似的情况。

LogicValue

相当于重载 ifwhile 的行为。

1
2
3
protocol LogicValue {
  func getLogicValue() -> Bool
}

Sequence

相当于重载 for-in 。和 Generator 联用。

1
2
3
4
5
6
7
8
9
protocol Sequence {
  typealias GeneratorType : Generator
  func generate() -> GeneratorType
}

protocol Generator {
  typealias Element
  mutating func next() -> Element?
}
1
2
3
4
5
// for .. in { }
var __g = someSequence.generate()
while let x = __g.next() {
    ...
}}

整型、 Index 相关协议

这些协议都是用来表示容器类型的索引、及相关的索引运算。

这里略去了部分私有内容。略去了 Printable 等。

RawOptionSet 相关协议

一般用来表示二进制的选项,类似于 C enum ,很多 Cocoa 的 flag 被映射到它。相当于一个 Wrapper 的作用。

可以看到要求被 Wrap 的对象支持 BitwiseOperations

Array 相关协议

图中用虚线标注了和 Generator 的关系。

Array<T> 类型实现了 ArrayType 协议。

Dictionary 类型实现了 Collection 协议。

反射相关协议

包括 MirrorMirrorDispositionReflectable

请参考 Swift 的反射

浮点数协议

只有一个 FloatingPointNumber。单独存在。是为了定义完整而存在。看官自己搞定。

IO 输出,伪输出相关

1
2
3
protocol Streamable {
  func writeTo<Target : OutputStream>(inout target: Target)
}

Streamable 表示可以被写入到输出流中,比如字符串、字符等。

1
2
3
protocol OutputStream {
  func write(string: String)
}

OutputStream 表示一个输出流,比如标准输出(stdout),也可以表示一个伪输出流,例如字符串 String

标准输出的获取方法

var stdout = _Stdout()

看起来是私有结构,某一天不能用的话,别怪我。调用时候用 inout 引用语法。

CVarArg 处理

1
2
3
protocol CVarArg {
  func encode() -> Word[]
}

用于处理 C 函数的可变参数,参考 简析 Swift 和 C 的交互,Part 二

Bridge 协议

这里有个疑问就是编译过程中这些 Bridge 协议有没有参与。目前还没办法确定。

  • _BridgedToObjectiveC
  • _ConditionallyBridgedToObjectiveC

具体内容可以参考 Swift 与 Objective-C 之间的交互一文。

其他

Sink 看起来是一个容器,可能是用来编码时使用。

ArrayBufferType 用于表示 ArrayType 的内部储存,看起来似乎也可以直接用。

UnicodeCodec 用于处理编码。有 UTF8UTF16UTF32 可用。

ArrayBound 用来处理数组边界,详细原理和作用过程未知。

总结

无。

参考:

My View of Swift (闲扯对 Swift 语言的看法)

其实这是很早就像要说的了,大概当时信誓旦旦说要看完那本 epub 写个读后感谈谈对 Swift 看法什么的。后来不了了之。 现在觉得这个时机或许差不多,对 Swift 的了解也算凑合了。

纯个人观点。

Swift 是系统编程语言

一开始大家还不太了解的时候,可能会有很多误解。现在好歹一个月了。误解终于少了。

是的, Swift 是系统编程语言,原因是因为它 ABI 兼容 C (不包括 name mangling 部分)。基于强大的 llvm 生成具体平台代码。不是翻译为 Objective-C 的。

编译器参数还显示, Swift 文件的中间编译结果(介于 Swift 代码和 llvm ir )是 SIL ,猜测是 Swift Intermediate Language 。好像和 llvm ir 有所联系。而且至少有两个 stage 。

不是脚本语言,也不是胶水语言。但是它的标准库 (import Swift 库) 几乎不含任何 IO 网络 等内容,随便做个功能强依赖 Cocoa 框架。也可以 import Darwin 用 C 语言的标准库来写。

猜测写个 Python C 模块这种任务是可以轻易胜任的。

而 Golang 、 Rust 本身 ABI 是和 C 不兼容的。虽然 Rust 通过 extern "C" 可以修改单个函数为兼容。

Swift 是现代语言

自动类型推导、泛型、 LLVM 。当然语言研究党都知道这些都是几十年前的“新东西”。

Swift 是半完成品

这么说主要是指 Swift 对 Cocoa 的库实在是太半吊子了。只是 Foundation 有 Bridge 支持,其他库中,明显的列表都无法支持 subscript 、 for-in 这样简单的操作。原因很简单,这些库都是自动转换 ObjC 头文件而来(参考模块那篇文章)。没有额外的封装代码。

所以其实真要用起来,可能会很纠结。或者可以预计很快就有第三方的 Bridge 库给这些类型加上舒服的 Swift 支持。

另外命令行没有静态链接库支持。只能用其他命令拼装。也侧面说明, Apple 希望开发者更多用动态链接库, Framework 。

另外目前的编译器 coredump 、 stackoverflow 太多太多。错哪都不知道。

Swift 隐藏细节太多

就对应到 Foundation 类型这个特性太说,太多黑魔法,隐式类型转换、 BridgeToObjectiveC 协议、指针类型转换。

这些隐藏的特性多少都会成为 Swift 的坑。

要知道定义在 ObjC 库的 NSString 参数某些情况下在 Swift 中被转换为 StringNSArray 都被转换为 AnyObject[]。即使有隐式类型转换,某些极端情况下,还是会有编译时错误。

Swfit 的性能

我没做过测试,但就语言特性来说, Swift 是比 ObjC 快的,因为静态类型使得他在编译时就已经知道调用函数的具体位置。而不是 Objective-C 的消息发送、 Selector 机制。

目前来看, Swift 性能略差原因主要是编译器还没足够优化、还有就是 Cocoa 拖了后腿, Cocoa 本身有大量的 AnyObject 返回值。所以实际写 Swift 代码时,多用 as 一定是好习惯。明确类型。

Swift 的未来

我不知道。至少好像感觉很多培训机构都看到了前途开始疯狂的做视频。

倒是觉得什么时候 Cocoa for Swift 出了才算它完全完成任务。

总觉得 Cocoa 拖后腿,不然放到其他平台也不错。

对了,之前不是在 App 开发领域,这才知道原来这个地盘水很深,太多唯利的培训机构,太多嗷嗷待哺等视频教程的新人。觉得挺有意思。就拿 ? ! 这个 Optional 为例,太多介绍的文章。可惜能说明白的太少太少。糊里糊涂做开发就是当前现状吧。