如何构造Swift框架(1)知识储备

[中文教程]
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
查看查看167 回复回复2 收藏收藏2 分享淘帖 转播转播 分享分享 微信
查看: 167|回复: 2
收起左侧

[中文教程] 如何构造Swift框架(1)知识储备

[复制链接]
Caesar 发表于 2016-10-14 10:19:38 | 显示全部楼层 |阅读模式
快来登录
获取优质的苹果资讯内容
收藏热门的iOS等技术干货
拷贝下载Swift Demo源代码
订阅梳理好了的知识点专辑
本帖最后由 Caesar 于 2016-10-14 10:46 编辑


就像从零搭建OC项目Service支撑一样,要在一定量知识储备的情况下才可以让框架质量得到保证。 Service:项目支撑服务。每个公司的项目都会依赖一定量的组件,每一个组件作为项目的独立模块提供本身的功能供使用。一般来讲,大公司的项目均为组件化项目,一方面为了方便安排开发,另一方面方面集成管理与扩展。小公司的项目则是依赖某一个私有库作为项目的支撑。建议每一个项目都按照组件化项目的思想来从零构造,便于以后核心服务代码抽出进行组件化拆分。




1、Swift知识储备与框架需求

知识储备即熟悉Swift编码思想,基础知识,进阶知识与细节知识,这里不做赘述。编码能力不能做衡量,即便知识储备不够,也可以慢慢的学习。给出的建议是:多阅读源码,不是大型开源项目的源码,而是小而精的功能性项目源码。

框架需求:按照开发步骤

  • [ ] 环境管理 ModeSwitch


控制当前环境,开发,预发,正式

  • [ ] 日志模块-基础日志 Console


完成基础日志模块,日志打印即可

  • [ ] 功能扩展 Foundation


Foundation常用的扩展

  • [ ] 界面扩展 UIKit


UIKit常用的扩展

  • [ ] 权限服务 Permission


获得应用程序权限

  • [ ] 组件处理/链接模块 Plug-in


组件化方案,组件加载,组件衔接(Router)

  • [ ] 数据模块 DataBase


数据持久层,暂定使用数据库

  • [ ] 网络模块 Network


网络层,网络请求,缓存控制,图片缓存等

  • [ ] 日志模块-日志上报 Console


完成日志上报功能(一般为奔溃上报)

  • [ ] 推送服务 Push


远程推送与本地推送服务

  • [ ] 位置服务 Location


位置获取服务

  • [ ] 数据收集服务 Monitor


数据打点与用户信息上报等




2、规范、知识 (仅了解基础的开发者阅读<知识回顾>)

别急着入手写代码,先制定一系列的规范,了解一些知识。



2.1、规范

Swift版本:3.0

Xcode版本:8.0+

支撑版本:iOS 8.0+

缩进: 2

如何构造Swift框架(1)知识储备 1

如何构造Swift框架(1)知识储备 - 敏捷大拇指 - 如何构造Swift框架(1)知识储备 1


单例写法与命名规范:

如何构造Swift框架(1)知识储备 2

如何构造Swift框架(1)知识储备 - 敏捷大拇指 - 如何构造Swift框架(1)知识储备 2


闭包预定义:

如何构造Swift框架(1)知识储备 3

如何构造Swift框架(1)知识储备 - 敏捷大拇指 - 如何构造Swift框架(1)知识储备 3


函数参数与规范:

使用 _来忽略参数描述,使用@discardableResult来标记该函数可以忽略返回值,尽量不要使用关键字。尽量避免函数复杂程度(不要写内部函数等)。命名明确,可选参数要写默认值。

注释规范:

[Swift] 纯文本查看 复制代码
// MARK: Initializing


[Swift] 纯文本查看 复制代码
/// Returns a matched view controller from a specified URL.
///
/// - parameter url: The URL to find view controllers.
/// - returns: A match view controller or `nil` if not matched.


MARK、TODO等使用//开头,默认注释使用///开头。

好的规范可以提高代码的可读性与统一性


上边只是一些简单的规范。还有很多好的建议,我们慢慢聊。



2.2、知识


2.2.1、空值合并

a ?? b 中的 ?? 就是是空值合并运算符,会对 a 进行判断,如果不为 nil 则解包,否则就返回 b 。使用的时候有以下两点要求:

  • a 必须是 optional 的
  • b 必须和 a 类型一致


当然如果你觉得这样可读性不高,我们可以自定义运算符来实现:

[Swift] 纯文本查看 复制代码
infix operator |->| {
}
func |->|<T> (left: T?, right: T) ->T {
  if let l = left {
    return l
  }
  return right
}
var a: String?
var b = "Hello world!"
var c = a |->| b



2.2.2、关于字符串

String传递的时候会进行拷贝,NSString通常为引用,使用countElements计算String数量与同样NSString.length计算的结果可能不同,因为 length 利用的是 UTF-16 类型的值,而不是 Unicode 字符。比如 emoji 表情加进去之后,UTF-16 算的结果是2,而 Unicode 的计算结果是1。


2.2.3、数组

除了普通的初始化方法,我们可以通过 init(count: Int, repeatedValue: T) 来初始化一个数组并填充上重复的值:

[Swift] 纯文本查看 复制代码
// [0.0, 0.0, 0.0]
var threeDoubles = [Double](repeating:0.0, count:3)


按某种规则获得新数组,使用map方法:

[Swift] 纯文本查看 复制代码
var oldArray = [10, 20, 30, 40]
var newArray = oldArray.map { (money: Int) in
  "¥\(money)"
}
print(newArray)


当然也可以简写:

[Swift] 纯文本查看 复制代码
var newArray = oldArray.map({ "¥\($0)" })


数组过滤:

[Swift] 纯文本查看 复制代码
var oldArray = [10, 20, 45, 32]
var filteredArray : Array<Int> = []
for money in oldArray {
  if (money > 30) {
    filteredArray += [money]
  }
}
print(filteredArray)


不过这个方法看起来很笨重,我们使用新方法:

[Swift] 纯文本查看 复制代码
var filtered = oldArray.filter {
  return $0 > 30
}


计算数组的和:

[Swift] 纯文本查看 复制代码
let array: Array<Int> = [10, 20, 30, 40]
var result = array.reduce(0, +)



2.2.4、解包技巧:

[Swift] 纯文本查看 复制代码
func add(_ num: Int?) ->Int? {
  if let n = num {
    return n + 1
  } else {
    return nil
  }
}
add(1)
add(nil)
// -> Map
func madd(_ num: Int?) ->Int? {
  return num.map {n in n + 1}
}
madd(1)
madd(nil)


字符串也可以进行容错

[Swift] 纯文本查看 复制代码
func madd(_ s: String?) ->String {
  return s.map{
    sss in "Hello \(sss)!"
  } ?? "default value"
}
madd("c")
madd(nil)



2.2.5、Switch中巧用where

[Swift] 纯文本查看 复制代码
let point = (1, -1)
switch point {
case let (x, y) where x == y:
  print("1")
  break
case let (x, y) where x == -y:
  print("2")
  break
default:
  break
}


fallthrough - 在 switch 中,将代码引至下一个 case 而不是默认的跳出 switch。不过要注意的是,fallthrough不能用在定义了变量的case内,只能用在直接的判断中。



2.2.6、控制流语法大全

[Swift] 纯文本查看 复制代码
// for loop (array)
let myArray = [1, 1, 2, 3, 5]
for value in myArray {
    if value == 1 {
        println("One!")
    } else {
        println("Not one!")
    }
}
// for loop (dictionary)
var dict = [
    "name": "Steve Jobs",
    "title": "CEO",
    "company": "Apple"
]
for (key, value) in dict {
    println("\(key): \(value)")
}
// for loop (range)
for i in -1...1 { // [-1, 0, 1]
    println(i)
}
// use .. to exclude the last number
// for loop (ignoring the current value of the range on each iteration of the loop)
for _ in 1...3 {
    // Do something three times.
}
// while loop
var i = 1
while i < 1000 {
    i *= 2
}
// do-while loop
do {
    println("hello")
} while 1 == 2
// Switch
let vegetable = "red pepper"
switch vegetable {
case "celery":
    let vegetableComment = "Add some raisins and make ants on a log."
case "cucumber", "watercress":
    let vegetableComment = "That would make a good tea sandwich."
case let x where x.hasSuffix("pepper"):
    let vegetableComment = "Is it a spicy \(x)?"
default: // required (in order to cover all possible input)
    let vegetableComment = "Everything tastes good in soup."
}
// Switch to validate plist content
let city:Dictionary<String, AnyObject> = [
    "name" : "Qingdao",
    "population" : 2_721_000,
    "abbr" : "QD"
]
switch (city["name"], city["population"], city["abbr"]) {
    case (.Some(let cityName as NSString),
        .Some(let pop as NSNumber),
        .Some(let abbr as NSString))
    where abbr.length == 2:
        println("City Name: \(cityName) | Abbr.:\(abbr) Population: \(pop)")
    default:
        println("Not a valid city")
}



2.2.7、函数

可选的参数请提供默认值:

[Swift] 纯文本查看 复制代码
func add(_ a: Int = 1, _ b: Int = 2) ->Int {
  return a + b
}
add(3) // 3 + 2


可变参数:

[Swift] 纯文本查看 复制代码
func add(nums: Int ...) ->Int {
  return nums.reduce(0, +)
}
add(nums: 1, 2, 3, 4, 5) // 15


如果不止一个参数,需要把可变参数放在最后,否则会报错。

通过函数修改原始值需要 inout :

[Swift] 纯文本查看 复制代码
func add( v:inout Int) {
  v = v + 1
}
var a = 1
add(v: &a)


泛型函数:

[Swift] 纯文本查看 复制代码
func value<T>( value1:inout T, oldValue: inout T) {
  let temp = value1
  value1 = oldValue
  oldValue = temp
}
var n1 = "mr r"
var n2 = "ms s"
value(value1: &n1, oldValue: &n2)
print(n1)
print(n2)
var i1 = 1
var i2 = 2
value(value1: &i1, oldValue: &i2)
print(i1)
print(i2)


变量:

[Swift] 纯文本查看 复制代码
func functionAdd(_ a: Int) ->Int {
  return a + 2
}
let funcalias = functionAdd
funcalias(2)


函数既然是类型的一种,那么显然也是可以作为参数传递的:

[Swift] 纯文本查看 复制代码
func addFunc(_ a: Int) ->Int {
  return a + 3
}
func withFunc(addingFunc: (Int) ->Int, a: Int) {
  print("R: \(addingFunc(a))")
}
withFunc(addingFunc: addFunc, a: 4) // 7


函数也是可以作为结果返回的。比如返回的值是一个参数为 Int 返回值为 Int 的函数,就是这样定义:func foo() -> (Int) -> Int

[Swift] 纯文本查看 复制代码
func add(a: Int) ->Int {
  return a + 1
}
func rev(a: Int) ->Int {
  return a - 1
}
func choose(a2r: Bool) -> (Int) ->Int {
  return a2r ? add: rev
}
let functionForUse = choose(a2r: false)
functionForUse(3) // rev   2


用多了会发现在一大串 ()-> 中又穿插各种 ()-> 是一个非常蛋疼的事情。我们可以用 typealias 定义函数别名,其功能和 OC 中的 typedef 以及 shell 中的 alias 的作用基本是一样的:

[Swift] 纯文本查看 复制代码
typealias handler = (Bool, String) ->String
func requetsWithHandler (_ completeHandler: handler) -> String {
  return completeHandler(false, "Hello")
}
requetsWithHandler { (a:Bool, s:String) -> String in
  if a {
    return s
  } else {
    return "World!"
  }
}


函数嵌套:

[Swift] 纯文本查看 复制代码
func choose(a2r: Bool) -> (Int) ->Int {
  
  func add(a: Int) ->Int {
    return a + 1
  }
  
  func rev(a: Int) ->Int {
    return a - 1
  }
  
  return a2r ? add: rev
}
let functionForUse = choose(a2r: true)
functionForUse(3)


柯里化(简单提及)

[Swift] 纯文本查看 复制代码
func add(b:Int)(a:Int) -> Int{
    return a + b ;
}
let addOne = add(1)
let addTwo = add(2)
var a = 0
a = addOne(a: a)    // 1
a = addTwo(a: a)    // 3



2.2.8、闭包

闭包的完整形态是这个样子的:

[Swift] 纯文本查看 复制代码
{(parameters) -> (returnType) in statements}


闭包有3种形态:

  • 全局的函数都是闭包,它们有自己的名字,但是没有捕获任何值。
  • 内嵌的函数都是闭包,它们有自己的名字,而且从包含他们的函数里捕获值。
  • 闭包表达式都是闭包,它们没有自己的名字,通过轻量级的语法定义并且可以从上下文中捕获值。


我们可以直接用 $0 $1 $2 这种来依次定义闭包的参数。比如 sorted 函数:

[Swift] 纯文本查看 复制代码
var reversed = sorted(["c","a","d","b"], { $0 > $1 })   // d c b a


如果闭包是函数的最后一个参数,Swift 提供了尾随闭包 (Trailing Closures) 解决这个审美问题:

[Swift] 纯文本查看 复制代码
// 以下是不使用尾随闭包进行函数调用
someFunc(0, {
    // 闭包主体部分
})
// 以下是使用尾随闭包进行函数调用
someFunc(0) {
  // 闭包主体部分
}


使用尾随,之前的函数改写为:

[Swift] 纯文本查看 复制代码
var reversed = sorted(["c","a","d","b"]) { $0 > $1 } // d c b a


如果除了闭包没有其他参数了,甚至可以把小括号也去掉。

[Swift] 纯文本查看 复制代码
var oldArray = [10, 20, 30, 40]
var filteredArray = oldArray.filter{
  return $0 > 30
}


closure 的变形大致有以下几种形态:

[Swift] 纯文本查看 复制代码
[1, 2, 3].map( { (i: Int) ->Int in return i * 2 } )
[1, 2, 3].map( { i in return i * 2 } )
[1, 2, 3].map( { i in i * 2 } )
[1, 2, 3].map( { $0 * 2 } )
[1, 2, 3].map() { $0 * 2 }
[1, 2, 3].map { $0 * 2 }


一个活生生的例子:

[Swift] 纯文本查看 复制代码
var canvas = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
canvas.backgroundColor = UIColor.red
UIView.animate(withDuration: 4, animations: {
  canvas.backgroundColor = UIColor.blue
}) {
  print("Result \($0)")
}
PlaygroundPage.current.liveView = canvas


常见形态:

[Swift] 纯文本查看 复制代码
// 作为变量
var closureName: (parameterTypes) -> (returnType)
// 作为可选类型的变量
var closureName: ((parameterTypes) -> (returnType))?
// 做为一个别名
typealias closureType = (parameterTypes) -> (returnType)
// 作为函数的参数
func({(parameterTypes) -> (returnType) in statements})
// 作为函数的参数
array.sort({ (item1: Int, item2: Int) -> Bool in return item1 < item2 })
// 作为函数的参数 - 隐含参数类型
array.sort({ (item1, item2) -> Bool in return item1 < item2 })
// 作为函数的参数 - 隐含返回类型
array.sort({ (item1, item2) in return item1 < item2 })
// 作为函数的参数 - 尾随闭包
array.sort { (item1, item2) in return item1 < item2 }
// 作为函数的参数 - 通过数字表示参数
array.sort { return $0 < $1 }
// 作为函数的参数 - 尾随闭包且隐含返回类型
array.sort { $0 < $1 }
// 作为函数的参数 - 引用已存在的函数
array.sort(<)



2.2.9、枚举

Swift 中的相关值有点像是 F# 中的 Discriminated Unions,它允许在枚举中存储额外的数据。

[Swift] 纯文本查看 复制代码
enum BarCode {
  case UPCA(Int, Int)
  case QRCode(String)
}
var productBar = BarCode.QRCode("abc")
productBar = .UPCA(1, 2)
switch productBar {
case .UPCA(let a, let b):
  print("\(a) \(b)")
  break
default:
  break
}





2.2.10、懒加载

[Swift] 纯文本查看 复制代码
class Person {
  var name: String
  lazy var greeting: String = {
    [unowned self] in
    return "Hello, \(self.name)!"
    }()
  
  init(name: String) {
    self.name = name
  }
}
var p = Person(name: "World!")
p.greeting


[Swift] 纯文本查看 复制代码
class ViewController: UIViewController {
  lazy var animator: UIDynamicAnimator = {
    return UIDynamicAnimator(referenceView: self.view)
  }()
  
  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
  }
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    animator.addBehavior(UIGravityBehavior())
  }
}



2.2.11、其他类型

  • Any 可以表示任何类型
  • AnyObject 可以代表任何class类型的实例



2.2.12、类型设计

[Swift] 纯文本查看 复制代码
import Foundation
enum Github {
   case Zen
   case UserProfile(String)
}
protocol Path {
   var path : String { get }
}
extension Github : Path {
   var path: String {
       switch self {
       case .Zen: return "/zen"
       case .UserProfile(let name): return "/users/\(name)"
       }
   }
}
let sample = Github.UserProfile("ashfurrow")
println(sample.path) // Prints "/users/ashfurrow"
// So far, so good
protocol Moya : Path {
   var baseURL: NSURL { get }
   var sampleData: String { get } // Probably JSON would be better than AnyObject
}
extension Github : Moya {
   var baseURL: NSURL { return NSURL(string: "https://api.github.com")! }
   var sampleData: String {
       switch self {
       case .Zen: return "Half measures are as bad as nothing at all."
       case .UserProfile(let name): return "{login: \"\(name)\", id: 100}"
       }
   }
}
func url(route: Moya) -> NSURL {
   return route.baseURL.URLByAppendingPathComponent(route.path)
}
println(url(sample)) // prints [url=https://api.github.com/users/ashfurrow]https://api.github.com/users/ashfurrow[/url]




大部分知识块儿内容来自这个粗心的开发者,不过我很喜欢。

熟悉并回顾了这些杂乱的东西之后,都跃跃欲试了吧?下篇文章开始正式的写代码。




相关内容

如何构造Swift框架(1)知识储备

如何构造Swift框架(2)功能编写

如何构造Swift框架(3)数据模块

如何构造Swift框架(4)推送服务

作者:dylan

都看到这里了,就把这篇资料推荐给您的好朋友吧,让他们也感受一下。

回帖是一种美德,也是对楼主发帖的尊重和支持。

*声明:敏捷大拇指是全球最大的Swift开发者社区、苹果粉丝家园、智能移动门户,所载内容仅限于传递更多最新信息,并不意味赞同其观点或证实其描述;内容仅供参考,并非绝对正确的建议。本站不对上述信息的真实性、合法性、完整性做出保证;转载请注明来源并加上本站链接,敏捷大拇指将保留所有法律权益。如有疑问或建议,邮件至marketing@swifthumb.com

*联系:微信公众平台:“swifthumb” / 腾讯微博:@swifthumb / 新浪微博:@swifthumb / 官方QQ一群:343549891(满) / 官方QQ二群:245285613 ,需要报上用户名才会被同意进群,请先注册敏捷大拇指

嗯,不错!期待更多好内容,支持一把:
支持敏捷大拇指,用支付宝支付10.24元 支持敏捷大拇指,用微信支付10.24元

评分

参与人数 2金钱 +10 贡献 +10 专家分 +11 收起 理由
Ding + 1 扎实
Anewczs + 10 + 10 + 10 32个赞!专家给力!

查看全部评分

Ding 发表于 2016-10-14 12:02:00 | 显示全部楼层
这几篇非常好。受益匪浅。备着经常看。
转行的 发表于 2016-10-14 17:24:46 | 显示全部楼层
Ding 发表于 2016-10-14 12:02
这几篇非常好。受益匪浅。备着经常看。

果断收藏!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

做任务,领红包。
我要发帖

分享扩散

都看到这里了,就把这资料推荐给您的好朋友吧,让他们也感受一下。
您的每一位朋友访问此永久链接后,您都将获得相应的金钱积分奖励
关闭

站长推荐 上一条 /3 下一条

热门推荐

合作伙伴

Swift小苹果

  • 北京治世天下科技有限公司
  • ©2014-2016 敏捷大拇指
  • 京ICP备14029482号
  • Powered by Discuz! X3.1 Licensed
  • swifthumb Wechat Code
  •   
快速回复 返回顶部 返回列表