Swift是强类型的,尽管只有六种类型。
可能会有疑问,那些基本类型:Bool,Int,UInt, Float, Double, Character, String, Array, Set, Dictionary, Optional。实际上他们都是通过命名类型创建的。
struct、enum和
class。Swift标准库中的绝大多数类型都是struct,甚至Foundation中的一些类也提供了它们在Swift中的struct版本,而class和enum只占很少一部分。
Class,Struct and Enum对比表
| copy by | inheritance | static variable | instance variable | static method | instance method |
|---|---|---|---|---|---|
| Class | Reference | ✅ | ✅ | ✅ | ✅ |
| Struct | Value | ✘ | ✅ | ✅ | ✅ |
| Enum | Value | ✘ | ✅ | ✘ | ✅ |
protocolextension,扩充methodclass来实现。struct或enum来实现。struct Point {
var x: Double
var y: Double
}
这个结构体包含两个名x和y的存储属性。存储属性是被绑定和存储在结构体中的常量或变量。
** 初始化 **
var pointA = Point(x: 10, y: 20)
struct Point {
var x = 0.0
var y = 0.0
}
var pointB = Point()
使用这种方法,必须给每一个属性指定默认值。因为Swift中要求init方法必须初始化自定义类型每个属性。如果无法做到,我们可以自定义逐一初始化方法。
struct Point {
var x : Double
var y : Double
init(_ x : Double = 0.0, y : Double = 0.0) {
self.x = x
self.y = y
}
}
当我们自定义init方法之后,Swift将不会再自动创建逐一初始化方法。
var pointB = Point(200, y: 100)
var pointC = Point(100, y: 200) {
didSet {
print("\(pointC)")
}
}
pointC = pointB
// Point(x: 200.0, y: 100.0)
pointC.x = 200
//Point(x: 200.0, y: 100.0)
通过didSet观察pointC的变化。当修改pointC变量值时,控制台输出Point(x: 200.0, y: 100.0), 但是,修改pointC的修改某个属性,也会触发didSet。
这就是值语义的本质:即使字面上修改了pointC变量的某个属性,但实际执行的逻辑是重新给pointC赋值一个新的Point对象。
struct添加的方法,默认的都是只读的。计算Point之间的距离
extension Point {
func distance(to: Point) -> Double {
let distX = self.x - to.x
let distY = self.y - to.y
return sqrt(distX * distX + distY * distY)
}
}
pointC.distance(to: Point(0, y: 0))
当我们定义一个移动X轴坐标点的方法时,会导致编译错误:
extension Point {
func move(to: Point) {
self = to
}
}
这里提示self is immutable , 必须使用mutating修饰这个方法, Swift编译器就会在所有的mutating方法第一个参数的位置,自动添加一个 inout Self参数。
extension Point {
/* self: inout Self */
mutating func move(to: Point) {
self = to
}
}
以上,是关于Struct类型的基本内容。
init方法的合成规则struct上的表现enum做了诸多改进和增强,它可以有自己的属性,方法,还可以遵从protocol。
colorName枚举
enum ColorName {
case black
case silver
case gray
case white
case red
//.... and so on ....
}
// 也可以写在同一行上,用逗号隔开:
enum Month {
case january, februray, march,
april, may, june, july,
august, september, october,
november, december
}
使用
let black = ColorName.black
let jan = Month.january
注意: 与C和Objective-C不同,Swift的枚举成员在被创建时不会被赋予一个默认的整数值。上面定义的枚举成员是完备的值,这些值的类型就是定义好的枚举ColorName或Month。
func myColor(color: ColorName) -> String {
switch color {
case .black:
return "black"
case .red:
return "red"
default :
return "other"
}
}
注意
- color的类型可以通过
type inference推导出是ColorName。因此,可以省略enum的名字。
- 当Switch...case...将color的所有的值都列举出来时,可以省略
default。
enum默认不会为case绑定一个整数值。但是我们可以手动的绑定值,这个“绑定”来的值,叫做raw values。
enum Direction : Int {
case east
case south
case west
case north
}
现在定义Direction,Swift就会依次把case绑定上值。
let east = Direction.east.rawValue // 0
case绑定不同类型的值,我们管这种值叫做Associated value。
定义了一个表示CSSColor的enum:
enum CSSColor {
case named(ColorName)
case rgb(UInt8, UInt8, UInt8)
}
使用:
var color1 = CSSColor.named(.black)
var color2 = CSSColor.rgb(0xAA, 0xAA, 0xAA)
switch color2 {
case let .named(color):
print("\(color)")
case .rgb(let r, let g, let b):
print("\(r), \(g), \(b)")
}
注意: 提取”关联值“的内容时,可以把let和var写在case前面或者后面。例如:named和rgb。
enum和其他的命名类型一样,也可以采用protocol。
例如: 给CSSColor添加一个文本表示。
extension CSSColor: CustomStringConvertible {
var description: String {
switch self {
case .named(let colorname):
return colorname.rawValue
case .rgb(let red, let green, let blue):
return String(format: "#%02X%02X%02X", red, green, blue)
}
}
}
结果:
let color3 = CSSColor.named(.red)
let color4 = CSSColor.rgb(0xBB, 0xBB, 0xBB)
print("color3=\(color3), color4=\(color4)")
//color3=red, color4=#BBBBBB
注意:COW是特别添加到Swift数组和字典的功能,自定义的数据类型不会自动实现。
class表达的是一个具有明生命周期的对象,我们关心的是类的生命周期。而值类型,我关注的是值本身。
class不会自动生成init方法。如果不定义编译器报错。var a = Circle()
a.radius = 80
var b = a
a.radius = 1000
b.radius // 1000
Circle(定义为Struct)
var a = Circle()
a.radius = 80
var b = a
a.radius = 1000
b.radius // 80
使用值类型创建新对象时,将复制;使用引用类型时,新变量引用同一个对象。这是两者的关键区别。
struct添加的方法,默认的都是只读的。如果要修改必须用mutating来修饰。class中则不同,我们可以直接给 self赋值。class之间可以存在继承关系,因此它的初始化过程要比struct复杂,为了保证一个class中的所有属性都被初始化,Swift中引入一系列特定规则。
class Point2D {
var x : Double
var y : Double
}
这项写是不行了,因为没有定义初始化方法。
class Point2D {
var x : Double = 0
var y : Double = 0
}
let origin = Point2D()
这种方法只能创建一个固定的class。另外一种,添加一个memberwise init 方法
class Point2D {
var x : Double = 0
var y : Double = 0
init(x: Double, y: Double) {
self.x = x
self.y = y
}
}
添加个一个memberwise init方法,我们可以使用
let point = Point2D(x: 1, y: 1)
但是,如果你现在使用
let point = Point2D() // Error
结果会导致编译错误。 因为,我们接手了init的定义后,编译就不会插手init工作。所以,在定义init方法时添加默认参数, 我们称这种初始化为 designated init。
class Point2D {
var x : Double = 0
var y : Double = 0
init(x: Double = 0, y: Double = 0) {
self.x = x
self.y = y
}
}
class Point2D {
var x : Double = 0
var y : Double = 0
init(x: Double = 0, y: Double = 0) {
self.x = x
self.y = y
}
convenience init(at: (Double, Double) ) {
self.init(x: at.0, y: at.1)
}
}
convenience关键字修改;designated init完成对象的初始化;如果直接调用self.x或self.y,会导致编译错误。class Point2D {
// ....
convenience init?(at: (String, String)) {
guard let x = Double(at.0), let y = Double(at.1) else {
return nil
}
self.init(at:(x, y))
}
}
由于String tuple版的init可能失败,所以需要用init?形式定义。在实现里面,如果String无法转换为成Double, 则返回nil。
注意: 严格来说,构造器都不支持返回值。因为构造器本身的作用,只是为了确保对象能被正确构造。因此,return nil表示构造失败,而不能return表示成功。
class Point3D: Point2D {
var z: Double = 0
}
let origin3D = Point3D()
let point31 = Point3D(x: 1, y: 1)
let point33 = Point3D(at: (2, 3)) // 继承基类 convenience init
designated initializer,那么它将自动继承所有基类的designated initializer。designated init,那么它将自动继承基类所有的convenience init。class Point3D: Point2D {
var z: Double
init(x: Double = 0, y: Double = 0, z: Double = 0) {
self.z = z
super.init(x: x, y: y)
}
}
在派生类自定义designated init, 表示明确控制派生类的初始化构造过程, Swift 就不会干涉构造过程。那么,之前创建Point3D就会出现错误。
let point33 = Point3D(at: (2, 3)) // Error
如果想让Point3D从Point2D继承所有的convenience init,只有在派生类中实现所有的designated init方法。
class Point3D: Point2D {
var z: Double
init(x: Double = 0, y: Double = 0, z: Double = 0) {
self.z = z
super.init(x: x, y: y)
}
override init(x: Double, y: Double) {
// 注意先后顺序
self.z = 0
super.init(x: x, y: y)
}
}
此时,就可以正常工作了。只要派生类拥有基类所有的designated init方法,他就会自动获得所有基类的convenience init方法。另外,重载基类convenience init方法,是不需要override关键字修饰的。
您认为可以作为插件很好地存在于您的Rails应用程序中必须实现的哪些行为?您过去曾搜索过哪些插件功能但找不到?哪些现有的Rails插件可以改进或扩展,如何改进或扩展? 最佳答案 我希望在管理界面中看到一个引擎插件,它提供了应用程序中所有模型的仪表板摘要,以及可配置的事件图表。 关于ruby-on-rails-您希望看到哪些Rails插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questio
假设您编写了一个类Sup,我决定将其扩展为SubSup。我不仅需要了解你发布的接口(interface),还需要了解你的私有(private)字段。见证这次失败:classSupdefinitialize@privateField="fromsup"enddefgetXreturn@privateFieldendendclassSub问题是,解决这个问题的正确方法是什么?看起来子类应该能够使用它想要的任何字段而不会弄乱父类(superclass)。编辑:equivalentexampleinJava返回"fromSup",这也是它应该产生的答案。 最佳答案
由于匿名block和散列block看起来大致相同。我正在玩它。我做了一些严肃的观察,如下所示:{}.class#=>Hash好的,这很酷。空block被视为Hash。print{}.class#=>NilClassputs{}.class#=>NilClass为什么上面的代码和NilClass一样,下面的代码又显示了Hash?puts({}.class)#Hash#=>nilprint({}.class)#Hash=>nil谁能帮我理解上面发生了什么?我完全不同意@Lindydancer的观点你如何解释下面几行:print{}.class#NilClassprint[].class#A
在Ruby中,我试图理解to_enum和enum_for方法。在我提出问题之前,我提供了一些示例代码和两个示例来帮助理解上下文。示例代码:#replicatesgroup_bymethodonArrayclassclassArraydefgroup_by2(&input_block)returnself.enum_for(:group_by2)unlessblock_given?hash=Hash.new{|h,k|h[k]=[]}self.each{|e|hash[input_block.call(e)]示例#1:irb(main)>puts[1,2,3].group_by2.ins
我在我的rails应用程序中安装了来自github.com的acts_as_versioned插件,但有一段代码我不完全理解,我希望有人能帮我解决这个问题class_eval我知道block内的方法(或任何它是什么)被定义为类内的实例方法,但我在插件的任何地方都找不到定义为常量的CLASS_METHODS,而且我也不确定是什么here,并且有问题的代码从lib/acts_as_versioned.rb的第199行开始。如果有人愿意告诉我这里的内幕,我将不胜感激。谢谢-C 最佳答案 这是一个异端。http://en.wikipedia
是否有可能以某种方式访问Class.new范围内的a?a=5Class.new{defb;aend}.new.b#NameError:undefinedlocalvariableormethod`a'for#:0x007fa8b15e9af0>#:in`b' 最佳答案 即使@MarekLipka的回答是正确的——改变变量范围总是有风险的。这是可行的,因为每个block都带有创建它的上下文,因此您的局部变量a突然变得不那么局部了——它变成了一个“隐藏的”全局变量:a=5object=Class.new{define_method(
我目前有一个reddit克隆类型的网站。我正在尝试根据我的用户之前喜欢的帖子推荐帖子。看起来K最近邻或k均值是执行此操作的最佳方法。我似乎无法理解如何实际实现它。我看过一些数学公式(例如k表示维基百科页面),但它们对我来说并没有真正意义。有人可以推荐一些伪代码,或者可以查看的地方,以便我更好地了解如何执行此操作吗? 最佳答案 K最近邻(又名KNN)是一种分类算法。基本上,您采用包含N个项目的训练组并对它们进行分类。如何对它们进行分类完全取决于您的数据,以及您认为该数据的重要分类特征是什么。在您的示例中,这可能是帖子类别、谁发布了该项
classFooincludeModule.new{class_eval"deflab;puts'm'end"}deflabsuperputs'c'endendFoo.new.lab#=>mc======================================================================classFooincludeModule.new{instance_eval"deflab;puts'm'end"}deflabsuperputs'c'endend注意这里我把class_eval改成了instance_evalFoo.new.labresc
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我最近开始学习Ruby,这是我的第一门编程语言。我对语法感到满意,并且我已经完成了许多只教授相同基础知识的教程。我已经写了一些小程序(包括我自己的数组排序方法,在有人告诉我谷歌“冒泡排序”之前我认为它非常聪明),但我觉得我需要尝试更大更难的东西来理解更多关于Ruby.关于如何执行此操作的任何想法?
玩转Rails和Controller继承。我创建了一个名为AdminController的Controller,其中一个名为admin_user_controller的子类位于/app/controllers/admin/admin_user_controller.rb这是我的routes.rbnamespace:admindoresources:admin_user#Havetheadminmanagethemhere.endapp/controllers/admin/admin_user_controller.rbclassAdminUserController应用程序/Contr