1.前言
swift的基础用法和语法,可以参考这个教程,个人感觉讲的比较的完整,非常适合swift的语法入门学习,在基础语法的学习之后,咱们接下来学习swift语法中,让初学者们比较难以理解的一些知识内容,以及oc中用到的一些高级语法在swift中的实现方案。
废话不多说,进入主题。
2.闭包
这是一个swift初学者在接触swift语言后,最常用到的一个代码块,它的参数或者返回值,可以捕获上下文的任何常量和变量,有oc语法基础的同学们,肯定会有一种见到了block的小老弟的感觉。的确,在日常开发中我们需要用到闭包的地方,确实和block也是极其的相似,当然有其他语言基础的同学们,可能和对应语言的一些匿名函数和闭包函数都是有着异曲同工之处,接下来让我们来看一下,闭包的一些基础语法的使用示例。
1.下面是我项目中声明的一些常用的闭包
*/1-定义闭包格式: typealias 闭包名称 = (参数名称: 参数类型) -> 返回值类型public typealias voidClosure = () -> Void2- 声明闭包var removeItem: ((_ isLeft:Bool)->Void)!var editeClosure: ((_ isEdite:Bool)->Void)!
2.使用闭包
默认情况下,闭包会捕获附近作用域中的常量和变量,并使用强引用指向它们。你可以通过一个捕获列表来显式指定它的捕获行为。
捕获列表在参数列表之前,由中括号括起来,里面是由逗号分隔的一系列表达式。一旦使用了捕获列表,就必须使用 in 关键字,即使省略了参数名、参数类型和返回类型。
//值捕获
var a = 0
var b = 0
let closure = { [a] in
print(a, b)
}
a = 10
b = 10
closure() // 打印 “0 10”
//引用捕获
class SimpleClass {
var value: Int = 0
}
var x = SimpleClass()
var y = SimpleClass()
let closure = { [x] in
print(x.value, y.value)
}
x.value = 10
y.value = 10
closure() // 打印 “10 10”
3.特殊的闭包,逃逸闭包
@escaping用来标记逃逸闭包。
在一个函数式的方法中,有一个闭包式的参数,如果这个闭包式的参数在函数中被返回出去了就是逃逸闭包,没有被返回就是非逃逸闭包。
func showBronAlertVC(Title:String,
message:String,
sureBtnTitle:String,
cancelBtnTitle:String,
sureClosure:@escaping(voidClosure),
cancelClosure:@escaping(voidClosure)){
let alertVc = UIAlertController(title: Title, message: message, preferredStyle: UIAlertController.Style.alert)
let sureAlertAction = UIAlertAction(title: sureBtnTitle, style: UIAlertAction.Style.default) {(action) in
sureClosure()
}
let cacelAction = UIAlertAction(title: cancelBtnTitle, style: UIAlertAction.Style.default) {(action) in
cancelClosure()
}
alertVc.addAction(sureAlertAction)
alertVc.addAction(cacelAction)
kRootVC?.present(alertVc, animated: true, completion: nil)}
3.swift泛型
1.什么是泛型
-
泛型编码能让你写出符合需求、支持任意类型,灵活、可重用的函数。你能够编写避免重复和编程风格抽象、清晰、优雅的代码。
-
泛型是
Swift最强大的特性之一,许多Swift标准库是通过泛型代码构建的。例如,Swift的Array和Dictionary都是泛型集合。你可以创建一个Int数组,也可创建一个String数组,甚至可以是任意其他Swift类型的数组。同样的,你也可以创建存储任意指定类型的字典。
泛型就是具体类型的占位符,这个占位符只有在调用的时候才会确定类型,泛型代码让你能够根据自定义的需求,编写出适用于任意类型、灵活可重用的函数及类型。它能让你避免代码的重复,用一种清晰和抽象的方式来表达代码的意图。
所以,如果泛型使用得当,能编写出可复用,简洁,可维护的代码
2.泛型的一些使用
A. 泛型函数
泛型函数指的是:函数的参数或返回值类型使用泛型,而不是具体的类型
泛型函数的格式:
func 函数名<泛型1, 泛型2, …>(形参1, 形参2, ...) -> 返回类型 {
函数体
}
示例:
func myReduce<T, U>(arr: [T], initialValue: U, partialResult: (U, T) -> U) -> U {
var result = initialValue
arr.forEach {
result = partialResult(result, $0)
}
return result
}
myReduce这个函数有两个占位类型,T和U:
T作为形参arr的数组元素占位类型和形参partialResult闭包的第二个入参占位类型- U不仅作为了形参
initialValue和形参partialResult闭包的第一个参数占位类型,也作为闭包partialResult的返回类型和整个函数的返回类型
如何使用:
let array = ["1", "2", "3", "4", "5"]
let r1 = myReduce(arr: array, initialValue: 0) { $0 + (Int($1) ?? 0) }
// 打印结果:转为Int后的求和结果:15
print("转为Int后的求和结果:\(r1)")
let nums = [1, 2, 3, 4, 5]
let r2 = myReduce(arr: nums, initialValue: []) { $1 % 2 == 0 ? $0 + ["\($1)"] : $0 }
// 打印结果:偶数转为字符串数组:["2", "4"]
print("偶数转为字符串数组:\(r2)")
B. 泛型类型
除了泛型函数,Swift还允许定义泛型类型。这些自定义类、结构体和枚举可以适用于任何类型,类似于Array和Dictionary。
// Array 的定义
public struct Array<Element> {
// ...
}
// Dictionary 的定义
public struct Dictionary<Key, Value> where Key : Hashable {
// ...
}
Array后面尖括号中的Element就是Array的定义的泛型类型Dictionary尖括号中的Key、Value就是Dictionary定义的泛型类型
4.runtime在swift中的应用实例
通过之前封装的一个穿山甲信息流广告extention,我们来探寻一下swift中的runtime,seter方法和getter方法的,以及扩展的应用。
import Foundationimport BUAdSDKprivate
var KSubsriberKey:String = "KSubsriberKey"private
var KRemoveSubscriberKey:String = "KRemoveSubscriber"private
var KNativeAdManagerKey:String = "KNativeAdManagerKey"private
var KnativeInterstitialAdManagerKey:String = "KnativeInterstitialAdManagerKey"
extension UIView{
var nativeAdSubscriber:RACSubscriber?{
get{
return (objc_getAssociatedObject(self, &KSubsriberKey) as? RACSubscriber)
}
set{
objc_setAssociatedObject(self, &KSubsriberKey, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
var nativeAdRemoveSubscriber:RACSubscriber?{
get{ return (objc_getAssociatedObject(self, &KRemoveSubscriberKey) as? RACSubscriber)
}
set{
objc_setAssociatedObject(self, &KRemoveSubscriberKey, newValue, .OBJC_ASSOCIATION_RETAIN)
} }
var nativeExpressAdManager:BUNativeExpressAdManager?{
get{
return (objc_getAssociatedObject(self, &KNativeAdManagerKey) as? BUNativeExpressAdManager)
}
set{
objc_setAssociatedObject(self, &KNativeAdManagerKey, newValue, .OBJC_ASSOCIATION_RETAIN)
} }
var nativeInterstitialAdManager:BUNativeExpressInterstitialAd?{
get{
return (objc_getAssociatedObject(self, &KnativeInterstitialAdManagerKey) as? BUNativeExpressInterstitialAd) }
set{
objc_setAssociatedObject(self, &KnativeInterstitialAdManagerKey, newValue, .OBJC_ASSOCIATION_RETAIN)
} } //请求穿山甲信息流广告
func loadNativeAds(buSlot:BUAdSlot,adSize:CGSize,adNum:NSInteger)->RACSignal<AnyObject>{
return RACSignal.createSignal { [weak self](subsscriber) -> RACDisposable? in self?.nativeAdSubscriber = subsscriber self?.nativeExpressAdManager = BUNativeExpressAdManager(slot: buSlot, adSize: adSize) self?.nativeExpressAdManager?.delegate = self self?.nativeExpressAdManager?.loadAd(adNum) return nil } }
// 移除信息流广告
func removeAd()->RACSignal<AnyObject>{
return RACSignal.createSignal { (subsccriber) -> RACDisposable? in self.nativeAdRemoveSubscriber = subsccriber return nil } //插屏广告
func loadNativeInterstitialAdsSlot(buSlotId:String,adSize:CGSize)->RACSignal<AnyObject>{
return RACSignal.createSignal { [weak self](subsscriber) -> RACDisposable? in
self?.nativeAdSubscriber = subsscriber
self?.nativeInterstitialAdManager = BUNativeExpressInterstitialAd(slotID: buSlotId, adSize: adSize) self?.nativeInterstitialAdManager?.delegate = self self?.nativeInterstitialAdManager?.loadData() return nil } }}
extension UIView:BUNativeExpressAdViewDelegate{
public func nativeExpressAdSuccess(toLoad nativeExpressAd: BUNativeExpressAdManager, views: [BUNativeExpressAdView]) {
if views.count > 0 { self.addSubview(views[0]) views[0].rootViewController = kRootVC.views[0].render()
} } public func nativeExpressAdView(_ nativeExpressAdView: BUNativeExpressAdView, dislikeWithReason filterWords: [BUDislikeWords]) {
self.nativeAdRemoveSubscriber?.sendNext(nativeExpressAdView) }
public func nativeExpressAdViewRenderSuccess(_ nativeExpressAdView: BUNativeExpressAdView) {
self.nativeAdSubscriber?.sendNext(nativeExpressAdView) }}
extension UIView:BUNativeExpresInterstitialAdDelegate{
public func nativeExpresInterstitialAdRenderSuccess(_ interstitialAd: BUNativeExpressInterstitialAd) {
let curentVc = kRootVC?.children.first?.presentedViewController ?? kRootVC
self.nativeInterstitialAdManager?.show(fromRootViewController: curentVc ?? UIViewController()) }
public func nativeExpresInterstitialAd(_ interstitialAd: BUNativeExpressInterstitialAd, didFailWithError error: Error?) {
} public func nativeExpresInterstitialAdDidClose(_ interstitialAd: BUNativeExpressInterstitialAd) { }
}
Swift 扩展
扩展就是向一个已有的类、结构体或枚举类型添加新功能。
扩展可以对一个类型添加新的功能,但是不能重写已有的功能。
Swift 中的扩展可以:
- 添加计算型属性和计算型静态属性
- 定义实例方法和类型方法
- 提供新的构造器
- 定义下标
- 定义和使用新的嵌套类型
- 使一个已有类型符合某个协议
语法
扩展声明使用关键字 extension
extension SomeType {
// 加到SomeType的新功能写到这里
}
一个扩展可以扩展一个已有类型,使其能够适配一个或多个协议,语法格式如下:
extension SomeType: SomeProtocol, AnotherProctocol {
// 协议实现写到这里
}
getter和setter方法使用
swift实际开发中很少会使用到setter和getter方法,一般使用计算型属性,而且重写set方法,必须实现get方法,否则的换编译是不通过的,所以在swift中通常使用didSet来替代oc中的setter方法。
var cellData:[String:String] = [:] {
didSet{
guard let title = cellData["title"],
let imgIcon = cellData["imgIcon"],
let bgIcon = cellData["bgIcon"]
else { return }
self.bgIconImgView.image = UIImage(named: bgIcon)
self.listIconImgView.image = UIImage(named: imgIcon)
self.titleLabel.text = title
self.separateView.isHidden = bgIcon == "list_top" ? true : false
}
}
runtime在swift中的应用
oc是一门动态性语言,可以通过调用runtime API 调用和替换任意的方法,在swift中是否可以呢?我们来看一个示例
//MARK: - 纯SwiftClass
class BronClass {
@objc var bolValue: Bool = false
@objc var age: Int = 0
@objc var height: Float = 0
@objc var name: String?
@objc var exName: String?
@objc func testPureAction() {
print("BronClass.testPureAction") }
}
class OCClass:UIViewController {
@objc var bolValue: Bool = false
@objc var age: Int = 0
@objc var height: Float = 0
@objc var name: String?
@objc var exName: String?
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
@objc func createSubView(view : UIView) {
print("PemoleClass.createSubView") }
@objc func testVoidWithBool(boolValue : Bool , tempInt : Int , tempFloat : Float , str : String , obj : AnyObject) {
print("PemoleClass.testVoidWithBool") }
动态获取方法、属性的方法
func showClsRuntime (cls: AnyClass) {
print("----------方法列表开始-----------")
var methodNum: UInt32 = 0
let methodList: UnsafeMutablePointer<objc_property_t>! = class_copyMethodList(cls, &methodNum)
for index in 0..<numericCast(methodNum) {
let method: Method = methodList[index]
if let methodName: String = String(utf8String: property_getName(method)){
print(methodName)
}
}
print("----------方法列表结束-----------")
free(methodList)
print("----------属性列表开始-----------")
var propertyNum: UInt32 = 0
let propertyList: UnsafeMutablePointer<objc_property_t>! = class_copyPropertyList(cls, &propertyNum)
for index in 0..<numericCast(propertyNum) {
let property: objc_property_t = propertyList[index]
if let proName: String = String(utf8String: property_getName(property)){
print(proName)
}
}
free(propertyList)
print("----------属性列表开始-----------")
}
方法调用
showClsRuntime(cls: MuixSwiftClass.self)
showClsRuntime(cls: PureSwiftClass.self)
打印结果OCClass动态获取方法、属性
----------方法列表开始-----------
bolValue
setBolValue:
exName
setExName:
createSubViewWithView:
testVoidWithBoolWithBoolValue:tempInt:tempFloat:str:obj:
.cxx_destruct
name
setName:
initWithCoder:
initWithNibName:bundle:
viewDidLoad
viewDidAppear:
height
setHeight:
setAge:
age
----------方法列表结束-----------
----------属性列表开始-----------
bolValue
age
height
name
exName
----------属性列表结束-----------
BronClass动态获取方法、属性
----------方法列表开始-----------
bolValue
setBolValue:
exName
setExName:
testPureActionWithAId:
name
setName:
height
setHeight:
setAge:
age
----------方法列表结束-----------
----------属性列表开始-----------
bolValue
age
height
name
exName
----------属性列表结束-----------
结论
1.纯Swift类的函数调用已经不再是Objective-c的运行时发消息,而是类似C++的vtable,在编译时就确定了调用哪个函数,所以没法通过runtime获取方法、属性。
2.OCClass继承自UIViewController,基类NSObject,而Swift为了兼容Objective-C,凡是继承自NSObject的类都会保留其动态性,所以我们能通过runtime拿到他的方法。这里有一点说明:老版本的Swift(如2.2)是编译期隐式的自动帮你加上了@objc,而4.0以后版本的Swift编译期去掉了隐式特性,必须使用显式标示!
3.可以看到OCClass中有两个注释掉的方法。因为此两个方法在加上@objc后,Xcode直接报错提示此方法参数类型不能映射到OC类型,runtime也就无法获取。
4结合以上几点,不管是纯Swift类还是继承自NSObject的类只要在属性和方法前面添加@objc关键字就可以使用runtime。