[iOS] Swift學習文檔-框架版

一.前言

隨著Swift的逐漸完善, 越來越多的開發(fā)者轉型到Swift放棄OC, 其中一大部分是2017年后的培訓機構, 一部分是OC開發(fā)者迫于公司項目轉型的, 這樣一來國內iOS市場不僅被跨平臺蠶食又被Swift蠶食, 老的OC程序員找工作越來越困難, 作為一名 OC擁護者我想我也有寫一點東西的必要了, 本文主要講的就是OC轉型Swift需要注意的地方, 我希望把它作為長期項目寫下去, 需要用到的時候就直接查找, 不至于手忙腳亂, 下面就跟著我們的文章一起來看吧.

溫馨提示: 文章內容是根據自己的理解試出來的, 有些來自于百度, 請酌情閱讀, 如有錯誤請?zhí)嵝盐壹皶r修改.

二.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)
}

安全區(qū)域鋪滿

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

看到區(qū)別了么 導航欄上面的紅色不見了 這就是安全區(qū)域


Toast

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

基本使用

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

全局設置樣式
主要是用 ToastManager 的單例實現(xiàn)的

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

四.混編

在swift開發(fā)中難免會用到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)

自定義類

如果是自定義類 就需要創(chuàng)建橋接文件了

Swift中調用OC

1.在橋接文件中引入OC類

2.直接在swift中使用

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

OC中調用Swift

1.導入swift頭文件 規(guī)則是#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

但有時候會出現(xiàn)這個問題

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頭文件循環(huán)引用了, 解決方案是去掉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區(qū)別最大的東西, 下面就簡單來說一說

創(chuàng)建
let testBlock = {() -> Void in
    print("無參數無返回值")
}
testBlock()

// 或 下面這種是匿名立即執(zhí)行的簡便寫法

// 無參數無返回值
{() -> 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)
}

解決循環(huán)引用

先制造一個

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"
    }
}

點擊返回發(fā)現(xiàn)控制器不釋放了 stu和self循環(huán)引用了 怎么解決呢

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)

未完待續(xù), 持續(xù)更新中

finally enjoy it.

by objcat

2021.04.07

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容