iOS海马 2019-06-20
作者:shanks
本周大事是第一届中国 Swift 开发者大会。笔者也请假去北京朝圣。由于大神超多,场面很火爆。可以看出随着 Swift 的开源,加入 Swift 大军的程序猿越来越多。大神们的讲座也很精彩。不虚此行。大家可以通过这里查看大会的精彩内容。本周共整理了5个问题。
本周整理问题如下:
Can we expect that one day Swift Compiler will be written in Swift?
Type My Custom Class has no subscript members
Implicitly lazy static members in Swift
How to use Generic type in Swift2
Get nth character of a string in Swift programming language
对应的代码都放到了 github 上,有兴趣的同学可以下载下来研究:点击下载
<!--more-->
国外的程序员比较较真,如果逻辑上说得通的表达,就要问清楚是不是这么回事,楼主的问题是:
官网上有一句话写的是,Swift 出现的目的是为了取代那些基于C语言的系列语言(C,C++,OC)。
而 Swift 编译器是使用 C++ 开发出来的,那么是不是意味着,有一天 Swift 编译器可以用 Swift 来重新实现一遍?
在swift-evolution刚好有一个proposal 提到了这一点,被否了。原因是 LLVM 也是基于 C++ 实现的,除非重写 LLVM,然后用 Swift 把 C++ 的 API 重新实现一遍。做这个还不如做一些优先级高的事情。
楼主想要实现的功能是,定义一个类的构造函数,传入一个字典,使用字典里面的值,赋值给类中的成员变量。但是在赋值语句处报错。
class Product { var name : String! var type: String! var description: String! var taste: String! var picturePath: String! var pairings: [String] var similar: [String] init(dict: Dictionary<String, AnyObject>) { let props = ["name", "type", "description", "taste", "pairings", "similar"] for prop in props { self[prop] = dict[prop] //报错:Type 'Product' has no subscript members } } }
楼下的解答,也比较巧妙,类需要继承 NSObject,也就具有了 OC 的一些特性。在构造函数中使用 Mirror 得到类的结构信息,进一步得到类的成员变量数组,同时使用 oc 的 setValue(,forKey:) 设置对应的成员变量:
class Product : NSObject { var varDump: AnyObject? var name : String? var type: String? // ... init(dict: Dictionary<String, AnyObject>) { super.init() let a = Mirror(reflecting: self).children.filter { $0.label != nil } for b in a { self.setValue(dict[(b.label ?? "")], forKey: (b.label ?? "varDump")) } } } var propertyInit = [String:AnyObject]() propertyInit["name"] = "John" propertyInit["type"] = "Human" var a = Product(dict: propertyInit) print(a.name ?? "Not initalized") // John print(a.type ?? "Not initalized") // Human
楼主的问题是:
如何理解 struct 中的静态变量(static)?
1、在下面的代码中, Baz 类的构造函数只被调用了一次?
2、Foo 结构体中的静态常量 bar 是延迟加载的?
class Baz { init(){ print("initializing a Baz") } } struct Foo { static let bar = Baz() } var z = Foo.bar z = Foo.bar
静态变量(static)本身就是独立于对象存在的。通过类型名来直接访问。且在调用时候只初始化一次,也就是延迟加载的。
由于结构体是值类型,所以可以通过static关键字很方便的实现单例模式。
如果需要绑定到对象,不需要static即可。
struct Foo1 { static let cache = NSCache() // now you might have methods that use this shared `cache` } struct Foo2 { let bar = Baz() } let x = Foo2() let y = Foo2() //x,y 分别调用了 Baz 的构造函数
以下代码会在方法 addNewType 中报错,大家可以不看答案先,想想为啥会报错。这个问题比较初级,但是经常会有人问到。
class TypeA: NSObject { override init() { print("typeA") } } class ObjectA<T: TypeA>: NSObject { var type = [T]() init(type:T) { self.type.append(type) } func addNewType() { let newType = TypeA() self.type.append(newType) // Cannot invoke "append" with an argument list of type '(TypeA)' } }
ObjectA 类定义的泛型 T 遵从类 TypeA,定义的数组是[T],进行append操作时候,不能匹配具体的 TypeA,所以报错。可以把 type 换成 [TypeA], 因为泛型 T 是表示 TypeA 或者 它的子类。所以[TypeA] 可以 append T 或者 TypeA:
class ObjectA1<T: TypeA>: NSObject { var type = [TypeA]() init(type:T) { self.type.append(type) } func addNewType() { let newType = TypeA() self.type.append(newType) // Cannot invoke "append" with an argument list of type '(TypeA)' } }
以下代码会报错,String 不提供直接的下标访问字符串里面的字符了。这对于 C 和 C++ 程序员来说,有点不太适应。
var string = "Hello, world!" var firstChar:Character = string[0] // Throws error
需要自定义subscript来提供对字符串的访问,内部是通过Range来提供范围进行访问,见以下代码:
解法1:
扩展 SequenceType,因为数组的遍历是基于 SequenceType 协议的,所以自然会拥有扩展的方法 frequencies,用来计算元素在数组出现的次数。然后通过 sort 方法输出比较的结果。注意结果数组元素是元组形式。如果想变成与输入相同的结构,要做一下遍历输出。
extension String { subscript (i: Int) -> Character { return self[self.startIndex.advancedBy(i)] } subscript (i: Int) -> String { return String(self[i] as Character) } subscript (r: Range<Int>) -> String { let start = startIndex.advancedBy(r.startIndex) let end = start.advancedBy(r.endIndex - r.startIndex) return self[Range(start: start, end: end)] } } string.characters.first
关于 Swift 2 中对字符串更多的解释,可以查看:https://developer.apple.com/swift/blog/?id=30