• Swift

    2018-10-30 14:59:57
    1     ...OpenStack Object Storage(Swift)是...Swift是文件系统或者实时的数据存储系统,它是对象存储,用于永久类型的静态数据的长期存储,这些数据可以检索、...

    1       概述

    OpenStack Object StorageSwiftOpenStack开源云计算项目的子项目之一。Swift的目的是使用普通硬件来构建冗余的、可扩展的分布式对象存储集群,存储容量可达PB级。

    Swift并不是文件系统或者实时的数据存储系统,它是对象存储,用于永久类型的静态数据的长期存储,这些数据可以检索、调整,必要时进行更新。最适合存储的数据类型的例子是虚拟机镜像、图片存储、邮件存储和存档备份。

    Swift无需采用RAID(磁盘冗余阵列),也没有中心单元或主控结点。Swift通过在软件层面引入一致性哈希技术和数据冗余性,牺牲一定程度的数据一致性来达到高可用性(High Availability,简称HA)和可伸缩性,支持多租户模式、容器和对象读写操作,适合解决互联网的应用场景下非结构化数据存储问题。

    2       技术特性

    2.1        Swift的主要特征

    Swift的主要特性如下:

    • 极高的数据持久性(Durability)。
    • 完全对称的系统架构:“对称”意味着Swift中各节点可以完全对等,能极大地降低系统维护成本。
    • 无限的可扩展性:一是数据存储容量无限可扩展;二是Swift性能(如QPS、吞吐量等)可线性提升。
    • 无单点故障:Swift的元数据存储是完全均匀随机分布的,并且与对象文件存储一样,元数据也会存储多份。整个Swift集群中,也没有一个角色是单点的,并且在架构和设计上保证无单点业务是有效的。
    • 简单、可依赖。

    2.2        Swift和HDFS的技术差异

    Swift和Hadoop分布式文件系统(HDFS)都有着相似的目的:实现冗余、快速、联网的存储,它们的技术差异如下:

    • 在Swift中,元数据呈分布式,跨集群复制。而在HDFS使用了中央系统来维护文件元数据(Namenode,名称节点),这对HDFS来说无异于单一故障点,因而扩展到规模非常大的环境显得更困难。
    • Swift在设计时考虑到了多租户架构,而HDFS没有多租户架构这个概念。
    • 在Swift中,文件可以写入多次;在并发操作环境下,以最近一次操作为准。而在HDFS中,文件写入一次,而且每次只能有一个文件写入。
    • Swift用Python来编写,而HDFS用Java来编写。
    • Swift被设计成了一种比较通用的存储解决方案,能够可靠地存储数量非常多的大小不一的文件;而HDFS被设计成可以存储数量中等的大文件(HDFS针对更庞大的文件作了优化),以支持数据处理。

    3       关键技术

    3.1        一致性哈希(ConsistentHashing)

    在分布式对象存储中,一个关键问题是数据该如何存放。Swift是基于一致性哈希技术,通过计算可将对象均匀分布到虚拟空间的虚拟节点上,在增加或删除节点时可大大减少需移动的数据量;虚拟空间大小通常采用2的n次幂,便于进行高效的移位操作;然后通过独特的数据结构 Ring(环)再将虚拟节点映射到实际的物理存储设备上,完成寻址过程。


    图1 一致性哈希环结构

    衡量一致性哈希的4个指标:

    • 平衡性(Balance):平衡性是指Hash的结果能够尽可能分布均匀,充分利用所有缓存空间。
    • 单调性(Monotonicity):单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。
    • 分散性(Spread):分散性定义了分布式环境中,不同终端通过Hash过程将内容映射至缓存上时,因可见缓存不同,Hash结果不一致,相同的内容被映射至不同的缓冲区。
    • 负载(Load):负载是对分散性要求的另一个纬度。既然不同的终端可以将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。

    Swift使用该算法的主要目的是在改变集群的node数量时(增加/删除服务器),能够尽可能少地改变已存在key和node的映射关系,以满足单调性。

    考虑到哈希算法在node较少的情况下,改变node数会带来巨大的数据迁移。为了解决这种情况,一致性哈希引入了“虚拟节点”(vnode,也称为partition)的概念: “虚拟节点”是实际节点在环形空间的复制品,一个实际节点对应了若干个“虚拟节点”,“虚拟节点”在哈希空间中以哈希值排列。

    总的来说,Swift中存在两种映射关系,对于一个文件,通过哈希算法(MD5)找到对应的虚节点(一对一的映射关系),虚节点再通过映射关系(ring文件中二维数组)找到对应的设备(多对多的映射关系),这样就完成了一个文件存储在设备上的映射。


    图2 对象、虚结点、节点间的映射关系

    在设置虚结点数的时候,需要对系统预期的规模做充分考虑,假如集群的规模不会超过6000个结点,那么可以将虚结点数设置为结点数的100倍。这样,变动任意一个结点的负载仅影响1%的数据项。此时有6百万个vnode数,使用2bytes来存储结点数(0~65535)。基本的内存占用是6*(10^6)*2bytes=12Mb,对于服务器来说完全可以承受。

    假设有65536(2^16)个node,有128(2^7)倍的partition数(2^23,则PARTITION_POWER=23)。由于MD5码是32位的,使用PARTITION_SHIFT(等于32- PARTITION_POWER)将数据项的MD5哈希值映射到partition的2^23的空间中。

    3.2        数据一致性模型(ConsistencyModel)

    按照Eric Brewer的CAP(Consistency,Availability,PartitionTolerance)理论,无法同时满足3个方面,Swift放弃严格一致性(满足ACID事务级别),而采用最终一致性模型(Eventual Consistency),来达到高可用性和无限水平扩展能力。

    为了实现这一目标,Swift采用Quorum仲裁协议(Quorum有法定投票人数的含义):

    • 定义:N:数据的副本总数;W:写操作被确认接受的副本数量;R:读操作的副本数量。
    • 强一致性:R+W>N,以保证对副本的读写操作会产生交集,从而保证可以读取到最新版本;如果 W=N,R=1,则需要全部更新,适合大量读少量写操作场景下的强一致性;如果 R=N,W=1,则只更新一个副本,通过读取全部副本来得到最新版本,适合大量写少量读场景下的强一致性。
    • 弱一致性:R+W<=N,如果读写操作的副本集合不产生交集,就可能会读到脏数据;适合对一致性要求比较低的场景。

    Swift针对的是读写都比较频繁的场景,所以采用了比较折中的策略,即写操作需要满足至少一半以上成功W>N/2,再保证读操作与写操作的副本集合至少产生一个交集,即R+W>N。

    在分布式系统中,数据的单点是不允许存在的。线上正常存在的replica数量是1的话将非常危险的,因为一旦这个replica再次错误,就可能发生数据的永久性错误。假如我们把N设置成为2,那么,只要有一个存储节点发生损坏,就会有单点的存在。所以N必须大于2。但N越高,系统的维护和整体成本就越高。所以,工业界通常把N设置为3。

    Swift默认配置是N=3,W=2>N/2,R=1或2,即每个对象会存在3个副本,这些副本会被尽量存储在不同区域的节点上;W=2表示至少需要更新2个副本才算写成功。

    当R=1时,意味着某一个读操作成功便立刻返回,此种情况下可能会读取到旧版本(弱一致性模型)。

    当R=2时,需要通过在读操作请求头中增加x-newest=true参数来同时读取2个副本的元数据信息,然后比较时间戳来确定哪个是最新版本(强一致性模型)。

    如果数据出现了不一致,后台服务进程会在一定时间窗口内通过检测和复制协议来完成数据同步,从而保证达到最终一致性。

    图3 Quorum协议示例

    3.3        环(Ring)

    Ring是Swift中最重要的组件,用于记录存储对象与物理位置间的映射关系。在涉及查询Account、Container、Object信息时就需要查询集群的Ring信息。

    环是为了将虚拟节点(partition,分区)均衡地映射到一组物理存储设备上,并提供一定的冗余度而设计的,其数据结构由以下信息组成:

    存储设备列表、设备信息包括唯一标识号(id)、区域号(zone)、权重(weight)、IP 地址(ip)、端口(port)、设备名称(device)、元数据(meta)。

    Swift为账户、容器和对象分别定义了的Ring,其查找过程是相同的。Ring中每个partition在集群中都默认有3个replica。每个partition的位置由ring来维护,并存储在映射中。

    Ring使用zone来保证数据的物理隔离。每个partition的replica都确保放在了不同的zone中。Zone只是个抽象概念,它可以是一个磁盘(disk drive),一个服务器(server),一个机架(cabinet),一个交换机(switch),甚至是一个数据中心(datacenter),以提供最高级别的冗余性,建议至少部署5个zone。

    权重参数是个相对值,可以来根据磁盘的大小来调节,权重越大表示可分配的空间越多,可部署更多的分区。

    当集群中发生存储节点宕机、新增(删)存储节点、新增(删)zone等必须改变partition和node间的映射关系时,还可以对Ring文件通过重新平衡(rebalance)来进行更新。当虚节点需要移动时,环会确保一次移动最少数量的虚节点数,并且一次只移动一个虚节点的一个副本。

    总的来说,Ring引入一致性哈希的原因是为了减少由于增加结点导致数据项移动的数量来提高单调性;引入partition的原因是为了减少由于节点数过少导致移动过多的数据项;引入replica的原因是防止数据单点、提高冗余性;引入zone的原因是为了保证分区容忍性;引入weight的原因是为了保证partition分配的均衡。

    4       架构设计

    4.1        Swift数据模型

    Swift采用层次数据模型,共设三层逻辑结构:Account/Container/Object(账户/容器/对象)。每层节点数均没有限制,可以任意扩展。这里的账户和个人账户不是一个概念,可理解为租户,用来做顶层的隔离机制,可以被多个个人账户所共同使用;容器类似文件夹,代表封装一组对象;对象由元数据和数据两部分组成。

    4.2        Swift系统架构

    Swift采用完全对称、面向资源的分布式系统架构设计,所有组件都可扩展,避免因单点失效而扩散并影响整个系统运转;通信方式采用非阻塞式 I/O 模式,提高了系统吞吐和响应能力。

    Swift组件包括:

    • 代理服务(ProxyServer):Swift通过Proxy Server向外提供基于HTTP的REST服务接口,会根据环的信息来查找服务地址并转发用户请求至相应的账户、容器或者对象,进行CRUD(增删改查)等操作。由于采用无状态的REST请求协议,可以进行横向扩展来均衡负载。在访问Swift服务之前,需要先通过认证服务获取访问令牌,然后在发送的请求中加入头部信息 X-Auth-Token。代理服务器负责Swift架构的其余组件间的相互通信。代理服务器也处理大量的失败请求。例如,如果对于某个对象PUT请求时,某个存储节点不可用,它将会查询环可传送的服务器并转发请求。对象以流的形式到达(来自) 对象服务器,它们直接从代理服务器传送到(来自)用户—代理服务器并不缓冲它们。
    • 认证服务(AuthenticationServer):验证访问用户的身份信息,并获得一个对象访问令牌(Token),在一定的时间内会一直有效;验证访问令牌的有效性并缓存下来直至过期时间。
    • 缓存服务(CacheServer):缓存的内容包括对象服务令牌,账户和容器的存在信息,但不会缓存对象本身的数据;缓存服务可采用Memcached集群,Swift会使用一致性哈希算法来分配缓存地址。
    • 账户服务(AccountServer):提供账户元数据和统计信息,并维护所含容器列表的服务,每个账户的信息被存储在一个SQLite数据库中。
    • 容器服务(ContainerServer):提供容器元数据和统计信息(比如对象的总数,容器的使用情况等),并维护所含对象列表的服务。容器服务并不知道对象存在哪,只知道指定容器里存的哪些对象。 这些对象信息以SQLite数据库文件的形式存储,和对象一样在集群上做类似的备份。 
    • 对象服务(ObjectServer):提供对象元数据和内容服务,可以用来存储、检索和删除本地设备上的对象。在文件系统中,对象以二进制文件的形式存储,它的元数据存储在文件系统的扩展属性(xattr)中,建议采用默认支持扩展属性(xattr)的XFS文件系统。每个对象使用对象名称的哈希值和操作的时间戳组成的路径来存储。最后一次写操作总可以成功,并确保最新一次的对象版本将会被处理。删除也被视为文件的一个版本(一个以".ts"结尾的0字节文件,ts表示墓碑)。
    • 复制服务(Replicator):会检测本地分区副本和远程副本是否一致,具体是通过对比哈希文件和高级水印来完成,发现不一致时会采用推式(Push)更新远程副本:对于对象的复制,更新只是使用rsync同步文件到对等节点。帐号和容器的复制通过HTTP或rsync来推送整个数据库文件上丢失的记录;另外一个任务是确保被标记删除的对象从文件系统中移除:当有一项(对象、容器、或者帐号)被删除,则一个墓碑文件被设置作为该项的最新版本。复制器将会检测到该墓碑文件并确保将它从整个系统中移除。
    • 更新服务(Updater):当对象由于高负载或者系统故障等原因而无法立即更新时,任务将会被序列化到在本地文件系统中进行排队,以便服务恢复后进行异步更新;例如成功创建对象后容器服务器没有及时更新对象列表,这个时候容器的更新操作就会进入排队中,更新服务会在系统恢复正常后扫描队列并进行相应的更新处理。
    • 审计服务(Auditor):在本地服务器上会反复地爬取来检查对象,容器和账户的完整性,如果发现比特级的错误,文件将被隔离,并复制其他的副本以覆盖本地损坏的副本;其他类型的错误(比如在任何一个容器服务器中都找不到所需的对象列表)会被记录到日志中。
    • 账户清理服务(AccountReaper):移除被标记为删除的账户,删除其所包含的所有容器和对象。删除账号的过程是相当直接的。对于每个账号中的容器,每个对象先被删除然后容器被删除。任何失败的删除请求将不会阻止整个过程,但是将会导致整个过程最终失败(例如,如果一个对象的删除超时,容器将不能被删除,因此账号也不能被删除)。整个处理过程即使遭遇失败也继续执行,这样它不会因为一个麻烦的问题而中止恢复集群空间。账号收割器将会继续不断地尝试删除账号直到它最终变为空,此时数据库在db_replicator中回收处理,最终移除这个数据库文件。

    图4 Swift系统架构

    Swift支持的所有操作可以总结为下表:

    表1 SwiftRESTful API总结

    资源类型

    URL

    GET

    PUT

    POST

    DELETE

    HEAD

    账户

    /account/

    获取容器列表

    -

    -

    -

    获取账户元数据

    容器

    /account/container

    获取对象列表

    创建容器

    更新容器元数据

    删除容器

    获取容器元数据

    对象

    /account/container/object

    获取对象内容和元数据

    创建、更新或拷贝对象

    更新对象元数据

    删除对象

    获取对象元数据

    4.3        Ring的数据结构

    Ring 的数据结构由三个顶层域构成,其中:

    • List of Devices,表示集群中设备的列表,在Ring类内部被称为devs;
    • Partition Assignment List,用于存放每个replica与device间映射关系,在Ring类内部被称为_replica2part2dev_id;
    • Partition Shift Value,表示计算数据hash的移位量,在Ring类内部称为_part_shift。

    使用python读取/etc/swift/object.ring.gz存放的数据,可以获得以devs、 part_shift、 replica2part2dev_id 为key的dict类数据。

    4.4        Swift存储结构

    在Storage Node上运行着Linux系统并使用了XFS文件系统,逻辑上使用一致性哈希算法将固定总数的partition映射到每个Storage Node上,每个data也使用同样的哈希算法映射到partition上。

    存储内容一般放在/srv/node/sdb1之类的路径下,其目录结构如下所示:accounts、async_pending、containers、objects、quarantined和tmp。其中accounts、containers、objects分别是账号、容器、对象的存储目录,async_pending是异步待更新目录,quarantined是隔离目录,tmp是临时目录。

    • objects:在objects目录下存放的是各个partition目录,其中每个partition目录是由若干个suffix_path名的目录和一个hashes.pkl文件组成,suffix_path目录下是由object的hash_path名构成的目录,在hash_path目录下存放了关于object的数据和元数据;object的数据存放在后缀为.data的文件中,它的metadata存放在以后缀为.meta的文件中,将被删除的Object以一个0字节后缀为.ts的文件存放。
    • accounts:在accounts目录下存放的是各个partition,而每个partition目录是由若干个suffix_path目录组成,suffix_path目录下是由account的hsh名构成的目录,在hsh目录下存放了关于account的sqlite db;在account的db文件中,包含了account_stat、container、incoming_sync 、outgoing_sync 4张表;其中,表account_stat是记录关于account的信息,如名称、创建时间、container数统计等等;表container记录关于container的信息;表incoming_sync记录到来的同步数据项;表outgoing_sync表示推送出的同步数据项。
    • containers:containers目录结构和生成过程与accounts类似,containers的db中共有5张表,其中incoming_sync和outgoing_sync的schema与accounts中的相同。其他3张表分别为container_stat、object、sqlite_sequence;表container_stat与表account_stat相似,其区别是container_stat存放的是关于container信息。
    • tmp:tmp目录作为account/container/object server向partition目录内写入数据前的临时目录。例如,client向server上传某一文件,object server调用DiskFile类的mkstemp方法创建在路径为path/device/tmp的目录。在数据上传完成之后,再调用put()方法,将数据移动到相应路径。
    • async_pending:async_pending存放未能及时更新而被加入更新队列的数据。本地server在与remote server建立HTTP连接或者发送数据时超时导致更新失败时,将把文件放入async_pending目录。这种情况经常发生在系统故障或者是高负荷的情况下。如果更新失败,本次更新被加入队列,然后由Updater继续处理这些失败的更新工作;account与container的db和object两者的pending文件处理方式有所不同:db的pending文件在更新完其中的一项数据之后,删除pending文件中的相应的数据项,而object的数据在更新完成之后,移动pending文件到目标目录。
    • quarantined:quarantined路径用于隔离发生损坏的数据。Auditor进程会在本地服务器上每隔一段时间就扫描一次磁盘来检测account、container、object的完整性。一旦发现不完整的数据,该文件就会被隔离,该目录就称为quarantined目录。为了限制Auditor消耗过多的系统资源,其默认扫描间隔是30秒,每秒最大的扫描文件数为20,最高速率为10Mb/s。account和container的Auditor的扫描间隔比object要长得多。

    图5 隔离对象的处理流程

    5       小结

    Swift牺牲一定程度的数据一致性,来达到高可用性和可伸缩性,支持多租户模式、容器和对象读写操作,适合解决互联网的应用场景下非结构化数据存储问题。

    有理由相信,因为其完全的开放性、广泛的用户群和社区贡献者,Swift可能会成为云存储的开放标准,从而打破Amazon S3在市场上的垄断地位,推动云计算在朝着更加开放和可互操作的方向前进。

    6       参考资料

    1) 《Openstack Swift 原理、架构与 API 介绍》,http://www.kankanews.com/ICkengine/archives/66411.shtml

    2) 《深入云存储系统Swift核心组件:Ring实现原理剖析》,http://www.cnblogs.com/yuxc/archive/2012/06/22/2558312.html

    3) 《深入云存储系统Swift核心组件:Ring数据结构及构建、重平衡操作》,http://www.cnblogs.com/yuxc/archive/2012/06/28/2568584.html

    4) 《深入云存储系统Swift存储节点:存储实现分析》,http://www.cnblogs.com/yuxc/archive/2012/07/04/2575536.html

    5) 《OpenStack对象存储——Swift开源云计算》,http://dev.yesky.com/244/33228744.shtml

    6) 《讨论:HDFS和OpenStack对象存储的技术差异》,http://os.51cto.com/art/201202/314254.htm

    展开全文
  • OpenStack Swift学习笔记

    2014-01-10 10:31:10
    Swift是文件系统或者实时的数据存储系统,它是对象存储,用于永久类型的静态数据的长期存储,这些数据可以检索、调整,必要时进行更新。最适合存储的数据类型的例子是虚拟机镜像、图片存储、邮件存储和存档备份...

    1       概述

    OpenStack Object StorageSwiftOpenStack开源云计算项目的子项目之一。Swift的目的是使用普通硬件来构建冗余的、可扩展的分布式对象存储集群,存储容量可达PB级。

    Swift并不是文件系统或者实时的数据存储系统,它是对象存储,用于永久类型的静态数据的长期存储,这些数据可以检索、调整,必要时进行更新。最适合存储的数据类型的例子是虚拟机镜像、图片存储、邮件存储和存档备份。

    Swift无需采用RAID(磁盘冗余阵列),也没有中心单元或主控结点。Swift通过在软件层面引入一致性哈希技术和数据冗余性,牺牲一定程度的数据一致性来达到高可用性(High Availability,简称HA)和可伸缩性,支持多租户模式、容器和对象读写操作,适合解决互联网的应用场景下非结构化数据存储问题。

    2       技术特性

    2.1        Swift的主要特征

    Swift的主要特性如下:

    • 极高的数据持久性(Durability)。
    • 完全对称的系统架构:“对称”意味着Swift中各节点可以完全对等,能极大地降低系统维护成本。
    • 无限的可扩展性:一是数据存储容量无限可扩展;二是Swift性能(如QPS、吞吐量等)可线性提升。
    • 无单点故障:Swift的元数据存储是完全均匀随机分布的,并且与对象文件存储一样,元数据也会存储多份。整个Swift集群中,也没有一个角色是单点的,并且在架构和设计上保证无单点业务是有效的。
    • 简单、可依赖。

    2.2        Swift和HDFS的技术差异

    Swift和Hadoop分布式文件系统(HDFS)都有着相似的目的:实现冗余、快速、联网的存储,它们的技术差异如下:

    • 在Swift中,元数据呈分布式,跨集群复制。而在HDFS使用了中央系统来维护文件元数据(Namenode,名称节点),这对HDFS来说无异于单一故障点,因而扩展到规模非常大的环境显得更困难。
    • Swift在设计时考虑到了多租户架构,而HDFS没有多租户架构这个概念。
    • 在Swift中,文件可以写入多次;在并发操作环境下,以最近一次操作为准。而在HDFS中,文件写入一次,而且每次只能有一个文件写入。
    • Swift用Python来编写,而HDFS用Java来编写。
    • Swift被设计成了一种比较通用的存储解决方案,能够可靠地存储数量非常多的大小不一的文件;而HDFS被设计成可以存储数量中等的大文件(HDFS针对更庞大的文件作了优化),以支持数据处理。

    3       关键技术

    3.1        一致性哈希(ConsistentHashing)

    在分布式对象存储中,一个关键问题是数据该如何存放。Swift是基于一致性哈希技术,通过计算可将对象均匀分布到虚拟空间的虚拟节点上,在增加或删除节点时可大大减少需移动的数据量;虚拟空间大小通常采用2的n次幂,便于进行高效的移位操作;然后通过独特的数据结构 Ring(环)再将虚拟节点映射到实际的物理存储设备上,完成寻址过程。


    图1 一致性哈希环结构

    衡量一致性哈希的4个指标:

    • 平衡性(Balance):平衡性是指Hash的结果能够尽可能分布均匀,充分利用所有缓存空间。
    • 单调性(Monotonicity):单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。
    • 分散性(Spread):分散性定义了分布式环境中,不同终端通过Hash过程将内容映射至缓存上时,因可见缓存不同,Hash结果不一致,相同的内容被映射至不同的缓冲区。
    • 负载(Load):负载是对分散性要求的另一个纬度。既然不同的终端可以将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。

    Swift使用该算法的主要目的是在改变集群的node数量时(增加/删除服务器),能够尽可能少地改变已存在key和node的映射关系,以满足单调性。

    考虑到哈希算法在node较少的情况下,改变node数会带来巨大的数据迁移。为了解决这种情况,一致性哈希引入了“虚拟节点”(vnode,也称为partition)的概念: “虚拟节点”是实际节点在环形空间的复制品,一个实际节点对应了若干个“虚拟节点”,“虚拟节点”在哈希空间中以哈希值排列。

    总的来说,Swift中存在两种映射关系,对于一个文件,通过哈希算法(MD5)找到对应的虚节点(一对一的映射关系),虚节点再通过映射关系(ring文件中二维数组)找到对应的设备(多对多的映射关系),这样就完成了一个文件存储在设备上的映射。


    图2 对象、虚结点、节点间的映射关系

    在设置虚结点数的时候,需要对系统预期的规模做充分考虑,假如集群的规模不会超过6000个结点,那么可以将虚结点数设置为结点数的100倍。这样,变动任意一个结点的负载仅影响1%的数据项。此时有6百万个vnode数,使用2bytes来存储结点数(0~65535)。基本的内存占用是6*(10^6)*2bytes=12Mb,对于服务器来说完全可以承受。

    假设有65536(2^16)个node,有128(2^7)倍的partition数(2^23,则PARTITION_POWER=23)。由于MD5码是32位的,使用PARTITION_SHIFT(等于32- PARTITION_POWER)将数据项的MD5哈希值映射到partition的2^23的空间中。

    3.2        数据一致性模型(ConsistencyModel)

    按照Eric Brewer的CAP(Consistency,Availability,PartitionTolerance)理论,无法同时满足3个方面,Swift放弃严格一致性(满足ACID事务级别),而采用最终一致性模型(Eventual Consistency),来达到高可用性和无限水平扩展能力。

    为了实现这一目标,Swift采用Quorum仲裁协议(Quorum有法定投票人数的含义):

    • 定义:N:数据的副本总数;W:写操作被确认接受的副本数量;R:读操作的副本数量。
    • 强一致性:R+W>N,以保证对副本的读写操作会产生交集,从而保证可以读取到最新版本;如果 W=N,R=1,则需要全部更新,适合大量读少量写操作场景下的强一致性;如果 R=N,W=1,则只更新一个副本,通过读取全部副本来得到最新版本,适合大量写少量读场景下的强一致性。
    • 弱一致性:R+W<=N,如果读写操作的副本集合不产生交集,就可能会读到脏数据;适合对一致性要求比较低的场景。

    Swift针对的是读写都比较频繁的场景,所以采用了比较折中的策略,即写操作需要满足至少一半以上成功W>N/2,再保证读操作与写操作的副本集合至少产生一个交集,即R+W>N。

    在分布式系统中,数据的单点是不允许存在的。线上正常存在的replica数量是1的话将非常危险的,因为一旦这个replica再次错误,就可能发生数据的永久性错误。假如我们把N设置成为2,那么,只要有一个存储节点发生损坏,就会有单点的存在。所以N必须大于2。但N越高,系统的维护和整体成本就越高。所以,工业界通常把N设置为3。

    Swift默认配置是N=3,W=2>N/2,R=1或2,即每个对象会存在3个副本,这些副本会被尽量存储在不同区域的节点上;W=2表示至少需要更新2个副本才算写成功。

    当R=1时,意味着某一个读操作成功便立刻返回,此种情况下可能会读取到旧版本(弱一致性模型)。

    当R=2时,需要通过在读操作请求头中增加x-newest=true参数来同时读取2个副本的元数据信息,然后比较时间戳来确定哪个是最新版本(强一致性模型)。

    如果数据出现了不一致,后台服务进程会在一定时间窗口内通过检测和复制协议来完成数据同步,从而保证达到最终一致性。

    图3 Quorum协议示例

    3.3        环(Ring)

    Ring是Swift中最重要的组件,用于记录存储对象与物理位置间的映射关系。在涉及查询Account、Container、Object信息时就需要查询集群的Ring信息。

    环是为了将虚拟节点(partition,分区)均衡地映射到一组物理存储设备上,并提供一定的冗余度而设计的,其数据结构由以下信息组成:

    存储设备列表、设备信息包括唯一标识号(id)、区域号(zone)、权重(weight)、IP 地址(ip)、端口(port)、设备名称(device)、元数据(meta)。

    Swift为账户、容器和对象分别定义了的Ring,其查找过程是相同的。Ring中每个partition在集群中都默认有3个replica。每个partition的位置由ring来维护,并存储在映射中。

    Ring使用zone来保证数据的物理隔离。每个partition的replica都确保放在了不同的zone中。Zone只是个抽象概念,它可以是一个磁盘(disk drive),一个服务器(server),一个机架(cabinet),一个交换机(switch),甚至是一个数据中心(datacenter),以提供最高级别的冗余性,建议至少部署5个zone。

    权重参数是个相对值,可以来根据磁盘的大小来调节,权重越大表示可分配的空间越多,可部署更多的分区。

    当集群中发生存储节点宕机、新增(删)存储节点、新增(删)zone等必须改变partition和node间的映射关系时,还可以对Ring文件通过重新平衡(rebalance)来进行更新。当虚节点需要移动时,环会确保一次移动最少数量的虚节点数,并且一次只移动一个虚节点的一个副本。

    总的来说,Ring引入一致性哈希的原因是为了减少由于增加结点导致数据项移动的数量来提高单调性;引入partition的原因是为了减少由于节点数过少导致移动过多的数据项;引入replica的原因是防止数据单点、提高冗余性;引入zone的原因是为了保证分区容忍性;引入weight的原因是为了保证partition分配的均衡。

    4       架构设计

    4.1        Swift数据模型

    Swift采用层次数据模型,共设三层逻辑结构:Account/Container/Object(账户/容器/对象)。每层节点数均没有限制,可以任意扩展。这里的账户和个人账户不是一个概念,可理解为租户,用来做顶层的隔离机制,可以被多个个人账户所共同使用;容器类似文件夹,代表封装一组对象;对象由元数据和数据两部分组成。

    4.2        Swift系统架构

    Swift采用完全对称、面向资源的分布式系统架构设计,所有组件都可扩展,避免因单点失效而扩散并影响整个系统运转;通信方式采用非阻塞式 I/O 模式,提高了系统吞吐和响应能力。

    Swift组件包括:

    • 代理服务(ProxyServer):Swift通过Proxy Server向外提供基于HTTP的REST服务接口,会根据环的信息来查找服务地址并转发用户请求至相应的账户、容器或者对象,进行CRUD(增删改查)等操作。由于采用无状态的REST请求协议,可以进行横向扩展来均衡负载。在访问Swift服务之前,需要先通过认证服务获取访问令牌,然后在发送的请求中加入头部信息 X-Auth-Token。代理服务器负责Swift架构的其余组件间的相互通信。代理服务器也处理大量的失败请求。例如,如果对于某个对象PUT请求时,某个存储节点不可用,它将会查询环可传送的服务器并转发请求。对象以流的形式到达(来自) 对象服务器,它们直接从代理服务器传送到(来自)用户—代理服务器并不缓冲它们。
    • 认证服务(AuthenticationServer):验证访问用户的身份信息,并获得一个对象访问令牌(Token),在一定的时间内会一直有效;验证访问令牌的有效性并缓存下来直至过期时间。
    • 缓存服务(CacheServer):缓存的内容包括对象服务令牌,账户和容器的存在信息,但不会缓存对象本身的数据;缓存服务可采用Memcached集群,Swift会使用一致性哈希算法来分配缓存地址。
    • 账户服务(AccountServer):提供账户元数据和统计信息,并维护所含容器列表的服务,每个账户的信息被存储在一个SQLite数据库中。
    • 容器服务(ContainerServer):提供容器元数据和统计信息(比如对象的总数,容器的使用情况等),并维护所含对象列表的服务。容器服务并不知道对象存在哪,只知道指定容器里存的哪些对象。 这些对象信息以SQLite数据库文件的形式存储,和对象一样在集群上做类似的备份。 
    • 对象服务(ObjectServer):提供对象元数据和内容服务,可以用来存储、检索和删除本地设备上的对象。在文件系统中,对象以二进制文件的形式存储,它的元数据存储在文件系统的扩展属性(xattr)中,建议采用默认支持扩展属性(xattr)的XFS文件系统。每个对象使用对象名称的哈希值和操作的时间戳组成的路径来存储。最后一次写操作总可以成功,并确保最新一次的对象版本将会被处理。删除也被视为文件的一个版本(一个以".ts"结尾的0字节文件,ts表示墓碑)。
    • 复制服务(Replicator):会检测本地分区副本和远程副本是否一致,具体是通过对比哈希文件和高级水印来完成,发现不一致时会采用推式(Push)更新远程副本:对于对象的复制,更新只是使用rsync同步文件到对等节点。帐号和容器的复制通过HTTP或rsync来推送整个数据库文件上丢失的记录;另外一个任务是确保被标记删除的对象从文件系统中移除:当有一项(对象、容器、或者帐号)被删除,则一个墓碑文件被设置作为该项的最新版本。复制器将会检测到该墓碑文件并确保将它从整个系统中移除。
    • 更新服务(Updater):当对象由于高负载或者系统故障等原因而无法立即更新时,任务将会被序列化到在本地文件系统中进行排队,以便服务恢复后进行异步更新;例如成功创建对象后容器服务器没有及时更新对象列表,这个时候容器的更新操作就会进入排队中,更新服务会在系统恢复正常后扫描队列并进行相应的更新处理。
    • 审计服务(Auditor):在本地服务器上会反复地爬取来检查对象,容器和账户的完整性,如果发现比特级的错误,文件将被隔离,并复制其他的副本以覆盖本地损坏的副本;其他类型的错误(比如在任何一个容器服务器中都找不到所需的对象列表)会被记录到日志中。
    • 账户清理服务(AccountReaper):移除被标记为删除的账户,删除其所包含的所有容器和对象。删除账号的过程是相当直接的。对于每个账号中的容器,每个对象先被删除然后容器被删除。任何失败的删除请求将不会阻止整个过程,但是将会导致整个过程最终失败(例如,如果一个对象的删除超时,容器将不能被删除,因此账号也不能被删除)。整个处理过程即使遭遇失败也继续执行,这样它不会因为一个麻烦的问题而中止恢复集群空间。账号收割器将会继续不断地尝试删除账号直到它最终变为空,此时数据库在db_replicator中回收处理,最终移除这个数据库文件。

    图4 Swift系统架构

    Swift支持的所有操作可以总结为下表:

    表1 SwiftRESTful API总结

    资源类型

    URL

    GET

    PUT

    POST

    DELETE

    HEAD

    账户

    /account/

    获取容器列表

    -

    -

    -

    获取账户元数据

    容器

    /account/container

    获取对象列表

    创建容器

    更新容器元数据

    删除容器

    获取容器元数据

    对象

    /account/container/object

    获取对象内容和元数据

    创建、更新或拷贝对象

    更新对象元数据

    删除对象

    获取对象元数据

    4.3        Ring的数据结构

    Ring 的数据结构由三个顶层域构成,其中:

    • List of Devices,表示集群中设备的列表,在Ring类内部被称为devs;
    • Partition Assignment List,用于存放每个replica与device间映射关系,在Ring类内部被称为_replica2part2dev_id;
    • Partition Shift Value,表示计算数据hash的移位量,在Ring类内部称为_part_shift。

    使用python读取/etc/swift/object.ring.gz存放的数据,可以获得以devs、 part_shift、 replica2part2dev_id 为key的dict类数据。

    4.4        Swift存储结构

    在Storage Node上运行着Linux系统并使用了XFS文件系统,逻辑上使用一致性哈希算法将固定总数的partition映射到每个Storage Node上,每个data也使用同样的哈希算法映射到partition上。

    存储内容一般放在/srv/node/sdb1之类的路径下,其目录结构如下所示:accounts、async_pending、containers、objects、quarantined和tmp。其中accounts、containers、objects分别是账号、容器、对象的存储目录,async_pending是异步待更新目录,quarantined是隔离目录,tmp是临时目录。

    • objects:在objects目录下存放的是各个partition目录,其中每个partition目录是由若干个suffix_path名的目录和一个hashes.pkl文件组成,suffix_path目录下是由object的hash_path名构成的目录,在hash_path目录下存放了关于object的数据和元数据;object的数据存放在后缀为.data的文件中,它的metadata存放在以后缀为.meta的文件中,将被删除的Object以一个0字节后缀为.ts的文件存放。
    • accounts:在accounts目录下存放的是各个partition,而每个partition目录是由若干个suffix_path目录组成,suffix_path目录下是由account的hsh名构成的目录,在hsh目录下存放了关于account的sqlite db;在account的db文件中,包含了account_stat、container、incoming_sync 、outgoing_sync 4张表;其中,表account_stat是记录关于account的信息,如名称、创建时间、container数统计等等;表container记录关于container的信息;表incoming_sync记录到来的同步数据项;表outgoing_sync表示推送出的同步数据项。
    • containers:containers目录结构和生成过程与accounts类似,containers的db中共有5张表,其中incoming_sync和outgoing_sync的schema与accounts中的相同。其他3张表分别为container_stat、object、sqlite_sequence;表container_stat与表account_stat相似,其区别是container_stat存放的是关于container信息。
    • tmp:tmp目录作为account/container/object server向partition目录内写入数据前的临时目录。例如,client向server上传某一文件,object server调用DiskFile类的mkstemp方法创建在路径为path/device/tmp的目录。在数据上传完成之后,再调用put()方法,将数据移动到相应路径。
    • async_pending:async_pending存放未能及时更新而被加入更新队列的数据。本地server在与remote server建立HTTP连接或者发送数据时超时导致更新失败时,将把文件放入async_pending目录。这种情况经常发生在系统故障或者是高负荷的情况下。如果更新失败,本次更新被加入队列,然后由Updater继续处理这些失败的更新工作;account与container的db和object两者的pending文件处理方式有所不同:db的pending文件在更新完其中的一项数据之后,删除pending文件中的相应的数据项,而object的数据在更新完成之后,移动pending文件到目标目录。
    • quarantined:quarantined路径用于隔离发生损坏的数据。Auditor进程会在本地服务器上每隔一段时间就扫描一次磁盘来检测account、container、object的完整性。一旦发现不完整的数据,该文件就会被隔离,该目录就称为quarantined目录。为了限制Auditor消耗过多的系统资源,其默认扫描间隔是30秒,每秒最大的扫描文件数为20,最高速率为10Mb/s。account和container的Auditor的扫描间隔比object要长得多。

    图5 隔离对象的处理流程

    5       小结

    Swift牺牲一定程度的数据一致性,来达到高可用性和可伸缩性,支持多租户模式、容器和对象读写操作,适合解决互联网的应用场景下非结构化数据存储问题。

    有理由相信,因为其完全的开放性、广泛的用户群和社区贡献者,Swift可能会成为云存储的开放标准,从而打破Amazon S3在市场上的垄断地位,推动云计算在朝着更加开放和可互操作的方向前进。

    6       参考资料

    1) 《Openstack Swift 原理、架构与 API 介绍》,http://www.kankanews.com/ICkengine/archives/66411.shtml

    2) 《深入云存储系统Swift核心组件:Ring实现原理剖析》,http://www.cnblogs.com/yuxc/archive/2012/06/22/2558312.html

    3) 《深入云存储系统Swift核心组件:Ring数据结构及构建、重平衡操作》,http://www.cnblogs.com/yuxc/archive/2012/06/28/2568584.html

    4) 《深入云存储系统Swift存储节点:存储实现分析》,http://www.cnblogs.com/yuxc/archive/2012/07/04/2575536.html

    5) 《OpenStack对象存储——Swift开源云计算》,http://dev.yesky.com/244/33228744.shtml

    6) 《讨论:HDFS和OpenStack对象存储的技术差异》,http://os.51cto.com/art/201202/314254.htm

    展开全文
  • 1 概述 ...本文将深入介绍 Swift 的基本设计原理、对称式的系统架构和 RESTful API。  OpenStack Object Storage (Swift)是OpenStack开源云计算项目的子项目之一。Swift的目的是使用普通硬件来构建冗
    1 概述
        OpenStack Swift 开源项目提供了弹性可伸缩、高可用的分布式对象存储服务,适合存储大规模非结构化数据。本文将深入介绍 Swift 的基本设计原理、对称式的系统架构和 RESTful API。
        OpenStack Object Storage (Swift)OpenStack开源云计算项目的子项目之一。Swift的目的是使用普通硬件来构建冗余的、可扩展的分布式对象存储集群,存储容量可达PB级。
        Swift并不是文件系统或者实时的数据存储系统,它是对象存储,用于永久类型的静态数据的长期存储,这些数据可以检索、调整,必要时进行更新。最适合存储的数据类型的例子是虚拟机镜像、图片存储、邮件存储和存档备份。
        Swift无需采用RAID(磁盘冗余阵列),也没有中心单元或主控结点。Swift通过在软件层面引入一致性哈希技术和数据冗余性,牺牲一定程度的数据一致性来达到高可用性(High Availability,简称HA)和可伸缩性,支持多租户模式、容器和对象读写操作,适合解决互联网的应用场景下非结构化数据存储问题。

    2 技术特性

    2.1 Swift的主要特征

    Swift的主要特性如下:

    • 极高的数据持久性(Durability)。
    • 完全对称的系统架构:“对称”意味着Swift中各节点可以完全对等,能极大地降低系统维护成本。
    • 无限的可扩展性:一是数据存储容量无限可扩展;二是Swift性能(如QPS、吞吐量等)可线性提升。
    • 无单点故障:Swift的元数据存储是完全均匀随机分布的,并且与对象文件存储一样,元数据也会存储多份。整个Swift集群中,也没有一个角色是单点的,并且在架构和设计上保证无单点业务是有效的。
    • 简单、可依赖。

    2.2 Swift和HDFS的技术差异

    Swift和Hadoop分布式文件系统(HDFS)都有着相似的目的:实现冗余、快速、联网的存储,它们的技术差异如下:

    • 在Swift中,元数据呈分布式,跨集群复制。而在HDFS使用了中央系统来维护文件元数据(Namenode,名称节点),这对HDFS来说无异于单一故障点,因而扩展到规模非常大的环境显得更困难。
    • Swift在设计时考虑到了多租户架构,而HDFS没有多租户架构这个概念。
    • 在Swift中,文件可以写入多次;在并发操作环境下,以最近一次操作为准。而在HDFS中,文件写入一次,而且每次只能有一个文件写入。
    • Swift用Python来编写,而HDFS用Java来编写。
    • Swift被设计成了一种比较通用的存储解决方案,能够可靠地存储数量非常多的大小不一的文件;而HDFS被设计成可以存储数量中等的大文件(HDFS针对更庞大的文件作了优化),以支持数据处理。

    3 关键技术
    3.1 一致性哈希(ConsistentHashing)
        在分布式对象存储中,一个关键问题是数据该如何存放。Swift是基于一致性哈希技术,通过计算可将对象均匀分布到虚拟空间的虚拟节点上,在增加或删除节点时可大大减少需移动的数据量;虚拟空间大小通常采用2的n次幂,便于进行高效的移位操作;然后通过独特的数据结构 Ring(环)再将虚拟节点映射到实际的物理存储设备上,完成寻址过程。


    图1. 一致性哈希环结构

    衡量一致性哈希的4个指标:

    • 平衡性(Balance):平衡性是指Hash的结果能够尽可能分布均匀,充分利用所有缓存空间。
    • 单调性(Monotonicity):单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。
    • 分散性(Spread):分散性定义了分布式环境中,不同终端通过Hash过程将内容映射至缓存上时,因可见缓存不同,Hash结果不一致,相同的内容被映射至不同的缓冲区。
    • 负载(Load):负载是对分散性要求的另一个纬度。既然不同的终端可以将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。
        Swift使用该算法的主要目的是在改变集群的node数量时(增加/删除服务器),能够尽可能少地改变已存在key和node的映射关系,以满足单调性。
        考虑到哈希算法在node较少的情况下,改变node数会带来巨大的数据迁移。为了解决这种情况,一致性哈希引入了“虚拟节点”(vnode,也称为partition)的概念: “虚拟节点”是实际节点在环形空间的复制品,一个实际节点对应了若干个“虚拟节点”,“虚拟节点”在哈希空间中以哈希值排列。
        总的来说,Swift中存在两种映射关系,对于一个文件,通过哈希算法(MD5)以及移位运算找到对应的虚节点(一对一的映射关系);虚节点再通过映射关系(ring文件中二维数组)找到对应的设备(多对多的映射关系),这样就完成了一个文件存储在设备上的映射。


    图2. 对象、虚结点、节点间的映射关系

         在设置虚结点数的时候,需要对系统预期的规模做充分考虑,假如集群的规模不会超过6000个结点,那么可以将虚结点数设置为结点数的100倍。这样,变动任意一个结点的负载仅影响1%的数据项。此时有6百万个vnode数,使用2bytes来存储结点数(0~65535)。基本的内存占用是6*(10^6)*2bytes=12Mb,对于服务器来说完全可以承受。
         假设有65536(2^16)个node,有128(2^7)倍的partition数(2^23,则PARTITION_POWER=23)。由于MD5码是32位的,使用PARTITION_SHIFT(等于32- PARTITION_POWER)将数据项的MD5哈希值映射到partition的2^23的空间中。

    3.2 数据一致性模型(ConsistencyModel)
        按照Eric Brewer的CAP(Consistency,Availability,PartitionTolerance)理论,无法同时满足3个方面,Swift放弃严格一致性(满足ACID事务级别),而采用最终一致性模型(Eventual Consistency),来达到高可用性和无限水平扩展能力。

    为了实现这一目标,Swift采用Quorum仲裁协议(Quorum有法定投票人数的含义):

    • 定义:N:数据的副本总数;W:写操作被确认接受的副本数量;R:读操作的副本数量。
    • 强一致性:R+W>N,以保证对副本的读写操作会产生交集,从而保证可以读取到最新版本;如果 W=N,R=1,则需要全部更新,适合大量读少量写操作场景下的强一致性;如果 R=N,W=1,则只更新一个副本,通过读取全部副本来得到最新版本,适合大量写少量读场景下的强一致性。
    • 弱一致性:R+W<=N,如果读写操作的副本集合不产生交集,就可能会读到脏数据;适合对一致性要求比较低的场景。
         Swift针对的是读写都比较频繁的场景,所以采用了比较折中的策略,即写操作需要满足至少一半以上成功W>N/2,再保证读操作与写操作的副本集合至少产生一个交集,即R+W>N。在分布式系统中,数据的单点是不允许存在的。线上正常存在的replica数量是1的话将非常危险的,因为一旦这个replica再次错误,就可能发生数据的永久性错误。假如我们把N设置成为2,那么,只要有一个存储节点发生损坏,就会有单点的存在。所以N必须大于2。但N越高,系统的维护和整体成本就越高。所以,工业界通常把N设置为3。
        Swift默认配置是N=3,W=2>N/2,R=1或2,即每个对象会存在3个副本,这些副本会被尽量存储在不同区域的节点上;W=2表示至少需要更新2个副本才算写成功。
    当R=1时,意味着某一个读操作成功便立刻返回,此种情况下可能会读取到旧版本(弱一致性模型)。
    当R=2时,需要通过在读操作请求头中增加x-newest=true参数来同时读取2个副本的元数据信息,然后比较时间戳来确定哪个是最新版本(强一致性模型)。
        如果数据出现了不一致,后台服务进程会在一定时间窗口内通过检测和复制协议来完成数据同步,从而保证达到最终一致性。


    图3. Quorum协议示例

        但其实根据源码分析来看,Quorum仲裁机制必须要等到3个Response都返回之后才会选举出最佳的Response返回给客户端(即使是Timeout,也会等待Storage Server返回),所以一个集群一旦遇到有一个慢节点,将非常影响Swift的性能,导致写入速度上不去(这是一个可以改进的地方!)


    3.3 环(Ring)
         Ring是Swift中最重要的组件,用于记录存储对象与物理位置间的映射关系。在涉及查询Account、Container、Object信息时就需要查询集群的Ring信息。
        环是为了将虚拟节点(partition,分区)均衡地映射到一组物理存储设备上,并提供一定的冗余度而设计的,其数据结构由以下信息组成:
    存储设备列表,设备信息包括唯一标识号(id)、区域号(zone)、权重(weight)、IP 地址(ip)、端口(port)、设备名称(device)、元数据(meta);
    分区到设备映射关系(replica2part2dev_id 数组);
    计算分区号的位移(part_shift 整数)
        以查找一个对象的计算过程为例:

    图4. 环的数据机构
         使用对象的层次结构 account/container/object 作为键,使用 MD5 散列算法得到一个散列值,对该散列值的前 4 个字节进行右移操作得到分区索引号,移动位数由上面的 part_shift 设置指定(一致性哈希映射到虚拟节点);按照分区索引号在分区到设备映射表(replica2part2dev_id)里查找该对象所在分区的对应的所有设备编号,这些设备会被尽量选择部署在不同区域(Zone)内,区域只是个抽象概念,它可以是某台机器,某个机架,甚至某个建筑内的机群,以提供最高级别的冗余性,建议至少部署 5 个区域;权重参数是个相对值,可以来根据磁盘的大小来调节,权重越大表示可分配的空间越多,可部署更多的分区。 
         Swift为账户、容器和对象分别定义了的Ring,其查找过程是相同的。Ring中每个partition在集群中都默认有3个replica。每个partition的位置由ring来维护,并存储在映射中。
         Ring使用zone来保证数据的物理隔离。每个partition的replica都确保放在了不同的zone中。Zone只是个抽象概念,它可以是一个磁盘(disk drive),一个服务器(server),一个机架(cabinet),一个交换机(switch),甚至是一个数据中心(datacenter),以提供最高级别的冗余性,建议至少部署5个zone。
         权重参数是个相对值,可以来根据磁盘的大小来调节,权重越大表示可分配的空间越多,可部署更多的分区。
         当集群中发生存储节点宕机、新增(删)存储节点、新增(删)zone等必须改变partition和node间的映射关系时,还可以对Ring文件通过重新平衡(rebalance)来进行更新。当虚节点需要移动时,环会确保一次移动最少数量的虚节点数,并且一次只移动一个虚节点的一个副本。
         总的来说,Ring引入一致性哈希的原因是为了减少由于增加结点导致数据项移动的数量来提高单调性;引入partition的原因是为了减少由于节点数过少导致移动过多的数据项(数据负载不均衡);引入replica的原因是防止数据单点、提高冗余性;引入zone的原因是为了保证分区容忍性;引入weight的原因是为了保证partition分配的均衡。

    4 架构设计
    4.1 Swift数据模型
        Swift采用层次数据模型,共设三层逻辑结构:Account/Container/Object(账户/容器/对象)。每层节点数均没有限制,可以任意扩展。这里的账户和个人账户不是一个概念,可理解为租户,用来做顶层的隔离机制,可以被多个个人账户所共同使用;容器类似文件夹,代表封装一组对象;对象由元数据和数据两部分组成。

    图 4. Swift 数据模型

    4.2 Swift系统架构
        Swift采用完全对称、面向资源的分布式系统架构设计,所有组件都可扩展,避免因单点失效而扩散并影响整个系统运转;通信方式采用非阻塞式 I/O 模式,提高了系统吞吐和响应能力。

    Swift组件包括:

    • 代理服务(ProxyServer):Swift通过Proxy Server向外提供基于HTTP的REST服务接口,会根据环的信息来查找服务地址并转发用户请求至相应的账户、容器或者对象,进行CRUD(增删改查)等操作。由于采用无状态的REST请求协议,可以进行横向扩展来均衡负载。在访问Swift服务之前,需要先通过认证服务获取访问令牌,然后在发送的请求中加入头部信息 X-Auth-Token。代理服务器负责Swift架构的其余组件间的相互通信。代理服务器也处理大量的失败请求。例如,如果对于某个对象PUT请求时,某个存储节点不可用,它将会查询环可传送的服务器并转发请求。对象以流的形式到达(来自) 对象服务器,它们直接从代理服务器传送到(来自)用户—代理服务器并不缓冲它们。
    • 认证服务(AuthenticationServer):验证访问用户的身份信息,并获得一个对象访问令牌(Token),在一定的时间内会一直有效;验证访问令牌的有效性并缓存下来直至过期时间。
    • 缓存服务(CacheServer):缓存的内容包括对象服务令牌,账户和容器的存在信息,但不会缓存对象本身的数据;缓存服务可采用Memcached集群,Swift会使用一致性哈希算法来分配缓存地址。
    • 账户服务(AccountServer):提供账户元数据和统计信息,并维护所含容器列表的服务,每个账户的信息被存储在一个SQLite数据库中。
    • 容器服务(ContainerServer):提供容器元数据和统计信息(比如对象的总数,容器的使用情况等),并维护所含对象列表的服务。容器服务并不知道对象存在哪,只知道指定容器里存的哪些对象。 这些对象信息以SQLite数据库文件的形式存储,和对象一样在集群上做类似的备份。 
    • 对象服务(ObjectServer):提供对象元数据和内容服务,可以用来存储、检索和删除本地设备上的对象。在文件系统中,对象以二进制文件的形式存储,它的元数据存储在文件系统的扩展属性(xattr)中,建议采用默认支持扩展属性(xattr)的XFS文件系统。每个对象使用对象名称的哈希值和操作的时间戳组成的路径来存储。最后一次写操作总可以成功,并确保最新一次的对象版本将会被处理。删除也被视为文件的一个版本(一个以".ts"结尾的0字节文件,ts表示墓碑)。
    • 复制服务(Replicator):会检测本地分区副本和远程副本是否一致,具体是通过对比哈希文件和高级水印来完成,发现不一致时会采用推式(Push)更新远程副本:对于对象的复制,更新只是使用rsync同步文件到对等节点。帐号和容器的复制通过HTTP或rsync来推送整个数据库文件上丢失的记录;另外一个任务是确保被标记删除的对象从文件系统中移除:当有一项(对象、容器、或者帐号)被删除,则一个墓碑文件被设置作为该项的最新版本。复制器将会检测到该墓碑文件并确保将它从整个系统中移除。
    • 更新服务(Updater):当对象由于高负载或者系统故障等原因而无法立即更新时,任务将会被序列化到在本地文件系统中进行排队,以便服务恢复后进行异步更新;例如成功创建对象后容器服务器没有及时更新对象列表,这个时候容器的更新操作就会进入排队中,更新服务会在系统恢复正常后扫描队列并进行相应的更新处理。
    • 审计服务(Auditor):在本地服务器上会反复地爬取来检查对象,容器和账户的完整性,如果发现比特级的错误,文件将被隔离,并复制其他的副本以覆盖本地损坏的副本;其他类型的错误(比如在任何一个容器服务器中都找不到所需的对象列表)会被记录到日志中。
    • 账户清理服务(AccountReaper):移除被标记为删除的账户,删除其所包含的所有容器和对象。删除账号的过程是相当直接的。对于每个账号中的容器,每个对象先被删除然后容器被删除。任何失败的删除请求将不会阻止整个过程,但是将会导致整个过程最终失败(例如,如果一个对象的删除超时,容器将不能被删除,因此账号也不能被删除)。整个处理过程即使遭遇失败也继续执行,这样它不会因为一个麻烦的问题而中止恢复集群空间。账号收割器将会继续不断地尝试删除账号直到它最终变为空,此时数据库在db_replicator中回收处理,最终移除这个数据库文件。


    图4 Swift系统架构

    Swift支持的所有操作可以总结为下表:

    表1 SwiftRESTful API总结

    资源类型

    URL

    GET

    PUT

    POST

    DELETE

    HEAD

    账户

    /account/

    获取容器列表

    -

    -

    -

    获取账户元数据

    容器

    /account/container

    获取对象列表

    创建容器

    更新容器元数据

    删除容器

    获取容器元数据

    对象

    /account/container/object

    获取对象内容和元数据

    创建、更新或拷贝对象

    更新对象元数据

    删除对象

    获取对象元数据


    4.3 Ring的数据结构

    Ring 的数据结构由三个顶层域构成,其中:

    • List of Devices,表示集群中设备的列表,在Ring类内部被称为devs;
    • Partition Assignment List,用于存放每个replica与device间映射关系,在Ring类内部被称为_replica2part2dev_id;
    • Partition Shift Value,表示计算数据hash的移位量,在Ring类内部称为_part_shift。
    使用Python读取/etc/swift/object.ring.gz存放的数据,可以获得以devs、 part_shift、 replica2part2dev_id 为key的dict类数据。

    4.4 Swift存储结构
        在Storage Node上运行着Linux系统并使用了XFS文件系统,逻辑上使用一致性哈希算法将固定总数的partition映射到每个Storage Node上,每个data也使用同样的哈希算法映射到partition上。
        存储内容一般放在/srv/node/sdb1之类的路径下,其目录结构如下所示:accounts、async_pending、containers、objects、quarantined和tmp。其中accounts、containers、objects分别是账号、容器、对象的存储目录,async_pending是异步待更新目录,quarantined是隔离目录,tmp是临时目录。
    • objects:在objects目录下存放的是各个partition目录,其中每个partition目录是由若干个suffix_path名的目录和一个hashes.pkl文件组成,suffix_path目录下是由object的hash_path名构成的目录,在hash_path目录下存放了关于object的数据和元数据;object的数据存放在后缀为.data的文件中,它的metadata存放在以后缀为.meta的文件中,将被删除的Object以一个0字节后缀为.ts的文件存放。
    • accounts:在accounts目录下存放的是各个partition,而每个partition目录是由若干个suffix_path目录组成,suffix_path目录下是由account的hsh名构成的目录,在hsh目录下存放了关于account的sqlite db;在account的db文件中,包含了account_stat、container、incoming_sync 、outgoing_sync 4张表;其中,表account_stat是记录关于account的信息,如名称、创建时间、container数统计等等;表container记录关于container的信息;表incoming_sync记录到来的同步数据项;表outgoing_sync表示推送出的同步数据项。
    • containers:containers目录结构和生成过程与accounts类似,containers的db中共有5张表,其中incoming_sync和outgoing_sync的schema与accounts中的相同。其他3张表分别为container_stat、object、sqlite_sequence;表container_stat与表account_stat相似,其区别是container_stat存放的是关于container信息。
    • tmp:tmp目录作为account/container/object server向partition目录内写入数据前的临时目录。例如,client向server上传某一文件,object server调用DiskFile类的mkstemp方法创建在路径为path/device/tmp的目录。在数据上传完成之后,再调用put()方法,将数据移动到相应路径。
    • async_pending:async_pending存放未能及时更新而被加入更新队列的数据。本地server在与remote server建立HTTP连接或者发送数据时超时导致更新失败时,将把文件放入async_pending目录。这种情况经常发生在系统故障或者是高负荷的情况下。如果更新失败,本次更新被加入队列,然后由Updater继续处理这些失败的更新工作;account与container的db和object两者的pending文件处理方式有所不同:db的pending文件在更新完其中的一项数据之后,删除pending文件中的相应的数据项,而object的数据在更新完成之后,移动pending文件到目标目录。
    • quarantined:quarantined路径用于隔离发生损坏的数据。Auditor进程会在本地服务器上每隔一段时间就扫描一次磁盘来检测account、container、object的完整性。一旦发现不完整的数据,该文件就会被隔离,该目录就称为quarantined目录。为了限制Auditor消耗过多的系统资源,其默认扫描间隔是30秒,每秒最大的扫描文件数为20,最高速率为10Mb/s。account和container的Auditor的扫描间隔比object要长得多。


    图5 隔离对象的处理流程


    5 小结
        Swift牺牲一定程度的数据一致性,来达到高可用性和可伸缩性,支持多租户模式、容器和对象读写操作,适合解决互联网的应用场景下非结构化数据存储问题。有理由相信,因为其完全的开放性、广泛的用户群和社区贡献者,Swift可能会成为云存储的开放标准,从而打破Amazon S3在市场上的垄断地位,推动云计算在朝着更加开放和可互操作的方向前进。

    6 个人小结

        Swift作为与OpenStack完美兼容的对象存储的解决方案,以及现有的开源形式,必将提升Swift的普及和开发热度。虽然高可用,但是因为是基于Python开发,所以在高并发的情况下,Swift的性能并不是特别稳定且有Latency的优势,开发语言方面的劣势。所以现有的Swift开源社区正在用Go语言实现Swift的重构。
        其次,Quorum仲裁机制有优化的地方,因为仲裁机制(仲裁机制也就是从3个Response中判断成功的Response数量是否>=2)必须要等到写入的3个Storage Server全部返回才进行仲裁(默认采用三备份),如果其中有慢节点的话,一直得不到响应,那就必须要等到Timeout,这样势必会严重影响集群的写入Latency性能(并且,集群如果有很多节点,出现慢节点的概率是很高的)。所以我们可以考虑只需要成功返回Response响应达到2个就判断为写入成功并返回给客户端,而无需等到3个Response全部返回采取作判断;或者说,直接控制写入2个副本,2个Response成功返回就判断为写入成功并返回即可,因为实现后端的三备份可由Replicator去完成,而第3个Response就不需要等待了,采用这两种方案都可以提升Swift的Latency性能。
        最后,还想到一个方案可以提升Swift的写入性能,现有的Swift架构Proxy起到了Router的关键作用,一次成功的写入需要经历如下过程:判断是否有Token等验证信息,进而去调用Keystone完成验证,验证完成之后根据相应的请求,需要查询进行MD5求值和移位运算得出Partition,然后再根据Partition在内存里面的Ring数据结构内部的_replica2part2dev_id查得Node的信息(包括id, IP, port等),再向Storage Server发送请求,等到Storage Server返回Response后,再回到Proxy进行后续同样地更新Container Server和Account Server。特别是在高并发的时候,Proxy的性能会非常受影响,进而势必影响Swift的读写性能。所以可以采用Proxy下沉到客户端来取缔Proxy,可由客户端来分散由于高并发引起的Proxy Latency,进而提升性能。

    本文参考自IBM的一篇介绍,非常适合入门和全局的理解!
    Reference:
    http://www.ibm.com/developerworks/cn/cloud/library/1310_zhanghua_openstackswift/index.html


    展开全文
  • Swift 开源项目汇总

    2019-09-16 16:07:05
    Swift 开源项目汇总

    1. 完整项目

    1.1 直播类App

    1. XJ直播是一款基于Swift3.0开发的直播平台,
    2. 整个项目使用MVVM架构 + 响应式编程 + Swift3.0进行编写,主要是练习swift3.0语法以及MVVM的使用;
    3. 本项目用到了视频的采集,编码,解码等技术,采用的是目前主流的第三方框架IJKMediaFramework;
    4. 在本地利用Socket搭建一个简易的服务器,用来模拟客户端和服务器之间的交互;
    5. 项目中主要用到了图文混排、弹幕、粒子动画、自定义动画等技术。
    6. Xib、Storyboard、Storyboard Reference、自动布局高级使用。
    7. UITabbarController、UICollectionView、UIScrollView高级使用。
    8. 下载地址:https://pan.baidu.com/s/1pKYx8Sj 密码: jkri
    • Swift 精仿有妖气漫画:90%精仿原版有妖气漫画(登录,充值等涉及版权或者会员等功能均不作实现),使用三方框架,感谢 SnapKit, Then, Moya, HandyJSON, Kingfisher, Reusable, LLCycleScrollView, MJRefresh, MBProgressHUD, HMSegmentedControl, IQKeyboardManagerSwift, EmptyDataSet-Swift

    Swift 精仿有妖气漫画

    1.2 安防类App

    1.3 新闻类App

    • xx头条:款专注郸城老家本地生活服务APP,集信息发布、商家品牌推广、相亲交友、求职招聘、找同学、吃喝玩乐等便民信息。(简述
    • DesignerNewsApp: Simple iOS client for Designer News, by the creator of Design+Code and the team, written in Swift.
    • SwiftHN: 一款新闻app. A Hacker News reader in Swift
    • 糗百客户端:基于swift,用糗事百科的API简单做了一个糗百客户端,可以浏览各种段子,浏览搞笑图片,查看精彩评论等.
    • 仿知乎日:用Swift实现的仿知乎日报应用
    • TodayNews: 高仿今日头条swift版本。
    • Swift 高仿喜马拉雅FM

      OC 版本:下载点击这里

    1.4 电商类App

    • 单糖:良品生活指南:家居零食、礼物美妆、海淘购物。(介绍文档

    单糖

    爱鲜蜂

    高仿小日子Swift2.0

    模仿“妙汇”电商项目

    Swift高仿项目,堆糖

    1.5 跨平台App

    1.6 工具类App

    1.7 游戏类App

    1.8 社交类App

    The Wire mobile app has an architectural layer that we call sync engine . It is the client-side layer that processes all the data that is displayed in the mobile app. It handles network communication and authentication with the backend, push notifications, local caching of data, client-side business logic, signaling with the audio-video libraries, encryption and decryption (using encryption libraries from a lower level) and other bits and pieces.

    • Yep: 一款轻量级社交app MVC + Rxswift编写

    Yep is a nifty and lightweight social networking app, revolving around the theme of “Meeting Genius”, enabling users to find experts or other learners of a particular domain.
    The project’s architecture is easy to understand, following the common software pattern MVC, separating UI, business logic and data model. Now, let’s dive into the Yep project!

    特色:

    1. 可以发送富文本,表情,图片还有声音。
    2. TSChatImageCell 中显示的图片使用 Mask Layer 进行绘制,你可以自由的切换聊天背景图,这样看上去 UI 也不会糟糕。
    3. 自定义的表情键盘,自定义的工具键盘。
    4. 录音的 wav 文件会被自动转换成 amr文件,这样可以方便和 Android 设备进行文件传输。这两种文件都已做了缓存。上传使用 amr 文件,播放使用 wav 文件。
    5. 当你点击声音 TSChatVoiceCell 的时候,它会自动检测缓存,如果没有那么会使用 Alamofire 自动进行下载。
    6. 当你发送图片的时候,系统会使用 Kingfisher 缓存机制存放在本地,上传成功后会自动修改成图片链接的 MD5 值文件名字。
    7. 数据是从 JSON 文件里面加载来的, 所有的 Model 是使用 ObjectMapper 创建的。
    • 聊天应用Acani Chats: Acani Chats是一款非常简单而又优雅的聊天应用,界面清爽明了。不过,别小看这一“简单”,通过它托管在Github上的源代码,你可以学习和探索类似应用开发所必须的语法。Acani Chats支持基础性的一些事件,比如联系人头像和现代化的iOS用户界面,绝对是你着手学习开发聊天应用的一个超好的案例。
      聊天应用Acani Chats

    1.9 音视频app

    1.10 图像处理app

    • PixPic: 一款swift编写的图像,相册处理app
    • Surge(Swift + Accelerate):Surge基于Accelerate框架开发,用于执行矩阵数学、数字信号处理以及图像处理等方面。换句话说,它可以快速处理复杂的数学、语音以及图像信号。并且依然来自于大神级人物mattt。
    • 图片查看器SwiftFlickrApp: SwiftFlickrApp是使用Swift编写的Flickr照片查看器。你可以在单独的页面上以网格或列表的形式浏览照片,或进行缩放处理。Github上托管的源码涉及不少知识点,会给你一些如何使用 UICollectionView和AFNetworking的见解和看法。

    1.11 生活助手类app

    花田小憩3.0.1

    1.12 支付app

    • Viewfinder : 移动支付公司 Square 将去年收购的照片应用 Viewfinder 开源了,包含服务端、iOS 和 Android 应用代码

    1.13 AR/VR 项目App

    在这里插入图片描述

    1.14 通讯录相关

    • 联系提醒应用iContactU:ContactU是一款用于提醒你联系指定人员的应用。在联系人中选择人员,设置提醒时间和内容,到了时间iContactU会提醒你,你可以直接在应用中通过邮件、短信、电话等方式联系对方。

    1.15 彩票类

    1.16 股票类

    1.17 摄像类

    1.18 阅读器类

    • Hacker News阅读器: Hacker News来自YCombinator,所以一直以来都有顶级创业者参与讨论,用户创造的内容质量也远高于其他创业者主题网站。最可贵的是Hacker News没有任何盈利企图,唯一的目的就是吸引和配有下一代创业者。而这里介绍的就是一款Hacker News阅读器,使用Swift语言编写,提供了一个不错的无缝用户界面,可以查看最新及最受喜欢的信息,并能够从中直接读取内容。

    2. 网络库

    基础网络框架

    1. Alamofire:著名的AFNetworking网络基础库Swift语言版

    2.1 Socket

    2.2 TCP/IP

    2.3 Websocket

    1. starscream:WebSocket客户端类库.

    2.4 WebRTC

    3. 数据处理

    3.1 Json数据解析

    • SwiftyJSON:GitHub上最为开发者认可的JSON解析类

    3.2 xml数据解析

    • AEXML:简单又易于的XML解析类及示例

    3.3 DB存储框架

    3.3.1 DB 处理框架

    • SQLite.swift:纯swift实现的类型安全的SQLite3封装,简单、轻量,使用上最SQL的SQLite封装库。特点:

    (1)简单的查询和参数绑定接口,
    (2)安全、自动类型数据访问
    (3)隐式提交和回滚接口
    (4)开发者友好的错误处理和调试
    (5)文档完善
    (6)通过广泛测试
    (7)支持全文检索
    (8)支持SQLCipher

    • Realm:志向代替Core Data和SQLite的移动数据库.Realm 是一个移动数据库,可运行于手机、平板和可穿戴设备之上。可以让你的应用更快速,带来难以想象的体验。其目标是为了代替 CoreData 和 SQLite 数据库。
      目前支持 iOS/OS X/Android 平台,提供 Swift/Objective-C/Java 语言的 API

    • WCDB.swift : 腾讯开发的,微信使用的开源框架。非常简单易用。

    • RealmIncrementalStore.swift - RealmIncrementalStore.swift:集 Realm 数据库和 CoreData 对象模型两者优势的 Realm 数据库访问类库。

    • fluent.swift - 纯swift实现的类型安全的SQLite3封装,数据存储和JSON解析是永恒的话题。

    • swiftydb - 是一个第三方 SQLite 工具,能够大大简化数据库操作。如果你不放心 Realm,那就用 SwiftyDB 吧。使用教程demo

    • SwiftStore - Key-Value store for Swift backed by LevelDB ?

    • SQL 数据库抽象层 API 库 Swift-Kuery

    Swift-Kuery是一个可插拔的SQL数据库驱动程序/ SDK抽象层。 其主要思想是提供一套API,能操作各种关系型数据库,目前支持PostgreSQL、SQLite、MySQL。
    虽然Swift-Kuery不是对象关系映射(ORM),但它为构建ORM提供了很大的基础。 如果您不想使用特定的数据库,允许在不同数据库之间轻松切换, Swift-Kuery将会很有用。

    • SQLite数据库工具包GRDB.swift: GRDB.swift是一个SQLite数据库工具包,主要用于应用程序的开发。
      与SQLite.swift或FMDB相比,GRDB可以为您提供大量的胶水代码。 与Core Data或Realm相比,它可以简化您的多线程应用程序。
    • Swift SQLite O-R 映射工具类库 swiftydb: swiftydb 是一个类型安全、基于协议的 SQLite 数据库工具类库,可以轻松的持久化任何对象,纯 Swift 实现。
    • Swift ORM框架 SwiftyDB: SwiftyDB是一个基于SQLite数据库的ORM框架,用Swift语言编写,同时支持Objective-C语言。
      其特性为:线程安全、嵌套事物、模型操作、支持迁移、使用方便。
    • Swift 面向协议的 Keychain 类库 Locksmith: Locksmith 是一个强大的,面向协议的类库,用于在 Swift 中使用 Keychain 。
    • 持久化类库Pantry: 可以持久化基础类型变量值的类库。

    3.3.2 CoreData 处理框架

    • Breeze - 用Swift写的一个轻量级的CoreData管理工具,并且还支持iCloud 。
    • AlecrimCoreData - Swift,更容易地访问 CoreData 对象封装类库。除了 CRUD,还提供指针定位,强大的排序、筛选,异步数据获取,以及独立线程后台存取数据。
    • Graph.swift - 设计新颖、使用简单基于 Core Data 的数据驱动框架库 (作者Daniel Dahan)
    • Swift 简便的 CoreData 查询类库 QueryKit: QueryKit,一种简便的类型安全的CoreData查询语言。支持Swift和Objective-C两种语言。
    • CoreValue: CoreValue是Core Data的轻量级封装框架。它也包含了一些简单的抽象,便于查询,更新,保存和删除。
    • SwiftRecord: 基于 Core Data 极为轻量、易用的对象持久化工具库。
    • AlecrimCoreData: AlecrimCoreData 是强大而简单的 Core Data 封装框架,使用 Swift 编写,灵感来自于MagicalRecord 和 LINQ。它提供了多种功能来获取、创建、更新以及保存实体,如果需要还可以在后台线程中获取和保存实体对象。未来将会为SQLite储存类型添加iCloud支持,添加聚合和迁移方法,添加文档源码,创建单元测试以及创建示例工程等。

    3.4 缓存处理

    • Cache.swift - 一款简单、易用的缓存库。支持 MemoryCache, DiskCache 以及前两项组合的 MultiCache。

    4. 图形处理

    4.1 图片处理

    • GPUImage2.swift - Swift 版基于 GPU 图像和视频处理框架库。
    • ShinpuruImage - Syntactic Sugar for Accelerate/vImage and Core Image Filters ?
    • core-image-explorer - Core Image 滤镜处理图片– swift ,Core Image 介绍
    • AAFaceDetection - AAFaceDetection–swift,简单、实用的面部识别封装库。虽然该技术从 iOS 5 发展,不过真正有趣的应用还不多。
    • Concorde - swift, Concorde, 一个可用于下载和解码渐进式 JPEG 的库, 可用来改善应用的用户体验。
    • ZoomTransition - swift, 通过手势操控图片的放大、缩小、旋转等自由变化效果的组件及示例。
    • AFImageHelper - swift,一套针对 UIImage 和 UIImageView 的实用扩展库,功能包含填色和渐变、裁剪、缩放以及具有缓存机制的在线图片获取。
    • PinterestSwift - swift,Pinterest 风格图片缩放、切换示例。
    • PhotoStackView-Swift - PhotoStackView——照片叠放视图,使用说明
    • MPParallaxView - 是用 Swift 写的类似 Apple TV Parallax 效果的视图。
    • SDECollectionViewAlbumTransition - 用自定义的 push 和 pop 实现了有趣的 iOS 相册翻开动画效果。
    • SKPhotoBrowser.swift - swift中规中矩、实用的图片浏览类库。示例也很完整。
    • Nuke.swift - 完整、强大、实用的图片管理类库。主要功能包括可定制装载,缓存,滤镜及尺寸变换。
    • PagingView.swift - 注重细节的自动布局分页视图组件。
    • SwViewCapture.swift - SwViewCapture.swift一个用起来还不错的iOS截图库.(支持截取所有内容, 适用于所有ScrollView组成的视图, 包括WebView)。
    • Filterpedia.swift - 强大的图片滤镜库演示。
    • preview-transition.swift - 通过向导式代码实现步骤,实现完整、自然流畅的图片预览及转场功能。
    • CartoonEyes.swift - 前置摄像头捕获图像后,采用 Core Image 脸部识别 CIDetector 和漫画效果滤镜复合出卡通效果眼睛。

    4.2 压缩

    4.3 svg图片处理

    4.4 统计图等绘制

    4.5 gif图片处理

    • gifu:高性能GIF显示类库

    4.6 网络图片处理

    • HanekeSwift:轻量带缓存高性能图片加载组件
    • Toucan:小而美的图片变换及处理类
    • DominantColor:提取图片主色示例项目
    • ImageScout:最小网络代价获得图片大小及类型

    5. 动画

    5.1 简单动画

    6. UI控件

    6.1 自动布局

    6.2 UI 适配

    6.3 UI 组件

    6.3.1 UILabel自定义

    6.3.2 UIButton自定义

    6.3.3 UISwitch自定义

    6.3.4 UIScroll自定义

    6.3.5 tabbar自定义

    6.3.6 UIProgress 进度条

    6.3.7 UIAlert 自定义

    6.3.8 图表绘制

    6.3.9 下拉刷新

    6.3.10 模糊效果

    6.3.11 自动布局

    6.3.12 富文本

    6.4 UI 地图类

    7. 框架类

    7.1 响应式框架

    • RxSwift - RxSwift:函数响应式编程框架。
    • RxPermission.swift - 通过绑定 RxSwift 实现的 RxPermission。
    • Permission.swift - 统一的 API 请求 iOS 本地设备及资源权限类库。
    • ReactiveAnimation - ReactiveCocoa 推出了一个叫 ReactiveAnimation 的子项目,直接用完全用 Swift 来实现了。

    8. 音视频处理

    8.1 音频处理

    • AudioKit: 音频合成、加工及分析平台(支持 iOS、OS X、tvOS)框架库。无论其易用性,还是功能性及专业性。

    8.2 视频处理

    8.2.1 摄像照相视频音频处理

    • CameraManager - 相机管理封装类库。看着极好用的样子—-swift。
    • recordDemo.swift - 一个Swift语言实现直接可以用的录音Demo
    • Swift-Radio-Pro - 集成 LastFM 的专业电台应用(基于 Swift 2.0)。
    • mobileplayer-ios.swift - 很不错的高度可定制播放器项目
    • Periscope-VideoViewController.swift - 简洁实用的视频快进、倒带控制视图类库。
    • SkfSwiftCammer - 一个相机demo,在oc里面调用了swift。
    • BMPlayer.swift - 基于 AVPlayer 使用 Swift 封装的视频播放器,方便快速集成,支持横屏、竖屏,上下滑动调节音量、屏幕亮度,左右滑动调节播放进度。

    9. 工具类

    9.1 “轮子”工具类

    • SwiftyJSON:GitHub上最为开发者认可的JSON解析类
    • Dollar.swift:Swift版Lo-Dash(或underscore)函数式工具库
    • OAuthSwift:国外主流网站OAuth授权类库
    • PathKit:小而美的路径管理类
    • XCGLogger:功能完整的日志管理类库
    • Surge:基于苹果Accelerate高性能计算框架封装库
    • Async:简洁的后台执行代码封装库
    • Euler:直观、简洁的数学表达式∛27÷3+∑[3,1,2]
    • LocationManager:地理位置管理封装库
    • Siren:当应用更新时,通知用户并提供App Store链接
    • BrightFutures:漫长或复杂计算由独立线程异步来完成
    • Design-Patterns:如何使用常用设计模式及示例

    9.2 框架类

    • AsyncDisplayKit:提供界面的高流畅性切换及更灵敏的响应
    • Cartography:基于代码级的自动布局封装框架
    • MMWormhole:iOS扩展与宿主应用的通讯框架
    • NetworkObjects:轻量版HttpServer框架,跨平台解决方案
    • ExSwift:包含一组标准类型和类的Swift扩展。
    • SwiftTask:SwiftTask是用Swift编写的状态机,综合了Promise+ progress + pause + cancel,使用SwiftState( Swift 编写的优雅的状态机)。
    • HanekeSwift:Haneke 是一个用 Swift 编写的轻量级iOS通用缓存,易于使用。

    9.3 PDF处理

    9.4 调式,测试

    • Sleipnir:Sleipnir是一个适用于Swift的BDD(Behavior Driven Development)风格框架,灵感来自于Cedar(一款BDD风格的Objective-C测试框架)。核心原则:Sleipnir不依赖NSObject,是纯Swift BDD测试框架;Sleipnir不使用XCTest;有着很好的命令行输出,支持自定义测试报告,等等。
    • Quick: Quick是一个适用于Swift和Objective-C的行为驱动开发测试框架。借鉴了RSpec,Specta以及Ginkgo

    9.5 函数响应式框架

    • Dollar.swift:Dollar是一个Swift库,提供了有用的函数式编程辅助方法,无需扩展任何内置对象。Dollar类似于Lo-Dash或者Javascript中的Underscore。

    10. 第三方框架,库

    11. MVVM框架项目

    11.1 RxSwift + Moya + MVVM + Realm项目

    swift文档资料类

    swift学习资料

    1. 苹果官方Swift文档《The Swift Programming Language》
    2. 苹果开发者Swift文档及介绍
    3. 网友整理的Swift中文文档《 Apple Swift编程语言入门教程中文版Apple官方Swift教程(Github协作翻译中)
    4. Apple Swift编程语言入门教程
    5. Swift语言概览中文版
    6. Swift英文文档markdown版
    7. Swift开发视频教程【入门篇】
    8. letsswift 编译的Swift中文教程
    9. Github上的Swift开源项目列表
    10. 国内第一个Swift社区
    11. SwiftGuide 很赞 的Swift学习资料
    展开全文
  • 目前随着公司开发模式的变更,swift也显得越发重要,相对来说,swift语言更加简洁,严谨.但对于我来说,感觉swift细节的处理很繁琐,可能是还没适应的缘故吧.基本每写一句代码,都要对变量的数据类型进行判断,还要进行强转...

    目前随着公司开发模式的变更,swift也显得越发重要,相对来说,swift语言更加简洁,严谨.但对于我来说,感觉swift细节的处理很繁琐,可能是还没适应的缘故吧.基本每写一句代码,都要对变量的数据类型进行判断,还要进行强转等等.

    好了,废话不多说了,直接把我对swift的语法的一些理解奉献给大家,希望能对学习swift语法的朋友有所帮助,如有不足之处,还请多多包涵,如果有错误之处,欢迎指正

    Swift 介绍

    简介
    Swift 语言由苹果公司在 2014 年推出,用来撰写 OS X 和 iOS 应用程序
    2014 年,在 Apple WWDC 发布

    历史
    2010 年 7 月,苹果开发者工具部门总监 Chris Lattner 开始着手 Swift 编程语言的设计工作
    用一年时间,完成基本架构
    Swift 大约历经 4 年的开发期,2014 年 6 月发表
     
    克里斯·拉特纳何许人?
              LLVM 项目的主要发起人与作者之一
              Clang 编译器的作者
              苹果公司『开发者工具』部门的主管
              领导Xcode、Instruments等编译器团队
              Swift的大部分基础架构均由他1人完成
     
    特点
         从它的语法中能看到`Objective-C、JavaScript、C#、Python`等语言的影子
         语法简单、代码简洁、使用方便
         可与Objective-C混合使用(相互调用)
         提供了类似 Java 的名字空间(namespace)、泛型(generic)、运算对象重载(operator overloading)
     
    为什么设计Swift语言
         让应用开发更简单、更快、更稳定
         确保最终应用有着更好的质量
     
    Swift初体验
    Playground是什么?
    从Xcode6开始出现(Swift开始出现)
    翻译为:操场/游乐场
    对于学习Swift基本语法非常方便
         所见即所得(快速查看结果)
         语法特性发生改变时,可以快速查看.
     
    一.Swift最基本的语法变化
     
    1.导入框架  
    1 OC: #import <UIKit/UIKit.h>
    2 Swift: import UIKit 
    2.定义标识符
         OC: int a = 20;
         Swift: let a : Int = 20   ==  let a = 20     
     
         定义标识符格式: let / var 标识符 : 数据类型 = 赋值    
         注意:let声明的是常量,不可以对常量重新赋值
     
         Swift中定义标识符,必须声明该标识符是变量(var)还是常量(let)
         注意:在swift中如果一行代码中只有一条语句,那么语句后面的 ;(分号) 可以省略
                 一行内有多条语句 ; 不可以省略,   不建议一行写多条语句
     
    3.Swift中的打印    
    1 OC: NSLog(@"Hello world”);  /  NSLog(@"%d", a);
    2 //Swift中字符串不需要@
    3 print(a)   /   print("hello world")
     
    二.常量&变量
     
    1.什么是常量和变量?
     
         在Swift中规定:在定义一个标识符时,必须明确说明该标识符是常量还是变量
         使用let来定义常量,定义之后,不能进行修改
         使用var来定义变量,定义之后,可以进行修改
     
    2.常量个变量的使用注意
     
         在使用中,建议先使用常量,这样更安全,防止不小心修改之前的值
         如果需要修改时,再把常量修改为变量
         注意:常量的本质是,指向的内存地址不能修改,但可以找到内存地址对应的对象,修改对象内部的属性
    1 let view : UIView = UIView()
    2 view.alpha = 0.5
    3 view.backgroundColor = UIColor.orangeColor()
     
    三.类型推导
     
    1.什么是类型推导?
         在Swift中,如果在定义标识符的同时直接对其进行赋值,那么系统会自动根据赋的值的数据类型,推导出标识符的类型
         如果定义标识符的同时直接赋值,那么标识符后面的数据类型可以省略
         可以通过option + 鼠标左键来查看标识符的类型
    1 let n = 3.14
    2 let View = UIView()
     
    四.Swift中的基本运算
     
    1.swift中在进行基本运算时,必须保证两个参与运算的值得数据类型一致,否则会报错
         因为swift中没有隐式转换
    1 let m = 20
    2 let n = 3.44
    3 // let result = m + n 错误写法 
    2.数据类型的转化
         将Int 转换为 Double  Double(m)
         将Double 转换为 Int Int(n)
    1 let a : Double = 2.44
    2 let b : CGFloat = 4.55
    3 let result1 = a + Double(b)
     
    五.逻辑分支
     
    1.什么是逻辑分支?
         分支就是if / witch / 三目运算符 等判断语句
         通过分支语句可以控制程序的执行流程
     
    2.if分支语句
     OC中if的写法 : BOOL --> YES/NO
    复制代码
    1  int a = 20
    2  if (a > 0) {
    3     NSLog(@"a大于0")
    4  }
    5  
    6  if (a) {
    7     NSLog(@"a不等于0")
    8  }
    复制代码
     Swift和OC的差异
        1. if后面的()可以省略掉
        2. swift中没有非0(nil)即真  Bool --> true/false
    复制代码
    1 let a = 20
    2 if a > 0 {
    3     print("a大于0")
    4 } else {
    5     print("a不大于0")
    6 }
    7  
    复制代码
    3.三目运算符
         swift中的三目运算符合OC没什么差别
    let result = m > n ? m : n
    4.guard的使用
     
         4.1 guard是Swift2.0新增的语法
     
         4.2 它与if语句非常类似,它设计的目的是提高程序的可读性
     
         4.3 guard语句必须带有else语句,它的语法如下:
     
              4.3.1当条件表达式为true时候跳过else语句中的内容,执行语句组内容
     
              4.3.2条件表达式为false时候执行else语句中的内容,跳转语句一般是return、break、continue和throw
    复制代码
     1 func online(age : Int , IDCard : Bool , money : Int)  {
     2     guard age >= 18 else {
     3         print("回家叫家长")
     4         return
     5     }
     6     guard IDCard == true else {
     7         print("回家拿身份证")
     8         return
     9     }
    10     guard money >= 5 else {
    11         print("回家拿钱")
    12         return
    13     }
    14     print("留下来上网")
    15 }
    16 online(19, IDCard: true, money: 4
    复制代码
     
    5.switch分支
     
         5.1苹果在swift中对swift中对switch进行了很大的加强
     
         5.2 Swift中switch和OC中switch的差异
              switch中后面的()可以省略
              case结束之后可以不加break,也不会产生case穿透
             补充:如果希望产生case穿透,需要在语句结束之后加上fallthrough
                     在swift中switch的case后面可以跟上多个条件, 并且多个条件以 , 分割 
    复制代码
    1 switch sex {
    2 case 0, 1:
    3     print("正常")
    4 default:
    5     print("非正常人")
    6 }
    复制代码

          5.3 swift支持多种数据类型判断 

    复制代码
     1 //浮点型switch判断
     2 switch m {
     3 case 3.14:
     4     print("m是π")
     5 default:
     6     print("m非π")
     7 }
     8  
     9 //字符串switch判断
    10 switch opration {
    11     case "+":
    12         result = a + b
    13     case "-":
    14         result = a - b
    15     case "*":
    16         result = a * b
    17     case "/":
    18         result = a / b
    19 default:
    20     print("非法操作")
    21 }
    22  
    复制代码
    判断区间类型
    什么是区间?
         通常我们指的是数字区间:0~10,100~200
    swift中的区间常见有两种
         半开半闭区间:0..<10 表示:0~9,不包括10
         闭区间:0...10 表示:0~10
    复制代码
     1 let score = 92
     2 
     3 switch score {
     4 case 0..<60:
     5     print("不及格")
     6 case 60..<80:
     7     print("及格")
     8 case 80..<90:
     9     print("良好")
    10 case 90...100:
    11     print("优秀")
    12 default:
    13     print("不合理分数")
    14 }
    15  
    复制代码


    六.循环
     
    常见的循环有:for/while/do while
    1.for循环
         1.1 OC中的for循环写法
    1   for (int i = 0; i < 10; i++) {
    2  
    3   }     
         1.2 swift中的写法 
    复制代码
     1 // 区间遍历 0..<10 0...9
     2 for i in 0..<10 {
     3     print(i)
     4 }
     5 
     6 for i in 0...9 {
     7     print(i)
     8 }
     9  
    10 // 如果一个标识符不需要使用, 那么可以通过 _ 来代替
    11 for _ in 0..<10 {
    12     print("hello world")
    13 }
    复制代码
    2.while循环
         2.1 OC中的写法
     int a = 20
     while (a) {
     }
         2.2 swift中的写法
              2.2.1while后面的()可以省略
              2.2.2没有非0(nil)即真
    1 var i = 10
    2 while i > 0 {
    3     print(i)
    4     i -= 1
    5 }
     
    3.do while循环
    复制代码
    1 // 区别: 不再使用do while --> repeat while
    2 var m = 0
    3 repeat {
    4     print(m)
    5     m += 1
    6 } while m < 10
    复制代码
     
    七.字符串
    1.字符串的介绍 
         1.1字符串在任何的开发中使用都是非常频繁的
     
         1.2OC和Swift中字符串的区别
              在OC中字符串类型时NSString,在Swift中字符串类型是String
              OC中字符串@"",Swift中字符串""
     
         1.3使用 String 的原因
              String 是一个结构体,性能更高
              NSString 是一个 OC 对象,性能略差
              String 支持直接遍历
              String 提供了 String 和 NSString之间的无缝转换
     
    2.字符串的定义
         2.1定义不可变字符串
    let str = "hello swift"
         2.2定义可变字符串
    var strM = "hello world"
    strM = "hello china" 
     
    3.获取字符串的长度
         先获取字符集合,再获取集合的count属性
    let length = str.characters.count
     
    4.遍历字符串
    for c in str.characters {
        print(c)
    }
     
     
    5.字符串的拼接
         5.1字符串之间的拼接
    let str1 = "Hello"
    let str2 = "World"
    let str3 = str1 + str2
         
         5.2字符串和其它标识符间的拼接
    let name = "lgp"
    let age = 18
    let height = 1.98
    let infoStr = "my name is \(name), age is \(age), height is \(height)"
     
         5.3字符串格式化
              比如时间:03:04  如果显示 3 : 4 就不好.所以需要格式化
    let min = 3
    let second = 4
    let timeStr = String(format: "%02d:%02d", arguments: [min, second]) 
     
    6.字符串的截取
     
         6.1简单的方式是将String转成NSString来使用
              在标识符后加:as NSString即可
    复制代码
    1 // 1.方式一: 将String类型转成NSString类型, 再进行截取
    2 // (urlString as NSString) --> NSString
    3 let header = (urlString as NSString).substringToIndex(3)
    4 let footer = (urlString as NSString).substringFromIndex(10)
    5 let range = NSMakeRange(4, 5)
    6 let middle = (urlString as NSString).substringWithRange(range)
    复制代码
         6.1Swift中提供了特殊的截取方式
              该方式非常麻烦
              Index创建较为麻烦
    复制代码
    1 // 2.方式二: Swift原生方式进行截取
    2 let headerIndex = urlString.startIndex.advancedBy(3)
    3 let header1 = urlString.substringToIndex(headerIndex)
    4 
    5 let footerIndex = urlString.endIndex.advancedBy(-3)
    6 let footer1 = urlString.substringFromIndex(footerIndex)
    7  
    8 let range1 = headerIndex.advancedBy(1)..<footerIndex.advancedBy(-1)
    9 let middle1 = urlString.substringWithRange(range1)
    复制代码
     
    八.数组的使用
     
    1.数组的介绍
         1.1 数组(Array)是一串有序的由相同类型元素构成的集合
         1.2 数组中的集合元素是有序的,可以重复出现
         1.3 Swift中的数组
              swift数组类型是Array,是一个泛型集合
     
    2.数组的初始化
         2.1 定义不可变数组,使用let修饰  
              注意:不可变数组要在定义的同时初始化,否则没有意义
         let array = ["why", "yz"]    
     
         2.2 定义可变数组,使用var修饰
              注意:数组是泛型集合,必须确定该数组中存放元素的类型
      //基本写法   
      var arrayM = Array<String>()
      //单写法
      var arrayM = [String]()
     
    3.对可变数组的基本操作(增删改查)
         3.1 添加元素
    arrayM.append("ljp")    
         3.2 删除元素 
    let removeItem = arrayM.removeAtIndex(1)  返回值为删除的元素
    arrayM.removeAll()
         3.3 修改元素
    arrayM[0] = "why"
         3.4 查找元素(根据下标获取元素)
    let item = arrayM[0]
     
    4.数组的遍历
         4.1 遍历下标值
    1 for i in 0..<array.count {
    2     print(array[i])
    3 }
         4.2遍历元素
    for name in array {
        print(name)
    } 
          4.3遍历下标值和元素
    1 for (index, name) in array.enumerate() {
    2     print(index)
    3     print(name)
    4 }

     

    5.数组的合并
         相同类型的数组可以相加进行合并
         可变数组和不可变的数组也能合并
    1 let array1 = ["why", "yz"]
    2 let array2 = ["lmj", "lnj"]
    3 let resultArray = array1 + array2
     
    九.字典的使用
     
    1.字典的介绍
         1.1字典允许按照某个键来访问元素
         1.2字典是由两部分集合构成的,一个是键(key)集合,一个是值(value)集合
         1.3键集合是不能有重复元素的,而值集合是可以重复的,键和值是成对出现的
         1.4Swift中的字典
              Swift字典类型是Dictionary,也是一个泛型集合
     
    2.字典的初始化
         2.1 定义不可变字典,使用let修饰  
              注意:不可变字典要在定义的同时初始化,否则没有意义
                系统会根据[]中存放的是键值对,还是一个个元素,来判断该[]是一个数组还是字典   
    let dict = ["name" : "why", "age" : 18, "height" : 1.88]
        
         2.2 定义可变字典,使用var修饰
              注意:字典是泛型集合,必须制定该数组中存放元素的类型
         基本写法      
    var dictM = Dictionary<String, NSObject>()
         简单写法   
    var dictM = [String : NSObject]() // 常见
     
    3.对可变字典的基本操作(增删改查)
         3.1 添加元素
    dictM.updateValue("why", forKey: "name")    
         3.2 删除元素 
    1 dictM.removeValueForKey("age") 
         3.3 修改元素
    1 //如果原有没有对应的key/value, 那么就添加键值对
    2 // 如果原有已经有对应的key/value, 那么直接修改
    3 dictM.updateValue("1.77", forKey: "height")
    4 dictM["name"] = "why"
         3.4 查找元素(获取元素)
    1 let item = dictM["name"]
     
    4.字典的遍历
         4.1 遍历字典中所有的key
    1 for key in dict.keys {
    2     print(key)
    3 }       
      4.2 遍历字典中所有的value
    1 for value in dict.values {
    2     print(value)
    3 }
          4.3 遍历字典中所有的key / value
    1 for (key, value) in dict {
    2     print(key)
    3     print(value)
    4 }
     
    5.字典的合并
         相同类型的字典也不可以相加进行合并
         可以把其中一个字典改为可变的,遍历不可变得字典,把元素一个一个的添加到另一个不可变字典中
    复制代码
    1 let dict1 = ["name" : "why", "age" : 18]
    2 var dict2 = ["height" : 1.88, "phoneNum" : "+86 110"]
    3 
    4 for (key, value) in dict1 {
    5     dict2[key] = value
    6 }
    7  
    复制代码
     
    十.元组的使用
    1.元组的介绍
         1.1元组是Swift中特有的,OC中并没有相关类型
         1.2它是什么呢?
              1.2.1 它是一种数据结构,在数学中应用广泛
              1.2.2 类似于数组或者字典
              1.2.3 可以用于定义一组数据
              1.2.4 组成元组类型的数据可以称为“元素”
     
    2.为什么使用元组?
         如果字典或数组保存多种数据类型,那么从字典会数组取出来的数据类型是NSObject,我们使用的时候很不方便,要先转换为真实类型
         元组保存多种数据类型,取出来就是该数据的真实类型,不需要转换就能直接使用
     
    3.元组的定义
    1 let infoTuple = ("why", 18, 1.88, "+86 110")
    2 // 使用元组描述一个人的信息
    3 ("1001", "张三", 30, 90)
    4 // 给元素加上元素名称,之后可以通过元素名称访问元素
    5 (id:"1001", name:"张三", english_score:30, chinese_score:90)

     

     

    前言:swift语法基础篇(二)来了,想学习swift的朋友可以拿去参考哦,有兴趣可以相互探讨,共同学习哦. 
     
     
    一.可选类型(重点内容)
     
    1.什么是可选类型?
     
         1.1在OC开发中,如果一个变量暂停不使用,可以赋值为0(基本属性类型)或者赋值为空(对象类型)
         1.2在swift开发中,nil也是一个特殊的类型.因为和真实的类型不匹配是不能赋值的(swift是强类型语言)
         1.3但是开发中赋值nil,在所难免.因此推出了可选类型
          目的:让代码更加严谨
     
    2.可选类型的取值
         可选类型要么是:空值 , 要么是:有值
         只有为可选类型,才能赋值为nil
     
    3.定义可选类型
           3.1 普通写法
      var name : Optional<String> = nil
       
           3.2 简单写法(语法糖) 
    var name : String? = nil
     
    4.给可选类型赋值
         4.1 普通赋值
    name = Optional("why")
         
         4.2 直接赋值
    name = "why" // 系统会对字符串进行包装Optional, 再进行赋值
     
    5.强制解包(取出可选类型中具体的值)
         5.1 为什么要强制解包
              可选类型的值的格式为: Optional("why") 我们要想使用其真实值,必须要解包
     
         5.2 怎么解包?
              在可选类型的标识符后面加!
     print(name!)
              注意:解包前,要先判断可选类型是否为nil , 如果为nil,强制解包会报错
     
    6.可选绑定
    1 if let name = name {
    2     print(name)
    3     print(name)
    4 }
         6.1 系统先判断可选类型是否为nil,如果为nil,就跳过{}中的内容
         6.2 如果有值,会先对name进行强制解包,然后把解包结果赋值给name
     

     

    7.可选类型的应用场景
         可选类型能让代码变得更加严谨
         类型转换后的数据类型一般都为可选类型,因为转换不成功就为nil , 只有可选类型能接收nil
     
         7.1字符串类型转Int类型
     
         
     
         可以看到,类型转换后的类型为可选类型
         使用转换后的结果的时候,一定要先判断是否为nil, 如果为nil, 使用会报错
     
         7.2 根据string创建一个NSURL
    复制代码
    1 let urlString = "www.baidu.com"
    2 
    3 let url : NSURL? = NSURL(string: urlString)
    4 if let url = url {
    5     let request = NSURLRequest(URL: url)
    6
    复制代码
         if let url = url 内部做了以下判断:
         1.判断url是否为nil,为nil 就跳过{}中的内容
         2.不为nil,就对url进行强制解包
         3.将解包后的结果赋值给前面的url
     
         7.3 根据一个plist文件创建一个数组
    1 let path = NSBundle.mainBundle().pathForResource("123.plist", ofType: nil)
    2 
    3 if let path = path {
    4     NSArray(contentsOfFile:path)
    5 }
     
    二.类型转化
    1.is的使用:判断是否是某种类型
    复制代码
     1 //1.is的使用
     2 let infoArray = ["why" , 18 , 1.98]
     3 let item = infoArray[1]
     4  
     5 //item.isKindOfClass(UIButton.self)
     6 
     7 //string是结构体,不能用isKindOfClass
     8 if item is String {
     9     print("是字符串")
    10 }else {
    11     print("不是字符串")
    12 }
    复制代码

     

    2.as的使用
         2.1 as直接使用: swift类型和oc类型的转化
    1 let urlString = "www.baidu.com"
    2 (urlString as NSString).substringToIndex(3)
     
         2.2 as? 的使用: 将NSObject转成某一确定类型的可选类型
     
    复制代码
     1 let item1 = infoArray[0]
     2 let name = item1 as? String
     3 if let name = name {
     4     print(name.characters.count)
     5 }
     6 简写:
     7 if let name = infoArray[0] as? String {
     8     print(name.characters.count)
     9 }
    复制代码
       
        2.3 as! 的使用: 将NSObject转成确定的类型,但如果转化不成功,则程序会崩溃
    1 let count = (infoArray[0] as! String).characters.count
     
    三.函数
     
    1.函数的介绍
     
        1.1 函数相当于OC中的方法
        1.2 函数的格式如下
         func 函数名(参数列表) -> 返回值类型 {
             代码块
             return 返回值
         }
         1.3 func是关键字,多个参数列表之间可以用逗号(,)分隔,也可以没有参数
         1.4 使用箭头“->”指向返回值类型
         1.5 如果函数没有返回值,返回值为Void.并且“-> 返回值类型”部分可以省略
     
    2.常见的函数类型
         
     
    3.内部参数和外部参数
     
         3.1 什么是内部参数
              3.11 在函数内部就可以看到的参数(标识符)就是内部参数
              3.12 默认所有的参数都是内部参数
     
         3.2 什么是外部参数
              3.21 在函数外部就可以看到的参数(标识符)就是外部参数
              3.22 默认从第二个参数开始都是外部参数
              3.23 如果想让第一个参数成为外部参数,可以设置标签:在变量名前加标签即可
              3.24 如果不想要外部参数,可以在参数名称前加_  (_ 和参数名称中间要加一个 空格)
              3.25 也可以给外部参数设置标签  ,相当于给外部参数起别名
     
     
    4.默认参数
         4.1 什么是默认参数?
              如果一个函数需要参数,而我们调用的时候又不想传参,那么在设计函数的时候,可以给函数一个默认的参数
              如果调用函数,不传参数,就按照默认的参数对函数进行处理
     

     

    5.可变参数
         5.1 swift中函数的参数个数可以变化,它可以接收不确定数量的输入类型参数
         5.2 它们必须具有相同的类型
         5.3 我们可以通过在参数类型名后面加入(...)的方式来指示这是可变参数
         作用:方便需求变化时,更改代码,当参数个数需求变更或很多时,可以轻松些代码

     

    6.指针参数
         6.1 默认情况下,函数的参数是值传递.如果想改变外面的变量,则需要传递变量的地址
         6.2 必须是变量,因为需要在内部改变其值
         6.3 Swift提供的inout关键字就可以实现
     

     

     
    7.函数的嵌套
         swift中函数是可以嵌套的,即函数中包含函数,但是不推荐该写法
         OC中方法不可以嵌套
    复制代码
     1 // 函数的嵌套
     2 let value = 55
     3 func test() {
     4     func demo() {
     5         print("demo \(value)")
     6     }
     7     print("test")
     8     demo()
     9 }
    10 demo() // 错误  必须在对应的作用域内调用
    11 test() // 执行函数会先打印'test',再打印'demo'
    复制代码
     
    8.函数的类型
         8.1什么是函数的类型?
              每个函数都有自己的类型, 函数的类型由 函数的参数类型和返回值类型组成
    复制代码
    1 // 定义两个函数
    2 func addTwoInts(a : Int, b : Int) -> Int {
    3     return a + b
    4 }
    5 
    6 func multiplyTwoInt(a : Int, b : Int) -> Int {
    7     return a * b
    8 }
    9  
    复制代码
         这两个函数的类型是 (Int, Int) -> Int
     
         8.2抽取两个函数的类型,并且使用
    复制代码
     1 // 定义函数的类型
     2 var mathFunction : (Int, Int) -> Int = addTwoInts
     3 
     4 // 使用函数的名称
     5 mathFunction(10, 20)
     6 
     7 // 给函数的标识符赋值其他值
     8 mathFunction = multiplyTwoInt
     9 
    10 // 使用函数的名称
    11 mathFunction(10, 20)
    复制代码
     
         8.3 函数作为方法的参数
    复制代码
    1 // 3.将函数的类型作为方法的参数
    2 func printResult(a : Int, b : Int, calculateMethod : (Int, Int) -> Int) {
    3     print(calculateMethod(a, b))
    4 }
    5 
    6 printResult(10, b: 20, calculateMethod: addTwoInts)
    7 printResult(10, b: 20, calculateMethod: multiplyTwoInt)
    复制代码
     
         8.4函数作为方法的返回值
    复制代码
     1 // 1.定义两个函数
     2 func stepForward(num : Int) -> Int {
     3     return num + 1
     4 }
     5 
     6 func stepBackward(num : Int) -> Int {
     7     return num - 1
     8 }
     9 
    10 // 2.定义一个变量,希望该变量经过计算得到0
    11 var num = -4
    12 
    13 // 3.定义获取哪一个函数
    14 func getOprationMethod(num : Int) -> (Int) -> Int {
    15     return num <= 0 ? stepForward : stepBackward
    16 }
    17  
    18 // 4.for循环进行操作
    19 while num != 0 {
    20     let oprationMethod = getOprationMethod(num)
    21     num = oprationMethod(num)
    22     print(num)
    23 }
    复制代码

     
    四.枚举类型
     
    1.什么是枚举?
     
         1.1 枚举是一种数据类型,枚举是一组具有共同特性的数据的集合
         1.2 枚举可以让我们更安全的使用数据
         1.3 oc中只能对枚举成员赋值为整型
         1.4 swift中的枚举更加灵活,可以给每一枚举成员赋值,这些值可以为字符,字符串,整型,浮点型等
     
    2.枚举类型的定义
     
         2.1 使用enum关键词,把枚举定义在一个大括号内
    1      enum <#name#> {
    2           case <#case#>
    3      }
     
         2.2 case关键词表明新的一行成员值将被定义
     
         2.3 不像 C 和 Objective-C 一样,Swift 的枚举成员在被创建时不会被赋予一个默认的整数值 (0.1.2.3...)
     
         2.4 定义方式二: 多个成员值可以写在同一行
    1 enum Planet {
    2   case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
    3 }
         
    3.枚举类型的使用
    复制代码
     1 enum Direction : Int{
     2     case East = 1 , West , North , Sourth
     3 }
     4 //完整写法
     5 let d : Direction = Direction.North
     6 //简单写法:根据上下文能推导出确定的类型
     7 var d1 = Direction.East
     8 d1 = .West
     9 
    10 //枚举类型的使用
    11 let btn = UIButton(type: .Custom)
    复制代码
     
    4.给枚举类型赋值
     
         4.1 枚举类型赋值可以是字符串/字符/整型/浮点型
         4.2 注意: 如果给枚举类型赋值, 则必须在枚举类型后面说明赋值的类型 
         4.3 给枚举类型赋的值的类型,必须跟说明的类型一致,否则会报错
    复制代码
     1 let btn = UIButton(type: .Custom)
     2 
     3 enum Direction : String{
     4     case East = "1"
     5     case West = "3"
     6     case North = "6"
     7     case Sourth = "9"
     8 }
     9 var b : Direction = .West
    10  
    11 let a = Direction(rawValue: “6”) 
    复制代码
      
    注意: 通过rawValue:方式取出来的值的类型  为 对应类型的 可选类型
         因为通过这种方法不一定能取出来值,可能为nil   所以为可选类型  比较严谨
     
         4.4给枚举类型赋值方式二
    1      enum Direction2 : Int{
    2          case East = 1 , West , North , Sourth
    3 //只要给第一个成员赋值,会自动按照递增的方式给后面的成员赋值
    4 //相当于 West = 2, North = 3, Sourth = 4
    5 //注意:这种赋值方法只对整型有效,赋值其它类型无效
     
    五.结构体
     
    1.什么是结构体?
     
         1.1结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合
         1.2结构体(struct)指的是一种数据结构
         1.3结构体是值类型,在方法中传递时是值传递
     
    2.结构的定义格式
     
    struct 结构体名称 {
        // 属性和方法
    }
     
    3.在swift中对结构体进行了很大的增强
     
         3.1 扩充构造函数
     
              3.11 默认情况下,创建Location  只能使用Location (x: Double , y : Double) 方法创建
              3.12 但为了让结构体的使用更加灵活, swift中,可以对其构造方法就行扩充 
              3.13 注意: 结构体中值必须为可变的var ,否则没有意义
     
            
            

     

     
         3.2 为结构体扩充方法
      
     
    六.类
     
    1.类的介绍和定义
     
         1.1 Swift也是一门面向对象开发的语言,面向对象的基础是类,类产生了对象
     
         1.2 在swift中如何定义类?
              class是Swift中的关键字,用于定义类
     
         1.3 定义的类,可以没有父类.那么该类是rootClass
     
         1.4 通常情况下,定义类时.继承自NSObject
     
    复制代码
     1 class 类名 : SuperClass {
     2     // 定义属性和方法
     3 }
     4 class Person {
     5     var name : String = ""
     6     var age : Int = 0
     7 }
     8 let p = Person()
     9 p.name = "lkj"
    10 p.age = 18    
    复制代码
     
    2.类的属性定义
     
         2.1类的属性介绍:Swift中类的属性有多种
         存储属性:存储实例的常量和变量
         计算属性:通过某种方式计算出来的属性
         类属性:与整个类自身相关的属性
     

     

    3.监听属性的改变
     
         3.1 在OC中我们可以重写set方法来监听属性的改变 (kvc监听系统属性的改变)
         3.2 Swift中可以通过属性观察者来监听和响应属性值的变化
         3.3 通常是监听存储属性和类属性的改变.(对于计算属性不需要定义属性观察者,因为可以在计算属性的set方法中直接观察)
         3.4 怎么监听?   
              通过willSet 和didSet两个方法来监听 , 统称为属性监听器
              

     

    4.类的构造函数
     
         4.1 构造函数类似于OC中的初始化方法: init方法
         4.2 创建一个类,系统会默认提供一个构造函数
     
         4.3 自定义构造函数 , 一般用来初始化时给属性赋值
              注意:如果自定义构造函数,会覆盖系统默认提供的构造函数 , 如果想保留,需要重写
    复制代码
     1  class Person {
     2      var name : String
     3      var age : Int
     4      // 自定义构造函数,会覆盖init()函数
     5      init(name : String, age : Int) {
     6      // 如果在一个方法中, 属性名称产生了歧义(重名), self.不可以省略
     7          self.name = name
     8          self.age = age
     9      }
    10 }
    11 // 创建一个Person对象
    12 let p = Person(name: "why", age: 18)
    13  
    14  
    15 class Person: NSObject {
    16     var name : String
    17     var age : Int
    18     // 重写了NSObject(父类)的构造方法  在init前面加上override
    19     override init() {
    20         name = ""
    21         age = 0
    22     }
    23 }
    24 // 创建一个Person对象
    25 let p = Person()
    复制代码
     
    5.字典转模型
         5.1 使用kvc的条件?
              5.11 必须继承自NSObject
              5.12 在构造函数中使用,必须先调用super.init()
     
         5.2 利用kvc字典转模型非常方便
     
         5.3 使用kvc注意点
              5.31 属性需要有默认的值
              5.32 基本数据类型默认值一般设置为0
              5.33 对象或结构体类型定义为可选类型(可选类型没有赋值前为nil)
    复制代码
     1 class Person: NSObject {
     2     // 结构体或者类的类型,必须是可选类型.因为不能保证一定会赋值
     3     var name : String?
     4     // 基本数据类型不能是可选类型,否则KVC无法转化
     5     var age : Int = 0
     6     // 自定义构造函数,会覆盖init()函数
     7     init(dict : [String : NSObject]) {
     8         // 必须先初始化对象
     9         super.init()
    10         // 调用对象的KVC方法字典转模型
    11         setValuesForKeysWithDictionary(dict)
    12     }
    13  //如果字典中某些键值对,在类中找不到对应的属性,就会报错
    14     //不想让它报错,可以重写setValue  forUndefinedKey key:
    15     override func setValue(value: AnyObject?, forUndefinedKey key: String) {
    16     }
    17 }
    18 // 创建一个Person对象
    19 let dict = ["name" : "why", "age" : 18]
    20 let p = Person(dict: dict)
    复制代码
     
    6.析构函数
         6.1 swift会自动释放不需要的实例以释放资源
              6.11 swift 通过ARC 处理实例的内存管理
              6.12 当引用计数为0时,系统会自动调用析构函数(不可以手动调用)
              6.13 通常在析构函数中释放一些资源(如:移除通知等操作)
     
         6.2 析构函数的写法
    1 deinit {
    2     // 执行析构过程
    3 }
     
              6.21 示例练习
    复制代码
     1 class Person {
     2     var name : String
     3     var age : Int
     4  
     5     init(name : String, age : Int) {
     6         self.name = name
     7         self.age = age
     8     }
     9  
    10     deinit {
    11         print("Person-deinit")
    12     }
    13 }
    14 
    15 var p : Person? = Person(name: "why", age: 18)
    16 p = nil
    复制代码

     

    前言:swift语法基础篇(二)来了,想学习swift的朋友可以拿去参考哦,有兴趣可以相互探讨,共同学习哦. 
     

     
    一.自动引用计数
     
    1.自动引用计数工作机制
         1.1 swift和oc一样,采用自动引用计数来管理内存
         1.2 当有强引用指向对象,对象的引用计数 +1 , 强引用消失,自动计数 -1
         1.3 如果对象的引用计数为0 , 那么该对象会被销毁
     
    2.循环引用
         2.1 什么是循环引用?
              两个(或多个)对象互相强引用
     
         2.2 循环引用对项目有什么影响
              循环引用会让对象不会被销毁,一直保存在内存中,可能导致项目运行不畅
         
         2.3 怎么解决强引用问题?
              只需要让其中一个对象对另外一个对象的引用变为弱引用即可
              在swift中用waek 相当于OC中的 __weak ,或者使用 unowned 相当于OC中的 __unsafe_unretained
     
    3.weak 和 unowned的区别
         3.1 相同点:
              都是一个弱引用,不会对对象进行retain
         
         3.2 不同点
              3.21 weak(__weak) :当弱引用指向的对象销毁时,该引用会指向nil   所以用weak指向的数据类型为可选类型
              3.22 unowned(__unsafe_unretained) :当弱引用指向的对象销毁时,依然指向原来的内存地址, 容易产生错误(野指针/访问僵尸对象)
              3.23 unowned不支持可选类型
     
    二.可选链
     
    1.什么是可选链?
         简单的说,就是可选类型的对象组成的链条
     
    2.为什么会产生可选链?
         2.1 假设有三个类, 人,狗,玩具   
         2.2 人里面有狗这个属性,狗里面有玩具这个属性, 玩具里面有价格这个属性
         2.3 把玩具这个对象赋值给狗(让狗拥有玩具), 把狗这个对象赋值给人(让人拥有这只狗)
         2.4 想要通过人来修改玩具的价格,就需要 person.dog.toy.price 来修改
         2.5 person.dog这个值得类型是可选类型 ,因为人的狗属性可能为nil   属性,想要使用person.dog ,必须要强制解包
         2.6 person.dog.toy也是可选类型,像这样由可选类型的对象组成的链条就是可选链
         
    3.可选链注意点
         3.1 利用可选链赋值的时候一定要解包
         3.2 利用可选链取值的时候也不要忘记解包
         3.3 利用可选链调用方法的时候 也要先解包
     
    4.利用可选链赋值, 取值,调用方法
         4.1 给可选链赋值:
    复制代码
    1  person.dog!.toy!.price = 50 太危险  强制解包,如果没值,直接程序崩溃
    2  
    3 if let dog = person.dog {
    4     if let toy = dog.toy {
    5         toy.price = 50
    6     }  这样解包虽然安全,但是太麻烦
    7 }
    复制代码
    苹果在swift中推荐使用这种方式来给可选链赋值
    1 person.dog?.toy?.price = 50 
    2 //当person.dog 为nil的时候,后面的操作就不再执行
     
         4.2 从可选链取值: 从可选链中取出的值得类型一定是可选类型 (有可能取不到)
    let price = person.dog?.toy?.price
     
         4.3 可选链调用方法:系统会自动判断可选类型是否有值
    person.dog?.toy?.flying()
     
    三.协议
     
    1.如何定义协议
     
         1.1 swift中协议的方式和类,结构体,枚举相似
    protocol SomeProtocol {
        // 协议方法
    }
     
         1.2 例如:定义一个运动协议
    1 protocol SportProtocol {
    2     func playBasketball()
    3     func playFootball()
    4 }
     
    2.声明一个类,并且遵守协议
         2.1 声明一个基类(不继承其它类),并遵守协议
    复制代码
     1 class SomeClass:FirstProtocol,AnotherProtocol {
     2     // 类的内容
     3     // 实现协议中的方法
     4 }
     5  
     6      例如:
     7 class Person : SportProtocol {
     8     var name : String = ""
     9    
    10     func playBasketball() {
    11         print("打篮球")
    12     }
    13    
    14     func playFootball() {
    15         print("踢足球")
    16     }
    17 }
    复制代码
     
         2.2 类继承自其他类,并遵守协议
    1 class SomeClass:SomeSuperClass, FirstProtocol,AnotherProtocol {
    2     // 类的内容
    3     // 实现协议中的方法
    4 }
     
    3.OC swift不支持多继承, 但是可以通过协议,间接实现多继承
     
    4.协议的继承关系
         4.1 swift中的及协议和OC(NSObject)中的不同 是:NSObjectProtocol
    1 protocol CrazySportProtocol : NSObjectProtocol {
    2     func jumping()
    3 }
     
         4.2 一个协议,可以遵守另一个协议
    1 protocol SportProtocol : CrazySportProtocol {
    2     func playBasketball()
    3 }
         当一个类遵守了这个协议(SportProtocol) 相当于也遵守了CrazySportProtocol 协议, 所以必须实现这两个协议中的方法
     
    5.协议的可选性
     
         5.1 OC中协议可以定义为可选和必选,默认是必选的
         5.2 默认情况下,swift中的协议都是必须实现的 ,否则编译器会报错
         5.3 在swift中如何让协议成为可选的(不用必须实现)
              要在协议前加 @objc ,可以保留OC某些特性,在方法前加optional  该方法就是可选的了
              在实现协议方法时,在方法前面也要加@objc
     
    复制代码
     1 @objc protocol SportProtocol {
     2     func playBasketball()
     3     func playFootball()
     4   //加optional该方法就成为可选的了
     5     optional func jumping()
     6 }
     7  
     8 class Person: SportProtocol {
     9     @objc func playBasketball() {  在方法前也要加上关键字@objc,不管是可选还是必选
    10     } 
    11     @objc func playFootball() {
    12     } 
    13     @objc func jumping() { 
    14     }
    15 }
    复制代码

     

    6.协议在代理模式中的使用
         6.1 一般来说协议都用weak来修饰(弱引用)
         6.2 weak只能用来修饰类
         6.3 在swift中协议既可以被类遵守,也可以被结构体,枚举遵守
     
         
         6.4 如何让协议只能被类准守
              在协议名称后面加上  :class  即可
     
    四.闭包
     
    1.什么是闭包?
         闭包和OC中的block非常相似,一般都用来函数的回调
     
    2.block的回顾
         block作为属性的格式: `@property (nonatomic, strong) void(^finishedCallback)(NSString *)`;
         block作为参数的定义格式:` (void (^)(NSString * str))finishedCallback`
     
    3.闭包的格式:
         (参数列表) -> (返回值类型)
     
    4.闭包的使用

     

    5.尾随闭包
    复制代码
     1  // 尾随闭包 : 如果函数的最后一个参数是一个闭包.那么可以将函数调用写成尾随闭包
     2  //就是把闭包写到()的后面, 本来是写在()里面的
     3  tools?.loadData() { (result) in
     4      print("在ViewController中获取到数据:\(result)")
     5  }
     6        
     7  // 如果函数有且只有一个参数,并且是一个闭包, 那么()也可以省略
     8  tools?.loadData { (result) in
     9      print("在ViewController中获取到数据:\(result)")
    10  }
    复制代码

     

    6.闭包的循环引用
     
         6.1 一般在定义工具类的时候,会在工具类的方法中用到闭包
         6.2 当工具类对闭包有强引用,一个控制器又调用包含该闭包的方法,在闭包方法中使用控制器的属性,就会发生循环引用
         6.3 控制器调用方法,就会对工具类有一个强引用, 闭包又拿到控制器的属性,闭包对象就对控制器有一个强引用
     
         6.4 在内存中就相当于这种表现
              

     

    7.怎么解决闭包的循环引用
     
         与oc中类型,只需要把闭包对控制器的引用改为弱引用
     
    8.怎么改?
     
         当闭包修改控制器的属性时,拿到控制器的属性时,把self(控制器)改成weakself即可
    weak var weakself : ViewController? = self
          
     
    五.swift项目的目录结构简介
     
    1.swift项目目录中没有.h和.m的文件, 只有一个.swift的文件,相当于
    2.swift目录中.swift文件就相当于oc中的.h和.m文件
    3.在swift中,调用项目中的其他源文件不需要导入头文件  (一个 .swift文件就是一个源文件)
     
     
    六.懒加载
     
    1.懒加载的介绍
         1.1 和OC中不同,swift有专门的关键字实现懒加载
         1.2 懒加载本质:当第一次使用时再加载,而且只会被加载一次
         
    2.swift中用lazy关键字来实现懒加载
         2.1 懒加载格式  
    lazy var 变量: 类型 = { 创建变量代码 }()
         = 后面是一个闭包 苹果推荐用闭包来实现懒加载,可在闭包中对变量属性进行初始化
     
         2.2 懒加载的使用
    1 lazy var names : [String] = {
    2   return ["why", "yz", "lmj"]
    3 }()

              当执行到上面代码的时候,names不会被加载到内存中, 当names第一次使用时,才会被加载

              无论names使用多少次,只会被加载一次,也就是说内存中只有一个names属性地址
     
     
    七.swift中的常见注释
     
    1.单行注释
         和OC中的单行注释一样  使用  // 注释内容
     
    2.多行注释
         和OC中的多行注释格式一样   /*  注释内容  */
         不同的是,swift中多行注释可以嵌套使用
     
    3.文档注释
         与oc中不一样  , swift中 用 /// 注释内容  来实现文档注释
     
    4.分组注释
         和oc不一样 oc:  #pragma mark - 注释内容
         swift:  //MARK : - 注释内容
     
    八.访问权限
     
    1.internal :内部的
         1.1 当不指定具体的访问权限时,默认为internal
         1.2 internal的访问权限: 在当前项目(包)的任何地方都能访问
     
    2.private : 私有的
         private的访问权限: 在当前源文件中能够访问    一个 .swift文件就是一个源文件
     
    3.public :公共的
         3.1 public的访问权限 : 可以跨包访问
         3.2 包的概念: 就是一个项目或一个框架   UIKit也是一个框架
     
    九.异常处理
     
    1.在swift中,如果一个方法的最后有一个throws,那么这个方法能抛出异常
         正则表达式就能抛出异常:
         NSRegularExpression(pattern: <#T##String#>, options: <#T##NSRegularExpressionOptions#>)
     
     
    2.如果一个方法抛出异常,必须要对异常进行处理,否则编译报错
     
    3.异常处理的三种方式
         3.1 try : 手动处理异常,可以拿到异常(error)
              要在方法前面加上try   而且外面要用do 包装
        
          //try方式 --> 手动处理异常, 并且可以获取到最终的异常结果
          do {    //如果有异常error有值      
          let regex = try NSRegularExpression(pattern: "", options: .CaseInsensitive)
          } catch {     //通过error拿到异常结果
               print(error)
          }

     

         3.2 try? : 系统处理异常
     
         try?方式 : 如果有异常,则返回nil,如果没有异常,则返回结果   结果(regex)为可选类型
                 let regex = try? NSRegularExpression(pattern: "", options: .CaseInsensitive)
                 regex?.matchesInString("", options: [], range: NSMakeRange(0, 0))
     
         3.3 try! :告诉系统不可能有异常
     
               try!方式(不推荐)  注意:一旦发生异常,程序就会崩溃
                let regex = try! NSRegularExpression(pattern: "", options: .CaseInsensitive)
     
    十.如何抛出异常
    1.在方法参数的后面加上 throws  ,一定要有返回值
     
    2.在某些具体的情况下抛出异常
         比如:传的参数不对等等  ,内部要对参数进行判断
     
    3.抛出的异常,一般定义为枚举类型  枚举后面要跟上 ErrorType 这种类型
     

     

    十一.OC和swift相互调用
     
    1.swift中调用oc
         1.1 创建一个桥接文件 (.h的文件) 文件名一般为 Bridge.h
         1.2 在桥接文件中导入oc的头文件
         1.3 配置桥接文件     工程 —> BuildSetting —> 搜索bridging   在后面写入Bridge.h 的相对路径
     

     

    2.oc中调用swift
         2.1 项目名称要规范 (不能有中文和特殊字符)
         2.2 swift中的类,属性,方法名 前面要加 public
         2.3 在oc文件中导入  工程名-Swift.h  的头文件  工程名-Swift.h  系统会自动生成
     

     

    展开全文
  • swift之GCD基础

    2020-05-04 14:59:59
    IOS之GCD(swift)队列1.主队列2.串行队列3.并行队列3.1创建队列方法参数解释4.同步任务(串行队列)4.1串行队列中新增同步任务4.2串行队列中嵌套本队列的同步任务4.3并行队列中嵌套本队列的同步任务4.4.串行队列中...
  • swift项目初探

    2017-11-24 19:18:37
    装上了黑苹果,因此也有机会接触到IOS相关的内容,所以大致过了一遍swift3的语法;养了一只小仓鼠,生活变的有趣许多;整理了一下github,切换了主邮箱。其他来说,生活依旧在继续,折腾一直未停止,为了生活,为了编程.我...
  • Swift结合了 C 和 Objective-C的优点并且受C兼容 性的限制。Swift采用安全的编程模式并添加了很多新特性,这将使编程更简单,更灵活,也更有趣。Swift是 基于成熟而且倍受喜爱的 Cocoa 和 Cocoa Touch框架,他的...
  • Swift纯代码构建项目框架 (简单的一个搭建项目的框架结构)AppDelegate.swift中代码 (主要是设置根控制器)import UIKit@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate {var window: ...
  • Swift语言快速入门

    2014-10-17 09:31:15
    Swift语言快速入门(首部同步新版官方API文档和语法的Swift图书,确保代码可编译,作者专家在线答疑,图书勘误实时跟进) 极客学院 编著  ISBN 978-7-121-24328-8 2014年10月出版 定价:59.00元 428页 16开 ...
  • 1 概述 OpenStack Object Storage(Swift)是OpenStack开源云计算项目的子项目之一。Swift的目的是使用普通硬件来构建冗余的、可扩展的分布式对象存储集群,存储容量可达PB级。...Swift是文件系统或...
  • 系统: Mac OS 10.15.2, XCode 11.3,swift 5.0 写作时间:2019-12-23 1. 说明 开发iOS App,基本上没有从零开始构建。因为系统以及提供了很多Framework,比如UIKit, Foundation, WebKit,MapKit等,GitHub上也有很...
  • 项目需求:做一个图片浏览器,点击图片查看大图,大图模式下,左右滚动能查看不同的图片. 项目的主要核心技术:图片的弹出和消失动画     一.... swift中,代码全部写在一起,阅读性极差   2.如何对代码进行抽取?
  • swift基本语法

    2016-04-26 15:43:16
    一 可选类型知识点补充1 例一 : 强转并且制定类型let str = "23" //打印...—-> 1.2 解答 : 因为很有可能是转化成功的,如果此时不用可选类型来接收的话,万一转化失败,那么该值就为空(nil),如果选择可选类型来接收
  • SBLayout是基于Swift4 封装系统的约束而成的单文件约束框架,总代码700多行,支持iOS9及其以上版本,一行代码可以搞定任意难度的AutoLayout约束 使用方法如下: 实例: let redView = UIView() re
  • 【分享】哪个OS X版本支持哪个Xcode的版本? 在安装Xcode时,会碰到跟OS X操作系统匹配的问题,对照下下面几个表,以免给自己带来编译不过或者奇怪的错误等问题
  • AAInfographics(AAChartKit-Swift) ----- 一款极其精美的开源图表库AAChartKit现更有Swift版本可用 您所喜爱的AAInfographics开源图表库同时更有Objective-C版本可供使用,详情请点击以下链接 传送门 ...
  • Integers ...IntegerTypes.swift.gyb Integers.swift IntegerParsing.swift 源码链接:https://github.com/apple/swift/blob/master/stdlib/public/core   .gyb文件是什么 GYB(...
  • 我遇到的问题就是在使用WebView加载网页的时候图片显示(我手机系统是5.1.1),当时出现这个问题我就想当然的以为,是不是给WebView少设置的什么东西。然后百度一下: mWebview.getSettings()....
1 2 3 4 5 ... 20
收藏数 813
精华内容 325