美文网首页
[iOS] Swift学习文档-框架版

[iOS] Swift学习文档-框架版

作者: objcat | 来源:发表于2021-04-07 15:21 被阅读0次

    一.前言

    随着Swift的逐渐完善, 越来越多的开发者转型到Swift放弃OC, 其中一大部分是2017年后的培训机构, 一部分是OC开发者迫于公司项目转型的, 这样一来国内iOS市场不仅被跨平台蚕食又被Swift蚕食, 老的OC程序员找工作越来越困难, 作为一名 OC拥护者我想我也有写一点东西的必要了, 本文主要讲的就是OC转型Swift需要注意的地方, 我希望把它作为长期项目写下去, 需要用到的时候就直接查找, 不至于手忙脚乱, 下面就跟着我们的文章一起来看吧.

    温馨提示: 文章内容是根据自己的理解试出来的, 有些来自于百度, 请酌情阅读, 如有错误请提醒我及时修改.

    二.MVC

    (1) Model

    我们可以看到Swift中定义属性, 直接干掉了属性修饰符, 与之代替的是可选类型泛型

    OC

    @interface Student : NSObject
    @property (strong, nonatomic) NSString *name;
    @property (strong, nonatomic) NSArray *arr;
    @property (strong, nonatomic) NSDictionary *dic;
    @end
    

    温馨提示: 请不要跟我辩论字符串是否必须用copy谢谢

    Swift

    class Student {
        var name: String?
        var arr: Array<Any>?
        var dic: Dictionary<String, Any>?
    }
    

    getter / setter

    swift与oc不同的是 swift不会自动生成内部成员变量 如果想储存setter需要第二个变量 如果写set就必须写get 如果只写get那就是只读, 而oc只读使用readonly修饰

    class Cat {
        private var _name: String?
        var name: String? {
            set {
                _name = newValue;
            }
            get {
                return _name;
            }
        }
    }
    

    willSet / didSet

    swift为了赋值方便提供了只写set的方法, 它相当于一个钩子, 虽然不能改变set的值, 但是能在赋值前和赋值后去做一些事情

    class Cat {
        var name: String? {
            willSet {
                print("赋值前 " + (self.name ?? ""))
            }
            didSet {
                print("赋值后 " + (self.name ?? ""))
            }
        }
    }
    

    (2) View

    这个模块感觉没什么好说的 以后想起来再说吧 上面刚讲完willset和didset 下面就来简单的讲一下如何使用

    cell中赋值的方法

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath) as! CustomTableViewCell
            cell.model = self.arr?[indexPath.row] as? CustomModel;
            cell.selectionStyle = .none;
            return cell
    }
    
    class CustomTableViewCell: UITableViewCell {
        
        var model: CustomModel? {
            didSet {
                self.titleLabel.text = self.model?.name;
            }
        }
        
        @IBOutlet weak var titleLabel: UILabel!
        
        override func awakeFromNib() {
            super.awakeFromNib()
            // Initialization code
        }
    
        override func setSelected(_ selected: Bool, animated: Bool) {
            super.setSelected(selected, animated: animated)
            // Configure the view for the selected state
        }
    }
    

    (3) Controller

    感觉这个也没啥好说的, 说一下dealloc吧, 我觉得视图控制器最重要的就是释放, 当然你如果觉得不重要就当我没说 - -

    OC

    - (void)dealloc {
        NSLog(@"控制器释放---%@", NSStringFromClass([self class]));
    }
    

    Swift

    可以看到swift使用deinit取代了dealloc

    deinit {
        print("控制器释放---\(type(of: self))")
    }
    

    三.框架

    数据解析

    下面来介绍一下 json字符串转字典和字典转模型

    字符串转字典

    
    let jsonString = """
    {"name": "张三"}
    """
    
    do {
        let object = try JSONSerialization.jsonObject(with: jsonString.data(using: .utf8) ?? Data(), options: .allowFragments)
        print(object)
    } catch {
        print(error)
    }
    
    或
    
    // 处理可能没有
    let object = try? JSONSerialization.jsonObject(with: jsonString.data(using: .utf8) ?? Data(), options: .allowFragments)
    print(object ?? [])
    
    // 处理一定有
    let object = try! JSONSerialization.jsonObject(with: jsonString.data(using: .utf8) ?? Data(), options: .allowFragments)
    print(object)
    

    字典转model

    pod 'KakaJSON'
    https://github.com/kakaopensource/KakaJSON
    
    class Cat: Convertible {
        required init() {}
        var name: String?
    }
    
    let catJson: [String: Any] = [
        "name": "huahua"
    ]
    
    let cat = catJson.kj.model(Cat.self)
    
    

    数组转model数组

    let catJsonArray: [Any] = [
        ["name": "huahua"],
        ["name": "zhangsan"],
        ["name": "lisi"]
    ]
    
    let catArray = catJsonArray.kj.modelArray(Cat.self)
    

    model中包含其他对象

    class Person: Convertible {
        required init() {}
        var name: String?
        var cat: Cat?
    }
    
    let personJson: [String: Any] = [
        "name": "huahua",
        "cat": ["name": "huahua"]
    ]
    
    let person = personJson.kj.model(Person.self)
    

    model数组属性中包含其他对象

    class Person: Convertible {
        required init() {}
        var name: String?
        var cat: Cat?
        var cats: [Cat]?
    }
    
    let personJson: [String: Any] = [
        "name": "huahua",
        "cat": ["name": "huahua"],
        "cats": [
            ["name": "huahua"],
            ["name": "zhangsan"],
            ["name": "lisi"]
        ]
    ]
    
    let person = personJson.kj.model(Person.self)
    

    model转json

    let personDict = JSONObject(from: person)
    

    model属性替换

    有时候定义的属性不一定和json完全对上 为了能够进行解析 需要进行属性替换
    比如这里有cat 但是定义在model中的是cat2 需要把cat解析到cat2上
    这里有一个口诀(顺序)
    没有cat2就找cat

    
    let personJson: [String: Any] = [
        "name": "huahua",
        "cat": ["name": "huahua"],
        "cats": [
            ["name": "huahua"],
            ["name": "zhangsan"],
            ["name": "lisi"]
        ]
    ]
    
    class Person: Convertible {
        required init() {}
        var name: String?
        var cat2: Cat?
        var cats: [Cat]?
    
        func kj_modelKey(from property: Property) -> ModelPropertyKey {
            switch property.name {
            case "cat2": return "cat"
            default: return property.name
            }
        }
    }
    

    json属性替换

    有时候转化成json的时候需要替换某些key
    拿上面的举例子 转回json的时候应该是cat2 如果这时你想转回原json 也就是cat
    那么需要进行json属性替换

    func kj_JSONKey(from property: Property) -> JSONPropertyKey {
        switch property.name {
        case "cat2": return "cat"
        default: return property.name
        }
    }
    

    网络请求

    pod 'Alamofire'
    https://github.com/Alamofire/Alamofire
    
    import Alamofire
    
    // get 默认
    AF.request("http://localhost:8080/api/v1/hello").response { response in
        debugPrint(response)
    }
    
    // post
    AF.request("http://localhost:8080/api/v1/hello2", method: .post).response { response in
        debugPrint(response)
    }
    

    json解析成字典

    AF.request("http://localhost:8080/api/v1/hello").response { response in
        let json = try? JSONSerialization.jsonObject(with: response.data ?? Data(), options: .allowFragments) as? Dictionary<String, Any>
        print(json ?? Dictionary())
    }
    

    json解析成对象

    import Foundation
    import KakaJSON
    
    class DataModel: Convertible {
        required init() {}
        var success: Bool?
        var result: Dictionary<String, Any>?
        var code: Int?
    }
    
    AF.request("http://localhost:8080/api/v1/hello3").response { response in
        let model = response.data?.kj.model(DataModel.self)
        debugPrint(model?.result ?? Dictionary())
    }
    

    图片展示

    没啥好说的

    pod 'Kingfisher'
    https://github.com/onevcat/Kingfisher
    
    import Kingfisher
    self.coverImageView?.kf.setImage(with: URL(string: "https://img2.baidu.com/it/u=2037072674,1804134981&fm=26&fmt=auto&gp=0.jpg"))
    

    布局框架

    基本就跟Masonry一样

    pod 'SnapKit'
    https://github.com/SnapKit/SnapKit
    

    完全铺满

    import SnapKit
    
    let view = UIView()
    view.backgroundColor = .red
    self.view.addSubview(view)
    view.snp.makeConstraints { (make) in
        make.edges.equalTo(self.view)
    }
    

    安全区域铺满

    view.snp.makeConstraints { (make) in
        if #available(iOS 11, *) {
            make.edges.equalTo(self.view.safeAreaLayoutGuide)
        } else {
            make.top.equalTo(self.topLayoutGuide.snp.top)
            make.bottom.equalTo(self.bottomLayoutGuide.snp.bottom)
            make.left.equalTo(self.view)
            make.right.equalTo(self.view)
        }
    }
    
    // 其中make.edges.equalTo(self.view.safeAreaLayoutGuide)可以拆分成
    self.view.safeAreaLayoutGuide.snp.top
    self.view.safeAreaLayoutGuide.snp.bottom
    self.view.safeAreaLayoutGuide.snp.left
    self.view.safeAreaLayoutGuide.snp.right
    

    看到区别了么 导航栏上面的红色不见了 这就是安全区域


    Toast

    pod 'Toast-Swift'
    https://github.com/scalessec/Toast-Swift
    

    基本使用

    self.view.makeToast("123", duration: 3, position: .center)
    

    全局设置样式
    主要是用 ToastManager 的单例实现的

    var toastStyle = ToastStyle()
    toastStyle.messageColor = .blue
    ToastManager.shared.style = toastStyle
    ToastManager.shared.isTapToDismissEnabled = false
    ToastManager.shared.isTapToDismissEnabled = false
    

    四.混编

    在swift开发中难免会用到oc的库, 那么要怎么使用呢

    导入库

    如果是库直接就可以使用

    import SDWebImage
    self.coverImageView?.sd_setImage(with: URL(string: "https://img2.baidu.com/it/u=2037072674,1804134981&fm=26&fmt=auto&gp=0.jpg"), completed: nil)
    

    自定义类

    如果是自定义类 就需要创建桥接文件了

    Swift中调用OC

    1.在桥接文件中引入OC类

    2.直接在swift中使用

    let person = Person()
    person.name = "123"
    print(person)
    

    OC中调用Swift

    1.导入swift头文件 规则是#import "项目名-Swift.h", 我的项目叫SwiftProject

    #import <Foundation/Foundation.h>
    #import "SwiftProject-Swift.h"
    @interface Abc : NSObject
    @end
    

    2.使用

    虽然Student是Swift类, 但使用方法与OC无异, 唯一需要注意的是Swift类的成员变量OC不能直接使用, 需要@objc, 否则OC无法访问, 并且一定要继承一个基类, 否则OC无法识别

    class Student: NSObject {
        @objc var name: String?
    }
    
    @implementation Abc
    - (instancetype)init
    {
        self = [super init];
        if (self) {
            Student *stu = [[Student alloc] init];
            stu.name = @"123";
            NSLog(@"%@", stu.name);
        }
        return self;
    }
    @end
    

    但有时候会出现这个问题

    Cycle inside SwiftProject; building could produce unreliable results. This usually can be resolved by moving the target's Headers build phase before Compile Sources.
    Cycle details:
    → Target 'SwiftProject': CodeSign /Users/macmini/Library/Developer/Xcode/DerivedData/SwiftProject-djnxjwwjwnrhjqbulwazwcvkbjnh/Build/Products/Debug-iphonesimulator/SwiftProject.app
     Target 'SwiftProject' has process command with output '/Users/macmini/Library/Developer/Xcode/DerivedData/SwiftProject-djnxjwwjwnrhjqbulwazwcvkbjnh/Build/Products/Debug-iphonesimulator/SwiftProject.app/Info.plist'
     Target 'SwiftProject' has compile command with input '/Users/macmini/demo/SwiftProject/SwiftProject/Assets.xcassets'
     Target 'SwiftProject' has compile command with input '/Users/macmini/demo/SwiftProject/SwiftProject/Abc.m'
     Target 'SwiftProject' has compile command for Swift source files
     Target 'SwiftProject': Ditto /Users/macmini/Library/Developer/Xcode/DerivedData/SwiftProject-djnxjwwjwnrhjqbulwazwcvkbjnh/Build/Intermediates.noindex/SwiftProject.build/Debug-iphonesimulator/SwiftProject.build/DerivedSources/SwiftProject-Swift.h /Users/macmini/Library/Developer/Xcode/DerivedData/SwiftProject-djnxjwwjwnrhjqbulwazwcvkbjnh/Build/Intermediates.noindex/SwiftProject.build/Debug-iphonesimulator/SwiftProject.build/Objects-normal/x86_64/SwiftProject-Swift.h
     Target 'SwiftProject' has compile command for Swift source files
    

    原因是可能是swift和oc头文件循环引用了, 解决方案是去掉bridge中的oc类 或者 去掉类中的swift头文件

    个人总结: 即一个开放给swift的OC类不能再调用swift的类

    五.文档

    数据类型

    OC

    typedef NS_ENUM(NSUInteger, StudentGender) {
        StudentGenderBoy,
        StudentGenderGirl
    };
    @interface Student : NSObject
    @property (strong, nonatomic) NSString *name;
    @property (strong, nonatomic) NSArray *arr;
    @property (strong, nonatomic) NSDictionary *dic;
    @property (strong, nonatomic) NSNumber *number;
    @property (assign, nonatomic) NSInteger integer;
    @property (assign, nonatomic) float testFloat;
    @property (assign, nonatomic) double testDouble;
    @property (assign, nonatomic) BOOL selected;
    @property (assign, nonatomic) StudentGender gender;
    @end
    

    Swift

    enum StudentGender {
        case StudentGenderBoy
        case StudentGenderGirl
    }
    
    class Student: NSObject {
        var name: String?
        var arr: Array<Any>?
        var dic: Dictionary<String, Any>?
        var number: Int?
        var testFloat: Float?
        var testDouble: Double?
        var selected: Bool?
        var gender: StudentGender?
    }
    

    Block

    之所以放在这里是因为Block是与OC区别最大的东西, 下面就简单来说一说

    创建
    let testBlock = {() -> Void in
        print("无参数无返回值")
    }
    testBlock()
    
    // 或 下面这种是匿名立即执行的简便写法
    
    // 无参数无返回值
    {() -> Void in
        print("无参数无返回值")
    }()
    
    // 有参数无返回值
    {(a: String) -> Void in
        print(a)
        print("有参数无返回值")
    }("123")
    
    // 有参数有返回值
    {(a: String) -> String in
        print("有参数有返回值")
        return a;
    }("123")
    
    

    回调

    block回调在oc中非常常见, 那么在swift中要怎么使用呢

    func say(callback: ()->Void) -> Void {
        callback()
    }
    
    func say(name: String, callback: ()->Void) -> Void {
        callback()
    }
    
    func say(name: String, callback: ()->Void, callback2: ()->Void) -> Void {
        callback()
        callback2()
    }
    
    func say(name: String, callback: (_ a: String)->Void) -> Void {
        callback(name)
    }
    

    使用也很简单 自己领悟吧

    let stu = Student()
    
    stu.say {
        
    }
    
    stu.say(name: "456") {
        
    }
    
    stu.say(name: "4564") {
        
    } callback2: {
        
    }
    
    stu.say(name: "789") { (a) in
        print(a)
    }
    

    解决循环引用

    先制造一个

    var callback: (() -> Void)?
    var stu: Student?
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        // Do any additional setup after loading the view.
        self.stu = Student()
        self.stu?.say {
            self.navigationController?.title = "123"
        }
    }
    

    点击返回发现控制器不释放了 stu和self循环引用了 怎么解决呢

    self.stu?.say { [weak self] in
        self?.navigationController?.title = "123"
    }
    

    可以看到 swift 是用了 [weak self]解决

    六.SwiftUI

    在老项目中引用

    let vc = UIHostingController(rootView: SwiftUIView())
    // push
    self.navigationController?.pushViewController(vc, animated: true)
    

    未完待续, 持续更新中

    finally enjoy it.

    by objcat

    2021.04.07

    相关文章

      网友评论

          本文标题:[iOS] Swift学习文档-框架版

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