2015-07-14 08:25:53 oQingQingZiZuo 阅读数 988
  • ASP.NET 开发课程 MVC5 入门篇

    MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。 主讲内容 第一讲 MVC5简介 第二讲 MVC 控制器 第三讲 视图 第四讲 模型 等课程

    13094 人正在学习 去看看 胡延亮

本系列的目的是为了使大家熟悉swift的不同组件以及它们是如何协同工作来提供持久性、可扩展性、高并发的对象存储服务。

Swift Data Model

Openstack Swift结合三个概念为用户提供存储非结构化数据的服务:租户(account)、容器(Container)、对象(Object)。通过使用三者中的一个及多个使系统能够确定数据的唯一存储路径。

/租户

租户存储位置是是名字唯一的存储区域,它包含了租户的元数据信息、该租户的容器列表。需要注意的是,在swift中,一个租户并不是通常所说的用户标示。你可以这么理解,租户就是一块存储区域。

/租户/容器

容器存储位置是用户在租户内部定义的存储区域,用来存储容器的元数据信息以及在容器中存储的对象列表。

/租户/容器/对象

对象存储位置是对象数据及其元数据所存储的位置。


由于各个部分组合在一起构成了存储位置,容器及对象的名称在一个集群中不必是唯一的。如果相同名字的对象被上传到不同的容器或租户区域中,那么它们都有唯一的存储路径。如图所示,对象名称为ObjectBlue的对象存储路径分别为:

/AccountA/Container1/ObjectBlue
/AccountA/Container2/ObjectBlue
/AccountB/Container1/ObjectBlue

我们在以上部分介绍了存储路径及其组成结构,在接下来的部分将分别进行深入介绍。


2017-08-17 18:14:53 wuming_apie 阅读数 1752
  • ASP.NET 开发课程 MVC5 入门篇

    MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。 主讲内容 第一讲 MVC5简介 第二讲 MVC 控制器 第三讲 视图 第四讲 模型 等课程

    13094 人正在学习 去看看 胡延亮

ActiveSQLite 更简单的Swift数据库方案。项目地址:https://github.com/KevinZhouRafael/ActiveSQLite

——————————————————————————————————

在做swift数据库的时候,选择并不多,有三种:CoreData,SQLite,Realm。很多喜欢用SQL的开发者在Objective-c中使用FMDB,而在swift中,star最多的SQLite框架就是SQLite.swift,其star数量也才4k+,但是没有更好的选择了。SQLite.swift功能很强大,但是有几点非常不方便:

1,要写Expression表达式。
2,不能自动包装成对象。

参考如下代码:

let users = Table("users")
let id = Expression("id")
let email = Expression("email")

for stmt in try! db.prepare(users.select(id, email)) {
    print("id: \(stmt[id]), email: \(stmt[email])")
}

1、在创建表,增删查改表都会用到Expression表达式,一般我们通常将表达式作为类的静态属性,这样统一管理。
2、读取stmt的数据只能显式的通过Expression表达式或者索引,这样在修改表的时候也是非常麻烦的,也很容易弄错。

为了让SQLite.swift更加好用,我写了一个框架ActiveSQLite,是对SQLite.swift的封装。主要特性:

1、自动创建表。

2、增删查改支持String和key-value的字典。

3、自动把查询结果包装为model。

4、更方便的事务和异步,模型与数据库表映射,日志,简便的数据库升级。

写的过程参考了MagicalRecordActiveRecord。易用性达到Realm的级别。

——————————————————————————————————————
具体介绍如下:

特性

  • 支持 SQLite.swift 的所有特性。
  • 自动创建表. 自动创建 id , created_at 和 updated_at 列。
  • 自动把SQL查询的数据赋值给数据库模型DBModel的属性。
  • 自定义表名和模型名之间的映射,列名和模型的属性名之间的映射。
  • 支持事务和异步。
  • 提供可扩展,链式,延迟执行的查询接口。
  • 通过属性名字符串,字典,或SQLite.swift的表达式Expression查询和修改数据。
  • 日志级别

例子

执行 ActiveSQLiteTests target.

用法

import ActiveSQLite

//定义model和table
class Product:DBModel{

    var name:String!
    var price:NSNumber!
    var desc:String?
    var publish_date:NSDate?

}

//保存
let product = Product()
product.name = "iPhone 7"
product.price = NSNumber(value:599)
try! product.save()

//查询
let p = Product.findFirst("name",value:"iPhone") as! Product

//or 
let name = Expression<String>("name")
let p = Product.findAll(name == "iPhone")!.first as! Product                    
//id = 1, name = iPhone 7, price = 599, desc = nil,  publish_date = nil, created_at = 1498616987587.237, updated_at = 1498616987587.237, 

//更新
p.name = "iPad"
try! p.update()

//删除
p.delete()

开始

在你的工程的target使用ActiveSQLite, 需要首先导入 ActiveSQLite 模块.

import ActiveSQLite

连接数据库

DBConfigration.dbPath = "..."

如果你没有设置dbPath,那么默认的数据库文件是在documents目录下的ActiveSQLite.db。

支持的数据类型

ActiveSQLite
Swift Type
SQLite.swift
Swift Type
SQLite
SQLite Type
NSNumber Int64 INTEGER
NSNumber Double REAL
String String TEXT
nil nil NULL
SQLite.Blob BLOB
NSDate Int64 INTEGER

NSNumber类型对应SQLite.swift的两种类型(Int64和Double)。NSNumber默认的映射类型是Int64。重写DBModel的doubleTypes()方法能标记属性为Double类型。

class Product:DBModel{

    var name:String!
    var price:NSNumber!
    var desc:String?
    var publish_date:NSDate?

  override func doubleTypes() -> [String]{
      return ["price"]
  }

}

ActiviteSQLite映射NSDate类型到SQLite.swift的Int64类型。 你可以通过查找SQLite.swift的文档Custom Types of Documentaion映射NSDate到String。

创建表

ActiveSQLite自动创建表并且添加”id”, “created_at”和 “updated_at”字段。”id”字段是主键。 创建的代码类似于下面这样:

try db.run(products.create { t in      
    t.column(id, primaryKey: true)
    t.column(Expression<NSDate>("created_at"), defaultValue: NSDate(timeIntervalSince1970: 0))  
    t.column(Expression<NSDate>("updated_at"), defaultValue: NSDate(timeIntervalSince1970: 0))  
    t.column(...)  

})                             

// CREATE TABLE "Products" (
//      "id" INTEGER PRIMARY KEY NOT NULL,
//      created_at INTEGER DEFAULT (0),
//      created_at INTEGER DEFAULT (0),
//     ...
//  )

“created_at”和”updated_at”字段的单位是毫秒ms。

映射

你可以自定义表的名字, 列的名字,还可以设置瞬时属性不存在数据库中。

1. 映射表名

默认的表名和类名相同。

//设置表名为 "ProductTable"
override class var nameOfTable: String{
    return "ProductTable"
}

2. 映射列名

默认的列名和属性名相同。

//设置"product"属性映射的列名为"product_name"
//设置"price"属性映射的列名为"product_price"
override class func mapper() -> [String:String]{
    return ["name":"product_name","price":"product_price"];
}

3. 瞬时属性。

瞬时属性不会被存在数据库中。

override class func transientTypess() -> [String]{
    return ["isSelected"]
}

ActiveSQLite 仅仅保存三种属性类型 (String,NSNumber,NSDate)到数据库。 如果属性不是这三种类型,那么不会被存入数据库,它们被当做瞬时属性看待。

表约束

如果你要自定义列, 你仅需要实现CreateColumnsProtocol协议的createColumns方法,那么ActiveSQLite就不会自动创建列。写自己的建列语句,要注意列名和属性名必须一致,否则不能自动从查询sql封装数据库模型对象。


class Users:DBModel,CreateColumnsProtocol{
    var name:String!
    var email:String!
    var age:Int?

    func createColumns(t: TableBuilder) {
        t.column(Expression<NSNumber>("id"), primaryKey: true)
        t.column(Expression<String>("name"),defaultValue:"Anonymous")
        t.column(Expression<String>("email"), , check: email.like("%@%"))
    }
}

更多信息查考SQLite.swift的文档table constraints document

插入记录

有三个方法用来插入记录。

插入一条。

func insert()throws ;

插入多条。

class func insertBatch(models:[DBModel])throws ;

保存方法。

如果数据库模型对象的 id == nil,那么执行插入。如果id != nil那么执行更新语句。

func save() throws;

例如:

let u = Users()
u.name = "Kevin"
try! u.save()

var products = [Product]()
for i in 1 ..< 8 {
    let p = Product()
    p.name = "iPhone-\(i)"
    p.price = NSNumber(value:i)
    products.append(p)
}

try! Product.insertBatch(models: products)

更多信息可以看ActiveSQLite的源码和例子, 也可以查阅SQLite.swift的文档Inserting Rows document

更新记录

有三种更新策略。

1. 通过改属性值

首先修改属性的值,然后执行save() 或者 update() 或者 updateBatch()。

p.name = "zhoukai"
p.save()

2. 通过属性名字符串和属性值

//更新一条
u.update("name",value:"3ds")
u.update(["name":"3ds","price":NSNumber(value:199)])


//更新多条
Product.update(["name": "3ds","price":NSNumber(value:199)], where: ["id": NSNumber(1)])

2. 通过SQLite.swift的Setter

//更新一条记录
p.update([Product.price <- NSNumber(value:199))

//更新多条
Product.update([Product.price <- NSNumber(value:199), where: Product.name == "3ds")

了解更多请看ActiveSQLite的源码和例子, 查看SQLite.swift的文档Updating Rows document , Setters document

查询记录

使用findFirst方法查询一条记录,使用findAll方法查询多条记录。

方法名前缀是”find”的是类方法,这种方法一次性查询出结果。

1.通过属性名字符串和属性值查询

let p = Product.findFirst("name",value:"iWatch") as! Product

let ps = Product.findAll("name",value:"iWatch",orders:["price",false]) as! [Product]

2.通过SQLite.swift的Expression查询

let id = Expression<NSNumber>("id")
let name = Expression<String>("name")

let arr = Product.findAll(name == "iWatch") as! Array<Product>

let ps = Product.findAll(id > NSNumber(value:100), orders: [Product.id.asc]) as! [Product]

链式查询

链式查询方法是属性方法。

let products = Product().where(Expression<NSNumber>("code") > 3)
                                .order(Product.code)
                                .limit(5)
                                .run() as! [Product]

不要忘记执行run()。

更多复杂的查询参考ActiveSQLite的源码和例子。和SQLite.swift的文档Building Complex Queries

表达式Expression

SQLite.swift再更新update和查询select操作中,使用表达式Expression转换成SQL的’where’判断,。更多复杂的表达式用法,参考文档filtering-rows

删除记录

//1. 删除一条
try? product.delete()

//2. 删除所有
try? Product.deleteAll()

//3. 通过表达式Expression链式删除。
try? Product().where(Expression<NSNumber>("code") > 3)
                                .runDelete()

事务

建议把所有的insert,update,delete操作和alter表的代码全部放在ActiveSQLite.save代码块中。一个块中的sql操作在同一个事务当中。

 ActiveSQLite.save({ 

                var products = [Product]()
                for i in 0 ..< 3 {
                    let p = Product()
                    p.name = "iPhone-\(i)"
                    p.price = NSNumber(value:i)
                    products.append(p)
                }
                try Product.insertBatch(models: products)


                let u = Users()
                u.name = "Kevin"
                try u.save()


            }, completion: { (error) in

                if error != nil {
                    debugPrint("transtion fails \(error)")
                }else{
                    debugPrint("transtion success")
                }

            })

异步

ActiveSQLite.saveAsync是一个异步的操作,当然代码块中的sql也在同一个事务当中。

 ActiveSQLite.saveAsync({ 
            .......

            }, completion: { (error) in
                ......
            })

改变表结构

重命名表和添加列

第1步. 用新的表名做映射,添加新的属性。

class Product{
    var name:String!

    var newColumn:String!
    override class var nameOfTable: String{
        return "newTableName"
    }

}

Step 2. 当数据库版本改变时候,执行修改表名和添加列sql,并放在同一个事务中。

let db = DBConnection.sharedConnection.db
            if db.userVersion == 0 {
                ActiveSQLite.saveAsync({
                    try Product.renameTable(oldName:"oldTableName",newName:"newTableName")
                    try Product.addColumn(["newColumn"])

                }, completion: { (error) in
                    if error == nil {

                        db.userVersion = 1
                    }
                })

            }             

更多SQLite.swift的修改表信息参看 Altering the Schema

索引

    let name = Expression<String>("name")
    Product.createIndex(name)
    Product.dropIndex(name)

更多信息查看 Indexes of SQLite.swift Document

删除表

Product.dropTable()

日志

有四种日志级别,分别是: debug,info,warn,error。
默认的日志级别是info。像这样来设置日志级别:

//1. 设置日志级别
DBConfigration.logLevel = .debug

//2. 设置数据库路径
DBConfigration.dbPath = "..."

保证首先设置日志级别,后设置数据库路径。

硬件需求

  • iOS 8.0+
  • Xcode 8.3.2
  • Swift 3

安装

Cocoapods

再Podfile文件中添加:

pod "ActiveSQLite"

作者

Rafael Zhou

License

ActiveSQLite is available under the MIT license. See the LICENSE file for more info.

2018-01-19 09:11:48 mn704058053 阅读数 661
  • ASP.NET 开发课程 MVC5 入门篇

    MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。 主讲内容 第一讲 MVC5简介 第二讲 MVC 控制器 第三讲 视图 第四讲 模型 等课程

    13094 人正在学习 去看看 胡延亮
 1.首先创建一个Person的类 继承 NSObject类

//
//  Person.swift
//  006-Swift中设置模型数据
//
//  Created by 庄壮勇 on 2018/1/10.
//  Copyright © 2018年 Personal. All rights reserved.
//

import UIKit

class Person: NSObject {
    var name: String?
}
2.创建一个DemoLabel 类 继承于 UILable类
//
//  DemoLabel.swift
//  006-Swift中设置模型数据
//
//  Created by 庄壮勇 on 2018/1/10.
//  Copyright © 2018年 Personal. All rights reserved.
//

import UIKit

class DemoLabel: UILabel {

    // 模型 -> 给视图设置模型,由视图自己根据模型的数据,决定显示内容
    var person: Person? {
        // *** 就是替代 OC 中重写 setter 方法
        // 区别: 再也不需要考虑 _成员变量 = 值
        // OC 中如果是copy属性,应该 _成员变量 = 值.copy;
        didSet{
            // 此时 name 属性已经有值,可以直接使用设置UI内容
            text = person?.name
        }
    }
}
3.ViewController
//
//  ViewController.swift
//  006-Swift中设置模型数据
//
//  Created by 庄壮勇 on 2018/1/10.
//  Copyright © 2018年 Personal. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let p = Person()
        p.name = "小花"
        let label = DemoLabel(frame: CGRect(x: 20, y: 40, width: 100, height: 40))
        view.addSubview(label)
       
        // 将模型设置给label
        label.person = p
    }
}

2015-04-24 19:52:50 qq_19762007 阅读数 2267
  • ASP.NET 开发课程 MVC5 入门篇

    MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。 主讲内容 第一讲 MVC5简介 第二讲 MVC 控制器 第三讲 视图 第四讲 模型 等课程

    13094 人正在学习 去看看 胡延亮

Swift 构造器探究

什么时候要用构造器?

对于类(Class)

其实在其他语言中,比如说Java对于属性的初始化没有严格的要求。甚至在Model层只有对应属性的get,set访问器。而在Swift中无论是对于结构体(Structure)还是类(Class),如果其中存在存储属性(stored property),那么必须在合适的地方给它赋初始值,也就是初始化。不能让它们成为不确定的状态,即没有初始化。关于初始化,Swift提供了两种方式。一种是属性定义的时候初始化,也就是赋默认值。 第二种是在构造器中初始化。这里做个小结:存储属性必须初始化,初始化的方式且只能在以上两种方式选择,并且至少包含其中一种。

而对于第一种,在Swift中又有两种初始化方式。第一种,是给予明确的值。比如说var name = "Bob"Swift的数据类型推断机制会自动推断出name是一个String类型变量,初始值为Bob。第二种,是Optional类型初始化,通常用在这个属性在程序运行过程中可能存在值也可能不存在值的时候。比如说var avatar: UIImage?一个人可能没有头像。假设avatarPerson类的一个存储属性,程序在运行过程,如果Person类创建一个实例let myPerson = Person(), 如果没有给myPerson.avatar赋值,那么myPersonavatar属性自动初始化为nil。如果myPerson.avatar = UIImage(named: "prettyGirl"),那么avatar属性初始化就是UIImage(named: "prettyGirl")

而对于第二种初始化方式,更加具体的说,是如果没有是实现第一种初始化方式的时候,必须实现的。也就是说如果你定义一个属性var avatar: UIImage既没有给予明确的初始值,也没有让它成为Optional类型,那么必须实现在构造器中的初始化。这就是什么时候要用构造器的重点了。
然而Swift的构造器又有两种,一种是designated构造器,一种是convenience构造器。所有designated构造器都必须初始化那些没有满足第一种情况的存储属性。注意这里是类中所有的designated构造器都必须要做这件事。具体怎么做请看后文Designated构造器

对于结构体(Structure)

Swift中结构体和类的构造器其实差不多。除了结构体中没有析构器(Deinitializer),不能够继承(inherit)以及结构体有memberwise构造器外大体上是一致的。所以你有时候看到一个结构体struct Point有两个存储属性var x: Double, var y: Double ,却没有任何构造器,但是他们既不符合类中讨论的第一种初始化方法(即赋默认值)。那么它们违背了语法规则吗?其实不是的,如果结构体没有自定义的构造器,Swift隐式创建了一个init(x:y:)的构造器其内容就相当于self.x = x, self.y = y。如果你手动给结构体加个空内容的构造器init(){ },编译器就报Return from initializer without initializing all stored properties的错误。也就是说,如果你创建了你自己定义的构造器,Swift就默默地帮你把memberwise构造器去掉了,而你自己定义的构造器又没有对存储属性初始化,那么这违背了语法规则。但是如果你想同时拥有这两个构造器(memberwise构造器和自定义构造器),你可以把自定义的构造写到Extension Point{ // custom initializer }里面进去。

构造器的继承

designated构造器

designated构造器在Swift中很常见,顾名思义这个构造器就是你类中所有构造器的“原型”。在这个构造器中只调用父类的designated构造器或者不调用其他任何构造器称为designated构造器。每个类都必须至少有一个designated构造器,但是你会看到有些情况看不见类中声明designated构造器,那是因为它是一个子类,如果不写任何designated构造器,将会自动继承父类所有designated构造器。我们将在下面的自动构造器继承中详细讲到。

    init(parameters){
        // statments
    }

convenience构造器

convenience构造器是第二种构造器。它主要是横向代理,就是说在convenience构造器中一定存在也只能存在该类的一个构造器通常用self.init(parameters)调用该类的一个构造器。当然convenience构造器不是必要的。

    convenience init(parameters){
        // 调用该类中的一个构造器
        self.init(parameters)

        // customize properties
    }

类的构造器代理规则

  • 规则1:子类中的designated构造器必须调用最近一级父类的desigated构造器
  • 规则2:convenience构造器必须在同一个类中调用其他一个构造器
  • 规则3:convenience构造器通过调用链(代理链)调用一个designated构造器

总结下也就说

  • desingated构造器必须一直向上代理(即调用最近一级父类的designated构造器)
  • convenience构造器必须横向代理,且代理终点为一个desingated构造器

下面这幅图(引用自苹果官方文档原图)就表明了这两点
构造器代理图
SubClass的convenience构造器调用了第二个designated构造器(符合规则2),第二个designated的调用了SuperClass的designated构造器(符合规则1),这表明了convenience构造器最终调用的是designatedg构造器(符合规则3)。同理其他的构造器调用亦是如此

下面再来一幅图(引用自苹果官方文档原图)

Two-Phase初始化

第一阶段类中每个存储属性必须有初始值,一旦每个储值属性的初始状态被确定了,第二阶段就开始了。第二阶段就是在新的实例可用之前对初始值的修改阶段。利用两阶段初始化可以让初始化安全,防止属性值在初始化完成之前被访问,以及属性值被另外的构造器设置为不同的值。这和OC差不多,唯一区别就是OC在第一阶段初始的默认值只能是0或者是nil

初始化有安全检查机制

  • 安全检查1:一个designated构造器在向上代理之前必须初始化该类中定义的所有存储属性
  • 安全检查2:一个designated构造器必须先向上代理调用一个父类的构造器,在修改父类的属性值之前。如果不这样,那么你修改的属性值,会被父类的构造器初始化属性的时候覆盖掉。
  • 安全检查3:一个convenience构造器在对本类任何属性操作之前(包括父类的属性以及本类定义的属性)必须调用另外一个本类中的构造器。如果不是这样,那么修改完的属性很可能就被本类中的构造器初始化属性的时候覆盖了。
  • 安全检查4:一个构造器不能调用实例方法,读取任何实例属性的值,或者用作为一个值指向self知道第一阶段初始化结束

初始化两个阶段

第一阶段:

  • 一个designated或者convenience构造器在类中被调用
  • 类的新实例向系统申请内存空间,但是内存还没有被初始化
  • 该类的一个designated构造器确认所有在该类中定义的存储属性已经被初始化。这些属性的内存被初始化。
  • designated的构造器告知父类构造器对父类自己的属性执行相同的操作
  • 一直代理到类继承链的最高级
  • 一旦到达类继承链的最高级,并且链上的最终类确认了它所有的存储属性已经初始化完毕,则实例内存被完全初始化,然后第一阶段到此就完成了。

下面这幅图是第一阶段

第二阶段:

  • 从继承链的最顶端开始每个designated的构造器可以修改实例中的属性值,此时构造器也可以访问self指针了,可以调用实例方法等等。
  • 最后在继承链上的任何convenience构造器可以用self指针来修改实例

下面这幅图是第二阶段

构造器的继承和重写

Swift和绝大多数面向对象的语言不一样,它不会默认继承所有父类的构造器。也就是说你用类初始化实例的时候,只能访问到该子类构造器(除了自动构造器继承的情况)。这是因为防止用父类的构造方法来创建一个比父类多出一些属性的子类。然而如果调用父类构造方法,就不能初始化子类中的存储属性了,这样就违背了Swift的安全机制。

注意:父类中的构造器在某些特殊情况也会被自动继承,详细内容在后面的自动构造器继承会讲到

如果你在子类中写了一个构造器,并且它与父类某个designated构造器的名字相同,那么你必须用override关键字修饰init构造器。这里还得注意,就算你父类没有任何显式的构造器(但是Swift会默认给你一个init(){ // 空 }构造器),子类在重写init() { // customize }的时候也是要加修饰词override的,就是这样override init() { // customize }

注意:就算你在子类中写的是convenience类型的构造器,如果名字和父类的某个designated构造器,也必须加关键字override修饰。

还有一种情况就是如果你子类中一个构造器的名字和父类的某个convenience构造器相同,又因为父类中的convenience构造器是没办法被子类直接调用的(这个在上面类的构造器代理规则中讲到过)。因此,严格地说,子类的convenience构造器不提供对父类convenience的重写,也就不需要用override修饰了。这个构造器就相当于新的自定义构造器,不和父类产生任何联系。

好吧讲了这么多理论估计你们都晕乎乎的,来讲点实际的例子把。

    class Vehicle {
        var numberOfWheels = 0
        var description: String {
            return "\(numberOfWheels)wheel(s)"
        }
    }

这是一个基类。numberOfWheels有了默认的初始值,也算是初始化的一种方式。description是一个计算属性,计算属性不需要初始化。虽然看到这个类没有任何的构造器,但是前面提到过默认构造器,就是一个空的构造器init() { //空 } 下面我们来创建一个实例

    let vehicle = Vehicle()
    println("Vehicle: \(vehicle.decription)")

这个时候控制台打印Vehicle: 0 wheel(s)

下面我们来创建一个子类Bicycle

    class Bicycle: Vehicle{
        override init(){
            super.init()
            numberOfWheels = 2
        }
    }

由于Bicycle类继承Vehicle类,定义了一个init()designated构造器,这个构造器名字和Vehicle类中的默认构造器相同,因此需要重写override。重写中,首先调用父类的super.init(),然后在修改父类属性的值。这符合Two-Phase初始化规则,在修改父类属性之前,先调用父类的designated的构造器,以保证父类的所有属性被初始化,并且防止修改的值被构造器的初始化过程给覆盖了。

下面我们来创建Bicycle子类的实例

    let bicycle = Bicycle()
    println("Bicycle: \(bicycle.description)")

控制台打印:Bicycle: 2 wheel(s)
分析:由于子类修改了父类中的numberOfWhiles属性,因此打印结果是2

注意:子类可以在合适的位置(具体是什么位置看Two-Phase初始化中介绍)对父类的属性在初始化的时候修改,但是如果属性是常量let声明的,这是不允许的,因为let属性常量一旦被初始化赋值之后,就再也不能改了。

自动构造器继承

上面很多地方我们提到了自动构造器继承,那么这东西到底是什么呢?一般情况下,子类如果定义了自己的designated的构造器,父类的构造器是不会被继承的。但是,如果子类没有定义任何自己的构造器,那么子类就会继承父类的构造器。
假设你给你子类中任何新的属性赋予了默认值,那么一下两条规则将会被适用:

  • 规则1:如果你的子类没有定义任何designated构造器,那么它会自动继承父类的所有designated构造器。
  • 规则2:如果你子类提供实现了所有父类的designated的构造器(无论是通过规则1实现的,还是提供自定义手动实现的)都会继承父类所有的convenience构造器。

即使你在子类中添加了其他convenience构造器,这些规则也是适用的。

注意上面所说的提供自定义手动实现,也可以包括子类中用convenience构造器覆盖父类的designated构造器。比如说父类有个init(original:)构造器,子类用override convenience init(original:)也是可以使之满足规则2的

这个是有点抽象,我们来举个例子把
先定义一个基类Food

    class Food {
        var name: String
        init(name: String) {
            self.name = name
        }
        convenience init() {
            self.name(name: "[Unnamed]")
        }
    }

这里init(name:)构造器是Food类的designated构造器,init()构造器是Food类的convenience构造器,里面调用了该类的init(name:)designated构造器

现在利用designated构造器创建一个实例

    let namedMeat = Food(name: "Bacon")

现在创建了一个实例叫namedMeat,它的name属性值为"Bacon"

现在利用convenience构造器创建一个实例

    let mysteryMeat = Food()

现在创建了一个实例叫mysteryMeat,它的name属性值为"[Unnamed]"

现在我们创建一个RecipeIngredient类,继承Food类

    class RecipseIngredient: Foof {
        var quantity: Int
        init(name: String, quantity: Int){
            self.quantity = quantity
            super.init(name: name)

            // customize superclass properties if needed
        }

        override convenience init(name: String) {
            self.init(name: name, quantity: 1)
        }
    }

这里我们定义了RecipeIngredient类,它继承了Food类。其中init(name:quantity)是它的designated构造器。init(name:)是convenience构造器,并且是重写了父类(Food类)的init(name:)designated构造器

构造器继承关系图

接下来我会结合Two-Phase初始化机制来讲解这个RecipeIngredient类的初始化过程。

首先明确的是,RecipeIngredient类只有一个designated构造器叫做init(name: String, quantity: Int)。当这个类初始化实例的时候后,如果使用这个构造器。首先,要满足类的代理规则1,在designated构造器中必须调用最近父类的designated的构造器。但是要先满足安全检查第一个步骤,在调用父类designated构造器之前,必须先初始化改类中定义的所有存储属性。所以对quantity属性进行初始化self.qunatity = qunatity。初始化完成之后,调用父类(Food类)的super.init(name:)构造器进行对父类属性的初始化,由于该例子中Food类已经是构造(代理)链的最上级了,所以在这里停止向上代理。等Food类属性初始化完成之后,返回到RecipeIngredient类中(就是执行完了super.init(name:)),如果你想再对父类属性进行修改,那么可以在这个语句后面自定义修改。这符合安全检查机制3.

现在我们来用不同的构造器创建几个实例

    let oneMysteryItem = RecipeIngredient()
    let oneBacon = RecipeIngredient(name: "Bacon")
    let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)

现在实例oneMysteryItem调用的是init()构造器。但是我们又没有在RecipeIngredient类中看到定义,而且有了designated构造器之后Swift不会给你默认创建init()构造器了。其实init()来自父类Food。因为子类实现了父类所有的designated构造器(即init(name: String)构造器),因此根据自动构造器继承的第二条规则,子类将继承父类的所有convenience构造器,也就是init()构造器。这个init()和内容和父类基本一致,只不过内部的self.init(name:String)调用的是RecipeIngredient类的,而不是父类的。也就是说现在oneMysteryItemquantity属性值为1,name属性值为“[Unnamed]”。而实例oneBacon调用的是RecipeIngredient的init(name: String)convenience构造器。所以它的name属性的值为"Bacon"quantity属性的值为1。sixEggs实例调用的是RecipeIngredient的init(name: String, quantity: Int)designated构造器,因此name属性的值为"Eggs"quantity属性的值为6

再来一个更加特殊的类ShopListItem

    class ShoppingListItem: RecipeIngredient {
        var purchased = false
        var description: String{
            var output = "\(quantity)x \(name)"
            output += purchased ? "√" : "×"
            return output
        }
    }

你可以看到这个类没有任何构造器对purchased的初始化,而是给予了一个默认值false因为一件物品默认是没有购买的。
由于它没有提供任何构造器,于是符合自动构造器继承的第一条规则,它将自动继承父类RecipeIngredient的所有designated的构造器,又因为如此,也就说子类是吸纳了父类的所有desigated的构造器,自动构造器继承的第二条规则也将适用,也就是自动继承父类的所有convenience构造器。下面就是整个继承图。

现在我们创建个实例数组

    var breakfastList = [
        ShoppingListItem(), 
        ShoppingListItem(name: "Bacon"), 
        ShoppingListItem(name: "Eggs", quantity: 6)
    ]
    breakfastList[0].name = "Orange juice"
    breakfastList[0].purchased = true
    for item in breakfastList {
        println(item.decription)
    }

控制台输出结果

1 x Orange juice √

1 x Bacon ×

6 x Eggs ×

这里第一个item是ShoppingListItem,它调用的是init()构造器是从Food一直继承下来的,然而在RecipeIngredient层,将它初始化的内容改为了self.init(name: name, quantity: 1)。最终到了ShoppingListItem这个类中,初始化的内容依然是self.init(name: name, quantity: 1)这个,只不过这个self不指向RecipeIngredient,而是指向ShoppingListItem。因此初始化完毕之后第一个item的name属性的值为“Unnamed”quantity属性的值为1purchased的属性的值为了false。但是在后来breakfastList[0]把属性name值改为了Orange juice,把purchased的属性的值改为了true。所以最后的decription1 x Orange juice √ 而其他初始化内容应该不难理解,我这里也不再重复叙述了。

2018-04-03 00:15:57 qq_30211165 阅读数 243
  • ASP.NET 开发课程 MVC5 入门篇

    MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。 主讲内容 第一讲 MVC5简介 第二讲 MVC 控制器 第三讲 视图 第四讲 模型 等课程

    13094 人正在学习 去看看 胡延亮
 let Userdefault = UserDefaults.standard;

//存


        //存
        Userdefault.set(true, forKey: "isSwitchState");
        //Userdefault.set("sssss字符串", forKey: "string");
        Userdefault.synchronize();

//取

  let isswitch = Userdefault.bool(forKey: "isSwitchState");
        //let string = Userdefault.url(forKey: "string");

swift 框架大全

阅读数 2615

没有更多推荐了,返回首页