swift基础语法篇二

320 阅读7分钟

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标准库是通过泛型代码构建的。例如,SwiftArrayDictionary都是泛型集合。你可以创建一个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还允许定义泛型类型。这些自定义类、结构体和枚举可以适用于任何类型,类似于ArrayDictionary

// Array 的定义
public struct Array<Element> {
    // ...
}
// Dictionary 的定义
public struct Dictionary<Key, Value> where Key : Hashable {
    // ...
}
  • Array后面尖括号中的Element就是Array的定义的泛型类型
  • Dictionary尖括号中的KeyValue就是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。