1、MARK 左敌、TODO、FIXME
- MARK: 類似OC中的#pragma mark
- MARK: - 類似OC中的#pragma mark -
- TODO: 用于標(biāo)記未完成的任務(wù)
- FIXME: 用于標(biāo)記待修復(fù)的問(wèn)題
TODO: 俐镐、FIXME: 可配合#warning("")使用效果更好
func test(){
//TODO:未完成
#warning("TODO:未完成")
}
func test2(){
var age = 10
//FIXME:修復(fù)
#warning("FIXME:修復(fù)")
}
public class Person {
//MARK: - 屬性
var age = 0
var weight = 0
var height = 0
//MARK: - 私有方法
//MARK: 跑
private func run1(){
}
private func run2(){
}
//MARK: 吃
private func eat1(){
}
private func eat2(){
}
//MARK: - 公共方法
//MARK: 走
public func walk1(){
}
public func walk2(){
}
}
3矫限、條件編譯
//操作系統(tǒng):macOS、iOS佩抹、tvOS叼风、watchOS、Linux棍苹、Android无宿、Windows、FreeBSD
#if os(macOS) || os(iOS)
//CPU架構(gòu):i386\x86_64\arn\arm64
#elseif arch(x86_64) || arch(arm64)
//swifta版本
#elseif swift(<5) && swift(>=3)
//模擬器
#elseif targetEnvironment(simulator)
//可以導(dǎo)入某模塊
#elseif canImport(Foundation)
#else
#endif
#if DEBUG
//debug模式
#else
//release模式
#endif
#if TEST
//debug模式
#else
//release模式
#endif
#if OTHER
//debug模式
#else
//release模式
#endif
4枢里、打印
//打印文件路徑孽鸡,代碼行數(shù),打印信息
func ZQlog<T>(_ msg: T, file: NSString = #file, line: Int = #line, fn: String = #function) {
#if DEBUG
let prefix = "\(file.lastPathComponent)_\(line)_\(fn)"
print(prefix,msg)
#else
#endif
}
5栏豺、系統(tǒng)版本檢測(cè)
@available(iOS 10, macOS 10.15, *)
class Person { }//在低于某些版本即不可用
struct Student {
@available(*, unavailable, renamed: "study")
func study_() { }//方法已不可用彬碱,使用study方法
func study() { }
@available(iOS, deprecated: 11)
@available(macOS, deprecated: 10.12)
func run() { }//方法版本已棄用
}
var stu = Student()
stu.study()
stu.run()//警告:'run()' was deprecated in macOS 10.12
var person = Person()//報(bào)錯(cuò):'Person' is only available in macOS 10.15 or newer
更多用法參考:API可用性更多用法
6、iOS程序的入口
- 在Appdelegate上面默認(rèn)有一個(gè)@UIApplicationMain標(biāo)記奥洼,這表示:
- 編譯器自動(dòng)生成入口代碼(main函數(shù)代碼巷疼,自動(dòng)設(shè)置Appdelegate)為APP的代理
- 也可以刪掉@UIApplicationMain,自定義入口代碼:新建一個(gè)main.swift文件
//
// main.swift
// TestIOS
//
// Created by apple on 2020/2/16.
// Copyright ? 2020 apple. All rights reserved.
//
import UIKit
class ZQUIApplication: UIApplication {
}
UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, NSStringFromClass(ZQUIApplication.self), NSStringFromClass(AppDelegate.self))
7、Swift調(diào)用OC代碼
- 新建一個(gè)橋接頭文件灵奖,文件名格式默認(rèn)為:targetName-Bridging-Header
新建OC文件嚼沿,也會(huì)自動(dòng)生成橋接文件
- 在targetName-Bridging-Header文件中#import OC需要暴露給Swift的內(nèi)容
Person.h
//C語(yǔ)言
int sum(int a, int b);
//OC對(duì)象
@interface Person : NSObject
@property(nonatomic, copy)NSString *name;
@property(nonatomic, assign) NSInteger age;
- (instancetype)initWithAge:(NSInteger)age withName:(NSString *)name;
+ (instancetype)personWithAge:(NSInteger)age withName:(NSString *)name;
- (void)run;
+ (void)run;
- (void)eat:(NSString *)food other:(NSString *)other;
+ (void)eat:(NSString *)food other:(NSString *)other;
@end
Person.m
#import "Person.h"
@implementation Person
-(instancetype)initWithAge:(NSInteger)age withName:(NSString *)name{
if (self = [self init]) {
self.age = age;
self.name = name;
}
return self;
}
+(instancetype)personWithAge:(NSInteger)age withName:(NSString *)name{
return [[self alloc]initWithAge:age withName:name];
}
+(void)run{
NSLog(@"Person + run");
}
-(void)run{
NSLog(@"%zd %@ - run",_age,_name);
}
+(void)eat:(NSString *)food other:(NSString *)other{
NSLog(@"Person +eat %@ %@", food, other);
}
-(void)eat:(NSString *)food other:(NSString *)other{
NSLog(@"%zd %@ -eat %@ %@", _age, _name, food, other);
}
@end
int sum(int a, int b){
return a + b;
}
Swift調(diào)用OC對(duì)象
var person = Person(age: 10, withName: "Jack")
person.age = 18
person.name = "Rose"
person.run()//輸出:18 Rose - run
person.eat("Apple", other: "Android")//輸出:Rose -eat Apple Android
Person.run()//輸出:Person + run
Person.eat("Java", other: "C++")//輸出:Person +eat Java C++
print(sum(10, 20))//輸出:30
- 如果C語(yǔ)言暴露給Swift的函數(shù)名跟Swift中的其他函數(shù)名沖突了,可以再Swift中使用@_silgen_name修改C函數(shù)名
//調(diào)用C函數(shù)時(shí) 修改C函數(shù)名的同時(shí) 保證參數(shù)類型桑寨,返回值與原函數(shù)一致
@_silgen_name("sum") func swift_sum(_ v1: Int32, _ v2: Int32) -> Int32
print(swift_sum(10, 20))//輸出:30
8伏尼、OC調(diào)用Swift代碼
- Xcode已經(jīng)默認(rèn)生成一個(gè)用于OC調(diào)用Swift的頭文件,文件名格式是:targetName-Swift.h
- Swift會(huì)暴露給OC的類最終繼承自NSObject(只要繼承就會(huì)暴露,但如果不暴露其初始化器爆阶,則無(wú)法初始化)
- 使用@objc修飾需要暴露給OC的成員
- 使用@objcMembers修飾類
- 代表默認(rèn)所有成員都會(huì)暴露給OC(包括擴(kuò)展中定義的成員)
- 最終是否成功暴露,還需要考慮成員自身的訪問(wèn)級(jí)別
Swift —— Car聲明及擴(kuò)展
@objcMembers class Car: NSObject {//@objcMembers標(biāo)記 暴露所有成員(方法沙咏,屬性)
var price: Double
var band: String//@objc 標(biāo)記單個(gè)屬性、方法暴露給OC
init(price: Double, band: String) {
self.price = price
self.band = band
}
func run() {
print(price,band,"run")
}
static func run(){
print("Car,run")
}
}
extension Car{
func test() {
print(price,band,"test")
}
}
testSwift()
- Xcode會(huì)根據(jù)swift生成對(duì)應(yīng)的OC聲明肢藐,寫入taretName-Swift.h 文件
.h
void testSwift();
.m
#import "example-Swift.h"
void testSwift(){
NSLog(@"testSwift");
Car *car = [[Car alloc] initWithPrice:110000 band:@"benz"];
car.price = 120000;
car.band = @"audi";
[car run];
[car test];
[Car run];
}
- 可以通過(guò)@objc重命名swift暴露給OC的符號(hào)名(類名、屬性名吆豹、函數(shù)名等) ——相應(yīng)生成的taretName-Swift.h 文件也會(huì)改動(dòng)鱼的,調(diào)用方式也一并更改
@objc(ZQCar)
@objcMembers class Car: NSObject {
var price: Double
@objc(name)
var band: String
init(price: Double, band: String) {
self.price = price
self.band = band
}
@objc(drive)
func run() {
print(price,band,"run")
}
static func run(){
print("Car,run")
}
}
extension Car{
@objc(fix)
func test() {
print(price,band,"test")
}
}
選擇器
- Swift中依然可以使用選擇器理盆,使用#seletor(name)定義一個(gè)選擇器
- 前提是必須被@objcMambers或@objc修飾的方法才可以定義選擇器
@objc(Swift_Person)
@objcMembers class Person: NSObject{
func test1(v1: Int) {
print("test1")
}
func test2(v1: Int, v2: Int) {
print("test2(v1:v2:)")
}
func test2(_ v1: Double, _ v2: Double) {
print("test2(v1:v2:)")
}
func run() {
perform(#selector(test1))
perform(#selector(test1(v1:)))
perform(#selector(test2(_:_:)))
perform(#selector(test2(v1:v2:)))
perform(#selector(test2 as (Double, Double) -> Void))
}
}
String(具體用法見(jiàn)第三章 此處只講SubString及String)
Substring
- Swift的字符串類型String,與OC的NSString凑阶,在Api設(shè)計(jì)上還是有很大差異的
- String可以通過(guò)下標(biāo)猿规、prefix、subffix等截取字符串宙橱,子串類型不是String姨俩,而是Substring
- Substring與它的base String共享字符串?dāng)?shù)據(jù)
- Substring發(fā)生修改或轉(zhuǎn)為String時(shí),會(huì)分配新的內(nèi)存存儲(chǔ)字符串?dāng)?shù)據(jù)
var str = "1_2_3_4_5#6*7"
var substr1 = str.prefix(3)//String.SubSequence類型 Substring
var substr2 = str.suffix(3)
print(substr1,substr2)//輸出:1_2 6*7
var rang = str.startIndex ..< str.index(str.startIndex, offsetBy: 4)
var substr3 = str[rang]
//substring.base 原字符串
print(substr3,substr3.base)//1_2 6*7 1_2_ 1_2_3_4_5#6*7
//如果對(duì)substring進(jìn)行修改或轉(zhuǎn)為string時(shí)师郑,會(huì)重新分配內(nèi)存 也不會(huì)影響原字符串
substr3.append(contentsOf: "zzz")
print(substr3,substr3.base)////輸出:1_2_zzz 1_2_zzz
var str2 = String(substr3)//Substring轉(zhuǎn)String
String相關(guān)的協(xié)議
- BidirectionalCollection協(xié)議包含內(nèi)容
- startIndex环葵、endIndex屬性,Index方法
- String宝冕、Array都遵守了這個(gè)協(xié)議
- RangeReplaceableCollection協(xié)議包含的內(nèi)容
- replaceSubrange 张遭、append、insert猬仁、remove方法
- String帝璧、Array都遵守了這個(gè)協(xié)議
- Dictionary、Set也有實(shí)現(xiàn)上述協(xié)議中聲明的一些方法湿刽,只是并沒(méi)有遵守上述協(xié)議
String與NSString
- String與NSString之間可以隨時(shí)隨地的橋接轉(zhuǎn)換
var str1: String = "Jack"
var str2: NSString = "rose"
var str3: NSString = str1 as NSString//底層是調(diào)用了橋接函數(shù)的 且str1修改 str3不受影響
var str4: String = str2 as String
var str5 = str3.substring(with: NSRange(location: 0, length: 2))//String
- 比較字符串等價(jià)
- String使用==運(yùn)算符
- NSString使用isEqual方法的烁,也可以使用==運(yùn)算符(本質(zhì)還是調(diào)用了isEqual)
Swift、OC橋接轉(zhuǎn)換表
協(xié)議
只能被class繼承的協(xié)議
- 被@objc修飾的協(xié)議诈闺,還可以暴露給OC去遵守實(shí)現(xiàn)
protocol Procotol1: AnyObject {
}
protocol Procotol2: class {
}
@objc protocol Procotol3 {
}
可選協(xié)議
- 可以通過(guò)@objc定義可選協(xié)議渴庆,這種協(xié)議只能被class遵守
- 也可以使用擴(kuò)展實(shí)現(xiàn)協(xié)議,達(dá)到可選協(xié)議的效果
@objc protocol Runnable {
func run1()
@objc optional func run2()
func run3()
}
class Dog: Runnable {
//Runnable只能被類遵守 run2()可以不用實(shí)現(xiàn)
func run1(){
}
func run3() {
}
}
dynamic
- 被@objc dynamic 修飾的內(nèi)容會(huì)有動(dòng)態(tài)性雅镊,比如調(diào)用方法會(huì)走runtime那一套流程
class Dog: NSObject {
@objc dynamic func test1() {
}
func test2() {
}
}
var dog = Dog()
dog.test1()//OC runtime那一套 msgSend消息機(jī)制
dog.test2()//Swift 虛表
KVC/KVO
- Swift支持KVC \ KVO的條件
- 屬性所在的類襟雷、監(jiān)聽(tīng)器最終繼承自NSObject
- 用@ibjc dynamic修飾對(duì)應(yīng)的屬性
class Observer: NSObject {
override class func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("observeValue",change?[.newKey] as Any)
}
}
class Student: NSObject {
@objc dynamic var age :Int = 0
var observer:Observer = Observer()
override init() {
super.init()
self.addObserver(observer, forKeyPath: "age", options: .new, context: nil)
}
deinit {
self.removeObserver(observer, forKeyPath: "age", context: nil)
}
}
var person = Student()
person.age = 20
person.setValue(25, forKey: "age")
block方式的KVO
class Student: NSObject {
@objc dynamic var age:Int = 0
var observation:NSKeyValueObservation?
override init() {
super.init()
observation = observe(\Student.age, options: .new, changeHandler: { (person, change) in
print(change.newValue as Any)
})
}
}
var stu = Student()
stu.age = 20//輸出:Optional(20)
stu.setValue(25, forKey: "age")//輸出:Optional(25)
關(guān)聯(lián)對(duì)象(Associated Object)
- Swift對(duì)象中,class依然可以使用關(guān)聯(lián)對(duì)象
- 默認(rèn)情況下仁烹,extension不可以增加存儲(chǔ)屬性
- 借助關(guān)聯(lián)對(duì)象耸弄,可以實(shí)現(xiàn)extension為class增加存儲(chǔ)屬性的效果
class Student {
}
extension Student{
private static var AGE_KEY:Void?
var age: Int{
get{
((objc_getAssociatedObject(self, &Self.AGE_KEY)) as? Int) ?? 0
}
set{
(objc_setAssociatedObject(self, &Self.AGE_KEY, newValue, .OBJC_ASSOCIATION_ASSIGN))
}
}
}
資源名管理
-
這種做法實(shí)際參考了Andriod的資源名管理方式
優(yōu)秀的資源名管理方式參考1
優(yōu)秀的資源名管理方式參考2
多線程開(kāi)發(fā)
異步
DispatchQueue.global().async {
//異步操作
DispatchQueue.main.async {
//回到主線程
}
}
//使用DispatchWorkItem
let item = DispatchWorkItem {
print("先執(zhí)行")
}
DispatchQueue.global().async(execute: item)
item.notify(queue: DispatchQueue.main) {
print("后執(zhí)行")
}
延遲
//主線程延遲second
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3.0) {
print("333")
}
異步延遲
let item = DispatchWorkItem {
print("異步延遲執(zhí)行")
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3.0, execute: item)
item.notify(queue: DispatchQueue.main) {
print("回到主線程執(zhí)行")
}
//可以取消
item.cancel()
下面對(duì)上面的進(jìn)行簡(jiǎn)單的封裝:
//封裝.swift文件
public typealias Task = () -> Void
struct Asyncs {
//傳一個(gè)異步任務(wù)
public static func async(task: @escaping Task){
_async(task: task)
}
//傳一個(gè)異步任務(wù) 和 異步任務(wù)完成后回到主線程需要做的任務(wù)
public static func async(task: @escaping Task, _ mainTask: @escaping Task){
_async(task: task, mainTask)
}
private static func _async(task: @escaping Task, _ mainTask: Task? = nil){
let item = DispatchWorkItem(block: task)
DispatchQueue.global().async(execute: item)
if let main = mainTask {
item.notify(queue: DispatchQueue.main, execute: main)//
}
}
//延遲
@discardableResult
public static func delay(_ second:Double, _ block: @escaping Task) -> DispatchWorkItem{
let item = DispatchWorkItem(block: block)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + second, execute: item)
return item
}
//異步延遲
@discardableResult
public static func asyncDelay(_ seconds: Double, _ task:@escaping Task) -> DispatchWorkItem{
//TODO:
_asyncDelay(seconds, task)
}
//異步延遲
@discardableResult
public static func asyncDelay(_ seconds: Double, _ task:@escaping Task, _ maintask: @escaping Task) -> DispatchWorkItem{
//TODO:
_asyncDelay(seconds, task, maintask)
}
private static func _asyncDelay(_ seconds: Double, _ task:@escaping Task, _ maintask: Task? = nil) -> DispatchWorkItem{
let item = DispatchWorkItem(block: task)
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + seconds, execute: item)
if let main = maintask{
item.notify(queue: DispatchQueue.main, execute: main)
}
return item
}
}
//調(diào)用
//異步線程
Asyncs.async {
print("1",Thread.current)//1 異步線程
}
//異步線程 回到主線程執(zhí)行下一任務(wù)
Asyncs.async(task: {
print("2",Thread.current)//2 異步線程
}) {
print("3",Thread.current)//3 主線程
}
//主線程延遲
Asyncs.delay(3.0) {
print("4",Thread.current)
}
//異步延遲
Asyncs.asyncDelay(4.0) {
print("異步延遲",Thread.current)
}
Asyncs.asyncDelay(5.0, {
print("異步延遲",Thread.current)
}) {
print("異步延遲,回到主線程",Thread.current)
}
once
- swift中已經(jīng)廢棄了dispatch_once
- 可以用類型屬性或者全局變量\常量,因?yàn)槟J(rèn)自帶lazy + dispatch_once效果
此處可以查看屬性章節(jié)-屬性
加鎖
- gcd信號(hào)量(方法1)
- Foundation(方法2)
public struct Cache{
private static var data = [String: Any]()
// private static var lock = DispatchSemaphore(value: 1) 方法1:
// private static var lock = NSLock() 方法2:
// private static var lock = NSRecursiveLock() 遞歸鎖
public static func get(_ key: String) -> Any?{
data[key]
}
// public static func set(_ key: String, _ value: Any){ 方法1:
// lock.wait()
// defer {
// lock.signal()
// }
// data[key] = value
// }
// public static func set(_ key: String, _ value: Any){ 方法2:
// lock.lock()
// defer {
// lock.unlock()
// }
// data[key] = value
// }
}