swift與OC

MARK、TODO吧彪、FIXME

// MARK: 類似于OC中的 #pragma mark

// MARK: - 類似于OC中的 #pragma mark

// TODO: 用于標記未完成的任務

// FIXME: 用于標記待修復的問題

條件編譯

// 操作系統(tǒng):macOS\iOS\tvOS\watchOS\Linux\Android\Windows\FreeBSD 
#if os(macOS) || os(iOS) 
// CPU架構:i386\x86_64\arm\arm64 
#elseif arch(x86_64) || arch(arm64) 
// swift版本 
#elseif swift(<5) && swift(>=3) 
// 模擬器 
#elseif targetEnvironment(simulator) 
// 可以導入某模塊 
#elseif canImport(Foundation) 
#else
#endif
swift條件編譯設置
// debug模式 
#if DEBUG 
// release模式 
#else 
#endif

#if TEST 
print("test") 
#endif

#if OTHER 
print("other") 
#endif

打印

func log<T>(_ msg: T, file: NSString = #file, line: Int = #line, fn: String = #function) { 
  #if DEBUG 
  let prefix = "\(file.lastPathComponent)_\(line)_\(fn):" 
  print(prefix, msg) 
  #endif
}

系統(tǒng)版本檢測

if #available(iOS 10, macOS 10.12, *) { 
  // 對于iOS平臺蹋肮,只在iOS10及以上版本執(zhí)行 
  // 對于macOS平臺出刷,只在macOS 10.12及以上版本執(zhí)行 
  // 最后的*表示在其他所有平臺都執(zhí)行 
}

API可用性說明

@available(iOS 10, macOS 10.15, *) 
class Person {}

struct Student {
  @available(*, unavailable, renamed: "study") 
  func study_() {} 
  func study() {}
  @available(iOS, deprecated: 11) 
  @available(macOS, deprecated: 10.12) 
  func run() {}
}

iOS程序的入口

在AppDelegate上面默認有個@UIApplicationMain標記,這表示 :編譯器自動生成入口代碼(main函數(shù)代碼)坯辩,自動設置AppDelegate為APP的代理

也可以刪掉@UIApplicationMain馁龟,自定義入口代碼:新建一個main.swift文件

main

Swift調(diào)用OC

新建1個橋接頭文件,文件名格式默認為:{targetName}-Bridging-Header.h

在 {targetName}-Bridging-Header.h 文件中 #import OC需要暴露給Swift的內(nèi)容

Swift調(diào)用OC – @_silgen_name

如果C語言暴露給Swift的函數(shù)名跟Swift中的其他函數(shù)名沖突了

可以在Swift中使用 @_silgen_name 修改C函數(shù)名

// C語言 int sum(int a, int b) { return a + b; }

// Swift 
@_silgen_name("sum") func swift_sum(_ v1: Int32, _ v2: Int32) -> Int32 
print(swift_sum(10, 20)) // 30 
print(sum(10, 20)) // 30

OC調(diào)用Swift

Xcode已經(jīng)默認生成一個用于OC調(diào)用Swift的頭文件漆魔,文件名格式是: {targetName}-Swift.h

Swift暴露給OC的類最終繼承自NSObject

使用@objc修飾需要暴露給OC的成員

使用@objcMembers修飾類 :1坷檩、代表默認所有成員都會暴露給OC(包括擴展中定義的成員) 2、最終是否成功暴露改抡,還需要考慮成員自身的訪問級別

Xcode會根據(jù)Swift代碼生成對應的OC聲明矢炼,寫入 {targetName}-Swift.h 文件

可以通過 @objc 重命名Swift暴露給OC的符號名(類名、屬性名阿纤、函數(shù)名等)

Swift

@objc(MYCar) 
@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(exec:v2:)
  func test() { 
    print(price, band, "test") 
  } 
}

OC

MYCar *c = [[MYCar alloc] initWithPrice:10.5 band:@"BMW"];
c.name = @"Bently";
c.price = 100.00;
[c drive]; // 100.00 Bently run
[c exec:10 v2:20]; // 100.00 Bently test 
[MYCar run]; // Car run
  

選擇器(Selector)

Swift中依然可以使用選擇器句灌,使用#selector(name)定義一個選擇器

必須是被@objcMembers或@objc修飾的方法才可以定義選擇器

@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(_:_:)") }
  func run() { 
    perform(#selector(test1)) 
    perform(#selector(test1(v1:))) 
    perform(#selector(test2(v1:v2:))) 
    perform(#selector(test2(_:_:)))
    perform(#selector(test2 as (Double, Double) -> Void))
  }
}

String

Swift的字符串類型String,跟OC的NSString欠拾,在API設計上還是有較大差異

// 空字符串 
var emptyStr1 = "" 
var emptyStr2 = String()

var str: String = "1" 
// 拼接胰锌,
jack_rose str.append("_2") 
// 重載運算符 + 
str = str + "_3" 
// 重載運算符 
+= str += "_4" 
// \()插值 
str = "\(str)_5" 
// 長度,9清蚀,1_2_3_4_5 
print(str.count)

var str = "123456" 
print(str.hasPrefix("123")) // true 
print(str.hasSuffix("456")) // true

String的插入和刪除

var str = "1_2" 
// 1_2_ 
str.insert("_", at: str.endIndex) 
// 1_2_3_4 
str.insert(contentsOf: "3_4", at: str.endIndex) 
// 1666_2_3_4 
str.insert(contentsOf: "666", at: str.index(after: str.startIndex)) 
// 1666_2_3_8884 
str.insert(contentsOf: "888", at: str.index(before: str.endIndex)) 
// 1666hello_2_3_8884 
str.insert(contentsOf: "hello", at: str.index(str.startIndex, offsetBy: 4))

// 666hello_2_3_8884 
str.remove(at: str.firstIndex(of: "1")!)

// hello_2_3_8884 
str.removeAll { $0 == "6" } 
var range = str.index(str.endIndex, offsetBy: -4)..<str.index(before: str.endIndex) 
// hello_2_3_4 
str.removeSubrange(range)

Substring

String可以通過下標匕荸、 prefix、 suffix等截取子串枷邪,子串類型不是String榛搔,而是Substring

var str = "1_2_3_4_5" 
// 1_2 
var substr1 = str.prefix(3) 
// 4_5 
var substr2 = str.suffix(3) 
// 1_2 
var range = str.startIndex..<str.index(str.startIndex, offsetBy: 3) 
var substr3 = str[range]

// 最初的String诺凡,1_2_3_4_5 
print(substr3.base)

// Substring -> String 
var str2 = String(substr3)

Substring和它的base,共享字符串數(shù)據(jù)

Substring發(fā)生修改 或者 轉為String時践惑,會分配新的內(nèi)存存儲字符串數(shù)據(jù)

String 與 Character

for c in "jack" { 
  // c是Character類型 
  print(c) 
}

var str = "jack" 
// c是Character類型 
var c = str[str.startIndex]

String相關的協(xié)議

BidirectionalCollection 協(xié)議包含的部分內(nèi)容 
startIndex 腹泌、 endIndex 屬性、index 方法 
String尔觉、Array 都遵守了這個協(xié)議

RangeReplaceableCollection 協(xié)議包含的部分內(nèi)容 
append凉袱、insert、remove 方法 
String侦铜、Array 都遵守了這個協(xié)議

Dictionary专甩、Set 也有實現(xiàn)上述協(xié)議中聲明的一些方法,只是并沒有遵守上述協(xié)議

多行String

"""三引號表示多行

如果要顯示3引號钉稍,至少轉義1個引號

縮進以結尾的3引號為對齊線

String 與 NSString

String 與 NSString 之間可以隨時隨地橋接轉換

如果你覺得String的API過于復雜難用涤躲,可以考慮將String轉為NSString

比較字符串內(nèi)容是否等價 : String使用 == 運算符 ; NSString使用isEqual方法,也可以使用 == 運算符(本質(zhì)還是調(diào)用了isEqual方法)贡未。

Swift种樱、OC橋接轉換表

String ? NSString
String NSMutableString
Array ? NSArray
Array NSMutableArray
Dictionary ? NSDictionary
Dictionary NSMutableDictionary
Set ? NSSet
Set NSMutableSet

只能被class繼承的協(xié)議

被 @objc 修飾的協(xié)議,還可以暴露給OC去遵守實現(xiàn)

protocol Runnable1: AnyObject {} 
protocol Runnable2: class {} 
@objc protocol Runnable3 {}

可選協(xié)議

可以通過 @objc 定義可選協(xié)議俊卤,這種協(xié)議只能被 class 遵守

@objc protocol Runnable { 
    func run1() 
  @objc optional func run2() 
  func run3() 
}

class Dog: Runnable { 
  func run3() { print("Dog run3") } 
  func run1() { print("Dog run1") } 
} 
var d = Dog() 
d.run1() // Dog run1 
d.run3() // Dog run3

dynamic

被 @objc dynamic 修飾的內(nèi)容會具有動態(tài)性嫩挤,比如調(diào)用方法會走runtime那一套流程

class Dog: NSObject { 
  @objc dynamic func test1() {} 
  func test2() {} 
} 
var d = Dog() 
d.test1() 
d.test2()

KVC\KVO

Swift 支持 KVC \ KVO 的條件

  • 屬性所在的類、監(jiān)聽器最終繼承自 NSObject
  • 用 @objc dynamic 修飾對應的屬性
class Observer: NSObject {
  override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 
    print("observeValue", change?[.newKey] as Any) 
  }
}

class Person: 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") 
  }
} 
var p = Person() 
// observeValue Optional(20) 
p.age = 20 
// observeValue Optional(25) 
p.setValue(25, forKey: "age")

block方式的KVO

class Person: NSObject {
  @objc dynamic var age: Int = 0 
  var observation: NSKeyValueObservation?
  override init() { 
    super.init() 
    observation = observe(\Person.age, options: .new) { 
      (person, change) in 
      print(change.newValue as Any) 
    }
  }
} 
var p = Person() // Optional(20) 
p.age = 20 // Optional(25) 
p.setValue(25, forKey: "age")

關聯(lián)對象(Associated Object)

在Swift中消恍,class依然可以使用關聯(lián)對象

默認情況岂昭,extension不可以增加存儲屬性

借助關聯(lián)對象,可以實現(xiàn)類似extension為class增加存儲屬性的效果

class Person {} extension Person {
  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)
    }
  }
}

var p = Person() 
print(p.age) // 0 
p.age = 10 
print(p.age) // 10

資源名管理

let img = UIImage(named: "logo")
let btn = UIButton(type: .custom) 
btn.setTitle("添加", for: .normal)
performSegue(withIdentifier: "login_main", sender: self)


let img = UIImage(R.image.logo)
let btn = UIButton(type: .custom) 
btn.setTitle(R.string.add, for: .normal)
performSegue(withIdentifier: R.segue.login_main, sender: self)


enum R {
  enum string: String { 
    case add = "添加" 
  } 
  enum image: String {
    case logo 
  } 
  enum segue: String {
    case login_main 
  }
}
extension UIImage { 
  convenience init?(_ name: R.image) { self.init(named: name.rawValue) } 
}

extension UIViewController { 
  func performSegue(withIdentifier identifier: R.segue, sender: Any?) { 
    performSegue(withIdentifier: identifier.rawValue, sender: sender) 
  } 
}

extension UIButton { 
  func setTitle(_ title: R.string, for state: UIControl.State) { 
    setTitle(title.rawValue, for: state) 
  } 
}

資源名管理的其他思路

let img = UIImage(named: "logo")
let font = UIFont(name: "Arial", size: 14)


let img = R.image.logo
let font = R.font.arial(14)


enum R {
  enum image { 
    static var logo = UIImage(named: "logo") 
  } 
  enum font {
    static func arial(_ size: CGFloat) -> UIFont? {
      UIFont(name: "Arial", size: size)
    } 
  }
}

多線程開發(fā) – 異步

public typealias Task = () -> Void

public static func async(_ task: @escaping Task) { _async(task) }

public static func async(_ task: @escaping Task, _ mainTask: @escaping Task) { _async(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)
  }
}

多線程開發(fā) – 延遲

@discardableResult 
public static func delay(_ seconds: Double, _ block: @escaping Task) -> DispatchWorkItem { 
  let item = DispatchWorkItem(block: block) 
  DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + seconds, execute: item) 
  return item
}

多線程開發(fā) – 異步延遲

@discardableResult 
public static func asyncDelay(_ seconds: Double, _ task: @escaping Task) -> DispatchWorkItem { 
  return _asyncDelay(seconds, task) 
}

@discardableResult 
public static func asyncDelay(_ seconds: Double, _ task: @escaping Task, _ mainTask: @escaping Task) -> DispatchWorkItem { 
  return _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
}

多線程開發(fā) – once

dispatch_once在Swift中已被廢棄哺哼,取而代之

可以用類型屬性或者全局變量\常量

默認自帶 lazy + dispatch_once 效果

fileprivate let initTask2: Void = { print("initTask2---------") }()

class ViewController: UIViewController { 
  static let initTask1: Void = { print("initTask1---------") }()
  override func viewDidLoad() { 
    super.viewDidLoad()
    let _ = Self.initTask1
    let _ = initTask2
  }
}

多線程開發(fā) – 加鎖

gcd信號量

class Cache {
  private static var data = [String: Any]() 
  private static var lock = DispatchSemaphore(value: 1) 
  static func set(_ key: String, _ value: Any) {
    lock.wait()
    defer { lock.signal() }
    data[key] = value
  }
}

Foundation

private static var lock = NSLock() 
static func set(_ key: String, _ value: Any) {
  lock.lock()
  defer { 
    lock.unlock() 
  } 
}

private static var lock = NSRecursiveLock() 
static func set(_ key: String, _ value: Any) {
    lock.lock()
    defer { lock.unlock() } 
}
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末佩抹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子取董,更是在濱河造成了極大的恐慌,老刑警劉巖无宿,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茵汰,死亡現(xiàn)場離奇詭異,居然都是意外死亡孽鸡,警方通過查閱死者的電腦和手機蹂午,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來彬碱,“玉大人豆胸,你說我怎么就攤上這事∠锾郏” “怎么了晚胡?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我估盘,道長瓷患,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任遣妥,我火速辦了婚禮擅编,結果婚禮上,老公的妹妹穿的比我還像新娘箫踩。我一直安慰自己爱态,他們只是感情好,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布境钟。 她就那樣靜靜地躺著肢藐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吱韭。 梳的紋絲不亂的頭發(fā)上吆豹,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機與錄音理盆,去河邊找鬼痘煤。 笑死,一個胖子當著我的面吹牛猿规,可吹牛的內(nèi)容都是我干的衷快。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼姨俩,長吁一口氣:“原來是場噩夢啊……” “哼蘸拔!你這毒婦竟也來了?” 一聲冷哼從身側響起环葵,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤调窍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后张遭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體邓萨,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年菊卷,在試婚紗的時候發(fā)現(xiàn)自己被綠了缔恳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡洁闰,死狀恐怖歉甚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情扑眉,我是刑警寧澤纸泄,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布赖钞,位于F島的核電站,受9級特大地震影響刃滓,放射性物質(zhì)發(fā)生泄漏仁烹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一咧虎、第九天 我趴在偏房一處隱蔽的房頂上張望卓缰。 院中可真熱鬧,春花似錦砰诵、人聲如沸征唬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽总寒。三九已至,卻和暖如春理肺,著一層夾襖步出監(jiān)牢的瞬間摄闸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工妹萨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留年枕,地道東北人。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓乎完,卻偏偏與公主長得像熏兄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子树姨,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容