美文网首页
写Swift代码的正确姿势

写Swift代码的正确姿势

作者: Oceanj | 来源:发表于2021-03-17 12:13 被阅读0次

1.多用协议,少用继承

继承只能单继承,这无法解决实现多继承的功能,当我们已经继承了某个类,而又想继承其他类,这就很无奈,而类型可以遵循多个协议,swift推荐使用面向协议编程。
比如想写一个BaseViewController抽象出公共的UIViewController的功能,这时候可以用协议,因为协议可以有默认实现,我们可以在默认实现里实现公共代码,如果想访问UIViewController的属性,可以对协议进行限定:只允许UIViewController的子类可以遵循该协议,代码如下:

public protocol ModalPresentable where Self: UIViewController { 
    // 设置一个遮罩
    var maskView: UIView { get }
}
private struct ModalPresentableKeys {
    public static var maskViewKey = "MaskViewKey"
}
var maskView: UIView {
        if let maskView = objc_getAssociatedObject(self, &ModalPresentableKeys.maskViewKey) as? UIView {
            return maskView
        }
        let mask = UIView(frame: view.bounds)
        mask.backgroundColor = UIColor(white: 0, alpha: 0.1)
        mask.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        // 可以访问self.view
        self.view.addSubview(mask)
        // 添加点击事件
        mask.addTapGesture { [unowned self] _ in 
            self.view.endEditing(true)
            self.dismiss(animated: true, completion: nil)
        }
        objc_setAssociatedObject(self, &ModalPresentableKeys.maskViewKey, mask, .OBJC_ASSOCIATION_RETAIN)
        return mask
    }

// where Self: UIViewController 限定只有UIViewController类型才能遵循此协议,这同时让我们获得了访问UIViewController里的属性的能力。
协议的作用不仅限与此,面向对象编程需要编程前考虑各个模型之间的关系,比如某个功能是应该放在父类还是应该放在父类的父类,面向协议编程让我们更容易构架我们的功能模块,这是一种扁平化的设计方式,我们需要某个功能就把他设计成协议。

2. 少用强制解包

通过!可以把Optional类型的对象强制解包,如果为nil,则会崩溃;可以通过下面几种方式解包:

var a: Any? = 1
if let a = a {
    print(a)
}
if let a = a as? Int {
    print(a)
}
switch a {
    case let x as Int:
        print(x)
    default:
        break
}
let b = a ?? 0

3. 条件保护使用guard,不用if

func test(_ num1: Int?, num2: Int?) {
    // 推荐
    guard let num1 = num1, let num2 = num2 else {
        print("null")
        return
    }
    print(num1, num2)
}

test(1, num2: 2)
test(nil, num2: 2)

4. 尽量使用函数式编程

比如想要实现一个功能,有一个学生对象数组,计算90分以上学生的分数总和:

struct Student {
    var name: String
    var uid: Int
    var score: Double
}
let students = [
    Student(name: "zhangsan", uid: 1, score: 80),
    Student(name: "zhangsan", uid: 1, score: 90),
    Student(name: "zhangsan", uid: 1, score: 94.5),
    Student(name: "zhangsan", uid: 1, score: 70),
    Student(name: "zhangsan", uid: 1, score: 100),
    Student(name: "zhangsan", uid: 1, score: 89)]
let sum = students
    .filter { $0.score >= 90 }
    .map { $0.score }
    .reduce(0, +)
print("90分以上学生分数总和:", sum)

使用filter过滤90分以上学生,map转换元素类型, reduce实现求和,代码简单方便。

5. 定义model时尽量用struct,少用class,除非需要继承。

就如上面的例子Student就是结构体,结构体是值类型更安全,class是引用类型。

6. 使用自定义初始化方法初始化类型

如果初始化类型时,同时需要向对象传递参数,则应该自定义初始化方法,传入参数。如下面代码初始化自定义ViewController

// 推荐方式
class RuleViewController: UIViewController {
    // 这里属性应该声明为private
    // 如果属性不需要修改,应该声明为let,
    private let content: String
    private let contentTitle: String
    /// 自定义初始化
    init(title: String, content: String) {
        self.contentTitle = title
        self.content = content
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
// 尽量用let修饰
let viewController = RuleViewController(title: "title", content: "content")

PlaygroundPage.current.liveView = viewController

// 下面是不推荐的方式
class RuleViewController1: UIViewController {
    private var content: String?
    private var contentTitle: String?
      
}
let viewController2 = RuleViewController1()
PlaygroundPage.current.liveView = viewController2

下面的方式需要声明属性为可选类型,使用时需要解包,这样使用很不方便而且不安全。

7. 不需要改变的变量用let

如上个例子中的属性声明private let content: String

8. switch or if else

swift的 switch功能强大,可以绑定变量,在case中加where子句进行条件匹配,等等。

9. enum枚举从此站起来啦

enum 可以关联变量,可以在enum中定义方法,这让enum有更多使用场景;比如在接口请求时,接口类型和请求参数可以封装到enum中:

enum UserAPI {
    case getUserInfo(uid: Int)
    case login(userName: String, password: String)
    
    /// 请求地址
    var urlPath: String {
        switch self {
            case .getUserInfo:
                return "/getuserInfo"
            case .login:
                return "/login"
            default:
                return ""
        }
    }
    /// 请求参数
    var paramter: [String: Any] {
        switch self {
            case .getUserInfo(uid: let uid):
                return ["uid": uid]
            case .login(userName: let un, password: let pwd):
                return ["userName": un, "password": pwd]
            default:
                return [:]
        }
    }
}

10. 不要用固定类型,用范型

如果封装的功能适用于多个类型 ,可以用范型,或者用协议+范型,这两个结合能碰撞多火花,如下面的代码。

// 定义一个协议
protocol Named {
    var name: String { get }
}

// Person 有名字
struct Person: Named {
    var name: String
}
// 限定T必须遵循Named
struct PrintName<T: Named> {
    let n: T
    init(n: T) {
        self.n = n
    }
    var printName: String {
        return n.name
    }
}

let s = Person(name: "zhangsan")
let p = PrintName(n: s)
print(p.printName)

11. 准确使用 open,public,internal,fileprivate,private

12. 有必要的时候可以将subscript 代替 func,使用起来真的很方便。

13. 便利初始化器

如果已有初始化器,又需要其他初始化方式,可以使用便利初始化器,便利初始化器不要最终调用唯一初始化器。
我们还以在extension中写一个便利初始化器,如下代码;定义了一个颜色便利初始化器,其中alpha有一个默认值1。

public extension UIColor {
    convenience init(hex: UInt, alpha: CGFloat = 1) {
        self.init(
            red: CGFloat((hex & 0xFF0000) >> 16) / 255.0,
            green: CGFloat((hex & 0x00FF00) >> 8) / 255.0,
            blue: CGFloat(hex & 0x0000FF) / 255.0,
            alpha: alpha
        )
    }
}
print(UIColor(hex: 0x888888))
print(UIColor(hex: 0x878787, alpha: 0.3))

13. 合理给实参和行参命名,行参给调用者看,实参给定义者在方法内部使用。同时可以给参数默认值,避免定义多个重载方法。

let intArry = [1,3,4,5,2]
func getData(by index: Int, start startIndex: Int = 0)-> Int{
    return intArry[index + startIndex]
}
print(getData(by: 1))
print(getData(by: 1, start: 1))

总结:切忌不要用OC的方式去写Swift,那样看起来是Swift实际上是OC,没有灵魂的Swift。

相关文章

  • 写Swift代码的正确姿势

    1.多用协议,少用继承 继承只能单继承,这无法解决实现多继承的功能,当我们已经继承了某个类,而又想继承其他类,这就...

  • 用 Swift 架构 iOS 应用的正确姿势

    用 Swift 架构 iOS 应用的正确姿势 用 Swift 架构 iOS 应用的正确姿势

  • 小白学Python必备求生姿势

    姿势提升 大牛说: 上手python的正确姿势是:立刻开始写代码!学习编程最大的错误是:没有立即开始写代码。 没错...

  • Swift单例的创建姿势

    Swift单例创建的正确姿势 [来源][http://my.oschina.net/jeans/blog/541750]

  • 收藏

    Swift4 JSON 解析 Swift里我用这个姿势写UserDefaults 用RxSwift仿写知乎日报 田...

  • swift正确的init姿势

    源于一个segmentFault提问,为什么在init方法里,self.xxx可以在super.init()之前,...

  • Swift:解包的正确姿势

    对于Swift学习而言,可选类型Optional是永远绕不过的坎,特别是从OC刚刚转Swift的时候,可能就会被代...

  • Swift小技巧

    Swift简洁,高效的小技巧 1.单例的正确姿势,相比OC,Swift的单例简洁到极致 2.defer语句会推迟包...

  • 写邮件的正确姿势

    零、阅读建议 第一部分 精读 2分钟 第二部分 通读 1分钟 一、你希望收到一封怎样的邮件,就写一封怎样的邮件...

  • 写PRD的正确姿势

    需求文档的主要作用是让团队对业务流程及功能规则达成共识,产品、设计、研发和运营人员都是需求文档的受众,为团队成员间...

网友评论

      本文标题:写Swift代码的正确姿势

      本文链接:https://www.haomeiwen.com/subject/siyscltx.html