• 的交互也变得更方便了, 同时Swift编写的应用适配到iOS 7, 所以, 我们可以相信: 在未来使用swift的情况会逐渐增加了, 同时会涉及到OC和Swift在项目中并存的情况, 这里我重新读了官方的'Using swift with Coco

    QQ截图20160620151152.png

    投稿文章,作者:ZeroJ(Gitbub

    前言:

    Swift3.0出来后, 可以看到改变很大, 和cocoa, Foundation...的交互也变得更方便了, 同时Swift编写的应用适配到iOS 7, 所以, 我们可以相信: 在未来使用swift的情况会逐渐增加了, 同时会涉及到OC和Swift在项目中并存的情况, 这里我重新读了官方的'Using swift with Cocoa and Objective-C(swift3)'的文档, 记录了一些个人觉得比较常用的笔记, 请大家选择性阅读(里面的代码 均来自文档)

    • OC的初始化方法在Swift中被引为

    1
    2
    3
     init(...) --- 如果初始化不会失败
     init?(...) --- 如果初始化可能失败
     init!(...) --- 否则
    • oc中的property里的(getter==, setter==)将会被swift忽略

    • id对应Anyobject 但是所有的Anyobject在swift中是可选值, 如果之前的值为可选值, 在被设置为Anyobject后就是多重可选值了

    • oc中的属性被标记为

    1
    2
    3
    nullable -> 在swift中相当于 ?
    nonnull -> 在swift中相当于 非可选属性
    未标记 -> 在swift中相当于 !
    • oc中的轻量级泛型也是对应与swift中的泛型

    1
    2
    @property NSArray *dates
    对应于  var dates: [Date]
    • swift 中的闭包默认捕获变量的方式相当于 oc中block中捕获被标记为 __block的变量方式 -> 就是说 闭包捕获到的是变量的指针

    • swift中只要不是在多线程中, 建议使用[unowned self]来避免循环引用, 多线程中, 建议使用[weak self]

    • == 操作符相当于oc中的isEqual: --- 即比较内容是否相等;=== 相当于oc中的指针比较

    • 继承自NSObject的子类如果重写了isEquals:方法, 应当提供 hash 这个属性

    • 不能在oc中继承swift的class

    • 如果在swift中遇到oc中不支持的命名 可以利用 @objc(name)为他(属性 枚举, 方法名...)名个别名

    • @nonobjc 用来标记oc中不支持的

    • dynamic 将属性或者方法标记为dynamic就是告诉编译器把它当作oc里的属性或方法来使用(runtime),

    • 当需要使用 KVO 或者 runtime的时候需要这样处理

    • 当使用oc的 perform(selector, with:)方法的时候会返回一个可选值(指向AnyObject的指针);但是使用perform(:on:with:waitUntilDone:modes:) and perform(:with:afterDelay:)不会返回可选值

    • 使用 #keyPath() 可以转换为string, #keyPath(class.property) == "property"

    • 可用于KVC 例如person.value(forKey: #keyPath(Person.name)) = person.name

    • 但是测试了下不能修改swift中的只读属性 不知道有什么方便的用处

    • NSClassFromString("MyFramework.MyClass")

    • @IBDesignable 用在class(UIView的子类)声明的前面, 然后就可以在storyBoard中的inspector编辑它;@IBInspectable 用在(UIView的子类)的属性前面, 然后就可以在storyBoard中的inspector编辑它 ,就想系统提供的可以设置颜色,字体...

    • swift中的属性默认是strong类型, 只有可选类型才能被标记为weak

    • oc中的 copy属性 转换为swift中的@NSCopying 必须遵守NSCoding协议

    • 使用Core Data的时候所有的属性和方法需要标记为 @NSManaged

    • 文档中指出"The corresponding reference types can be accessed with their original NS class name prefix."但是beta版本中不能很好的使用NS开头的

    • 在oc和swift的桥接类型之间 直接使用 as 可以相互转换

    • 因为swift中的String和NSString使用的编码方式不一样,所以在swift中要对string使用索引的时候 不能直接使用 Int 或者NSRange

    • 需要使用String.Index and Range

    • swift会将Double, Int, Bool, Uint, Float和NSNumber桥接, 所以可以直接将

    • 这些类型的值使用 as NSNumber转换为NSNumber, 但是逆向进行是得到的可选值 as?

    • Foundation 和Core Foundation 之间的类型有toll-free bridge('免费桥')

    • Foundation中的常量, 在swift中被换为类嵌套的枚举:NSJSONReadingOptions ----- >> JSONSerialization.ReadingOption

    • swift中使用 Core Foundation

    • 如果使用swift处理过的函数不用我们手动管理内存分配;否则需要我们处理

    • 区分的方式: 当返回值是 Unmanaged的时候说明需要我们处理

    • 处理方法: 在使用返回的值之前调用他对应的takeUnretainedValue() 或takeRetainedValue()即可

    • 例如let memoryManagedResult = StringByAddingTwoStrings(str1, str2).takeUnretainedValue()

    • swift中Core Foundation里的类型 Ref后缀被去掉了 例如 CFTypeRef -> CFType

    • 在oc的方法 使用 NS_SWIFT_NOTHROW , 将不会使用swift的异常抛出机制

    • swift中直接使用 is 来实现oc中isKindOfClass: 的功能

    • swift中使用kvo的条件: 1.必须继承自NSObject 2. 被观察的属性 要被标记为 dynamic

    • swift 中的单例很简单:

    1
    2
    3
    class Singleton {
      static let sharedInstance = Singleton()
    }

    或者

    1
    2
    3
    4
    5
    6
    7
    class Singleton {
      static let sharedInstance: Singleton = {
          let instance = Singleton()
          // setup code
          return instance
      }()
    }
    • swift和C的交互: c的函数在swift中均为全局函数

    • 使用CF_SWIFT_NAME 这个宏可以将c中的属性或者函数转换为swift中

    eg:

    1
    Color ColorCreateWithCMYK(float c, float m, float y, float k) CF_SWIFT_NAME(Color.init(c:m:y:k:));

    对应为swift中

    1
    2
    3
    extension Color {
      init(c: Float, m: Float, y: Float, k: Float)
    }
    • c语言中的枚举 如果使用了NS_ENUM定义, 则在swift中被处理为对应的枚举

    • 如果没有使用NS_ENUM定义, 则被处理为结构体

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
     UITableViewCellStyleDefault,
     UITableViewCellStyleValue1,
     UITableViewCellStyleValue2,
     UITableViewCellStyleSubtitle
    };
    //对应与
    enum UITableViewCellStyle: Int {
      case `default`
      case value1
      case value2
      case subtitle
    }
    typedef enum {
     MessageDispositionUnread = 0,
     MessageDispositionRead = 1,
     MessageDispositionDeleted = -1,
    } MessageDisposition;

    对应与

    1
    2
    3
    4
    struct MessageDisposition: RawRepresentable, Equatable {}
    var MessageDispositionUnread: MessageDisposition { get }
    var MessageDispositionRead: MessageDisposition { get }
    var MessageDispositionDeleted: MessageDisposition { get }

    c中的被NS_OPTIONS修饰的枚举, 在swift中是OptionSet类型, -> 即可以使用数组方式选多个值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
          UIViewAutoresizingNone                 = 0,
          UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
          UIViewAutoresizingFlexibleWidth        = 1 << 1,
          UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
          UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
          UIViewAutoresizingFlexibleHeight       = 1 << 4,
          UIViewAutoresizingFlexibleBottomMargin = 1 << 5
    };

    对应与

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public struct UIViewAutoresizing : OptionSet {
      public init(rawValue: UInt)
      public static var flexibleLeftMargin: UIViewAutoresizing { get }
      public static var flexibleWidth: UIViewAutoresizing { get }
      public static var flexibleRightMargin: UIViewAutoresizing { get }
      public static var flexibleTopMargin: UIViewAutoresizing { get }
      public static var flexibleHeight: UIViewAutoresizing { get }
      public static var flexibleBottomMargin: UIViewAutoresizing { get }
    }
    • 在swift中直接使用  let resize = [. flexibleLeftMargin, . flexibleWidth...]

    • 在swift中全局变量和存储属性都被保证只初始化一次,所以用来当作OC里面的#define定义的常量。同时swift全局函数可以当作OC里#define定义的复杂宏(类似函数)

    • swift中的条件编译 自定义编译符的方法(例如: DEBUG_LOGGING)

    • 首先在project 的设置里面设置swift -D DEBUG_LOGGING to set the DEBUG_LOGGING

    • 然后使用 #if DEBUG_LOGGING // 操作 #endif

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #if arch(arm) || arch(arm64)
    #if swift(>=3.0)
    print("Using Swift 3 ARM code")
    #else
    print("Using Swift 2.2 ARM code")
    #endif
    #elseif arch(x86_64)
    print("Using 64-bit x86 code.)
    #else
    print("Using general code.")
    #endif
    • swift中使用指针的方式

    • 使用inout方式 &变量

    • 使用UnsafePointer或者UnsafeMutablePointer

    例如这个函数接受的参数可以传许多种

    1
    2
    3
    4
    5
    6
    let x: Float = 0
    func takesAPointer(_ p: UnsafePointer!) {
    // ...
    }
    takesAPointer(&x)
    takesAPointer([0.0,1.0])
    • 在swift中申明oc中的包含可选实现方法的协议时需要在协议和方法前都标记objc

    1
    2
    3
    4
    5
    @objc public protocol MySwiftProtocol {
    // 必须实现
      func requiredMethod()
      @objc optional func optionalMethod()
    }
    • 将oc的方法或者属性使用NS_SWIFT_NAME()可以为他们在swift中命一个别名

    • 将oc的方法或使用 NS_SWIFT_UNAVAILABLE()可以让他在swift中不可用








      原文地址:http://www.cocoachina.com/ios/20160620/16769.html

    展开全文
  • 初次邂逅swift

    2016-07-04 10:01:59
    swift OC的初始化方法在Swift中被引为init(…) — 如果初始化不会失败init?...id对应Anyobject 但是所有的Anyobject在swift中是可选值, 如果之前的值为可选值, 在被设置为Anyobject后就是多重可选值了 oc

    swift

    • OC的初始化方法在Swift中被引为

      init(…) — 如果初始化不会失败

      init?(…) — 如果初始化可能失败

      init!(…) — 否则

    • oc中的property里的(getter==, setter==)将会被swift忽略

    • id对应Anyobject 但是所有的Anyobject在swift中是可选值, 如果之前的值为可选值, 在被设置为Anyobject后就是多重可选值了

    • oc中的属性被标记为

    nullable -> 在swift中相当于 ?
    nonnull -> 在swift中相当于 非可选属性
    未标记 -> 在swift中相当于 !

    • oc中的轻量级泛型也是对应与swift中的泛型

    @property NSArray *dates
    对应于 var dates: Date]

    • swift 中的闭包默认捕获变量的方式相当于 oc中block中捕获被标记为 __block的变量方式 -> 就是说 闭包捕获到的是变量的指针

    • swift中只要不是在多线程中, 建议使用unowned self]来避免循环引用, 多线程中, 建议使用weak self]

    • == 操作符相当于oc中的isEqual: — 即比较内容是否相等;=== 相当于oc中的指针比较

    • 继承自NSObject的子类如果重写了isEquals:方法, 应当提供 hash 这个属性

    • 不能在oc中继承swift的class

    • 如果在swift中遇到oc中不支持的命名 可以利用 @objc(name)为他(属性 枚举, 方法名…)名个别名

    • @nonobjc 用来标记oc中不支持的

    • dynamic 将属性或者方法标记为dynamic就是告诉编译器把它当作oc里的属性或方法来使用(runtime),

    • 当需要使用 KVO 或者 runtime的时候需要这样处理

    • 当使用oc的 perform(selector, with:)方法的时候会返回一个可选值(指向AnyObject的指针);但是使用perform(:on:with:waitUntilDone:modes:) and perform(:with:afterDelay:)不会返回可选值

    • 使用 #keyPath() 可以转换为string, #keyPath(class.property) == “property”

    • 可用于KVC 例如person.value(forKey: #keyPath(Person.name)) = person.name

    • 但是测试了下不能修改swift中的只读属性 不知道有什么方便的用处

    • NSClassFromString(“MyFramework.MyClass”)

    • @IBDesignable 用在class(UIView的子类)声明的前面, 然后就可以在storyBoard中的inspector编辑它;@IBInspectable 用在(UIView的子类)的属性前面, 然后就可以在storyBoard中的inspector编辑它 ,就想系统提供的可以设置颜色,字体…

    • swift中的属性默认是strong类型, 只有可选类型才能被标记为weak

    • oc中的 copy属性 转换为swift中的@NSCopying 必须遵守NSCoding协议

    • 使用Core Data的时候所有的属性和方法需要标记为 @NSManaged

    • 文档中指出”The corresponding reference types can be accessed with their original NS class name prefix.”但是beta版本中不能很好的使用NS开头的

    • 在oc和swift的桥接类型之间 直接使用 as 可以相互转换

    • 因为swift中的String和NSString使用的编码方式不一样,所以在swift中要对string使用索引的时候 不能直接使用 Int 或者NSRange

    • 需要使用String.Index and Range

    • swift会将Double, Int, Bool, Uint, Float和NSNumber桥接, 所以可以直接将

    • 这些类型的值使用 as NSNumber转换为NSNumber, 但是逆向进行是得到的可选值 as?

    • Foundation 和Core Foundation 之间的类型有toll-free bridge(‘免费桥’)

    • Foundation中的常量, 在swift中被换为类嵌套的枚举:NSJSONReadingOptions —– >> JSONSerialization.ReadingOption

    • swift中使用 Core Foundation

      如果使用swift处理过的函数不用我们手动管理内存分配;否则需要我们处理

      区分的方式: 当返回值是 Unmanaged的时候说明需要我们处理

      处理方法: 在使用返回的值之前调用他对应的takeUnretainedValue() 或takeRetainedValue()即可

      例如let memoryManagedResult = StringByAddingTwoStrings(str1, str2).takeUnretainedValue()

    • swift中Core Foundation里的类型 Ref后缀被去掉了 例如 CFTypeRef -> CFType

    • 在oc的方法 使用 NS_SWIFT_NOTHROW , 将不会使用swift的异常抛出机制

    • swift中直接使用 is 来实现oc中isKindOfClass: 的功能

    • swift中使用kvo的条件: 1.必须继承自NSObject 2. 被观察的属性 要被标记为 dynamic

    • swift 中的单例很简单:

    class Singleton {
      static let sharedInstance = Singleton()
    }

    或者

    class Singleton {
      static let sharedInstance: Singleton = {
          let instance = Singleton()
          // setup code
          return instance
      }()
    }
    • swift和C的交互: c的函数在swift中均为全局函数

    • 使用CF_SWIFT_NAME 这个宏可以将c中的属性或者函数转换为swift中

    eg:


    Color ColorCreateWithCMYK(float c, float m, float y, float k) CF_SWIFT_NAME(Color.init(c:m:y:k:));

    对应为swift中

    extension Color {
      init(c: Float, m: Float, y: Float, k: Float)
    }
    • c语言中的枚举 如果使用了NS_ENUM定义, 则在swift中被处理为对应的枚举

    • 如果没有使用NS_ENUM定义, 则被处理为结构体

    typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
     UITableViewCellStyleDefault,
     UITableViewCellStyleValue1,
     UITableViewCellStyleValue2,
     UITableViewCellStyleSubtitle
    };

    //对应与

    enum UITableViewCellStyle: Int {
      case `default`
      case value1
      case value2
      case subtitle
    }
    typedef enum {
     MessageDispositionUnread = 0,
     MessageDispositionRead = 1,
     MessageDispositionDeleted = -1,
    } MessageDisposition;

    对应与

    struct MessageDisposition: RawRepresentable, Equatable {}
    var MessageDispositionUnread: MessageDisposition { get }
    var MessageDispositionRead: MessageDisposition { get }
    var MessageDispositionDeleted: MessageDisposition { get }

    c中的被NS_OPTIONS修饰的枚举, 在swift中是OptionSet类型, -> 即可以使用数组方式选多个值

    typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
          UIViewAutoresizingNone                 = 0,
          UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
          UIViewAutoresizingFlexibleWidth        = 1 << 1,
          UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
          UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
          UIViewAutoresizingFlexibleHeight       = 1 << 4,
          UIViewAutoresizingFlexibleBottomMargin = 1 << 5
    };

    对应与

    public struct UIViewAutoresizing : OptionSet {
      public init(rawValue: UInt)
      public static var flexibleLeftMargin: UIViewAutoresizing { get }
      public static var flexibleWidth: UIViewAutoresizing { get }
      public static var flexibleRightMargin: UIViewAutoresizing { get }
      public static var flexibleTopMargin: UIViewAutoresizing { get }
      public static var flexibleHeight: UIViewAutoresizing { get }
      public static var flexibleBottomMargin: UIViewAutoresizing { get }
    }
    • 在swift中直接使用 let resize = . flexibleLeftMargin, . flexibleWidth…]

    • 在swift中全局变量和存储属性都被保证只初始化一次,所以用来当作OC里面的#define定义的常量。同时swift全局函数可以当作OC里#define定义的复杂宏(类似函数)

    • swift中的条件编译 自定义编译符的方法(例如: DEBUG_LOGGING)

      首先在project 的设置里面设置swift -D DEBUG_LOGGING to set the DEBUG_LOGGING

      然后使用 #if DEBUG_LOGGING // 操作 #endif

    #if arch(arm) || arch(arm64)
    #if swift(>=3.0)
    print("Using Swift 3 ARM code")
    #else
    print("Using Swift 2.2 ARM code")
    #endif
    #elseif arch(x86_64)
    print("Using 64-bit x86 code.)
    #else
    print("Using general code.")
    #endif
    • swift中使用指针的方式

      使用inout方式 &变量

      使用UnsafePointer或者UnsafeMutablePointer

    例如这个函数接受的参数可以传许多种

    let x: Float = 0
    func takesAPointer(_ p: UnsafePointer!) {
    // ...
    }
    takesAPointer(&x)
    takesAPointer(0.0,1.0])
    • 在swift中申明oc中的包含可选实现方法的协议时需要在协议和方法前都标记objc
    @objc public protocol MySwiftProtocol {
    // 必须实现
      func requiredMethod()
      @objc optional func optionalMethod()
    }
    • 将oc的方法或者属性使用NS_SWIFT_NAME()可以为他们在swift中命一个别名

    • 将oc的方法或使用 NS_SWIFT_UNAVAILABLE()可以让他在swift中不可用

    • 类型后面加一个问号来标记这个变量的值是可选的 :如果?之前的值是 nil,?后面的东西都会被忽略,并且整个表达式返回 nil。否则,?之后的东西都会被运行。

    • switch 支持任意类型的数据以及各种比较操作

    • 注意:Swift 的 nil 和 Objective-C 中的 nil 并不一样。在 Objective-C 中,nil 是一个指向 不存在对象的指针。在 Swift 中,nil 不是指针——它是一个确定的值,用来表示值缺失。
      任何类型的可选都可以被设置为 nil,不只是对象类型。

    • 在 Swift 中,当匹配的 case 块中的代码执行完毕后,程序会终止 switch 语句,而不会继续执行下一个 case 块,不需要 在 case 块中显式地使用 break 语句

    • 一个 case 也可以包含多个模式,用逗号把它们分开(如果太长了也可以分行写)
      可以使用元组在同一个 switch 语句中测试多个值

    let somePoint = (1, 1) 
     switch somePoint {
          case (0, 0):
           println("(0, 0) is at the origin")
          case (_, 0):  //
           println("(\(somePoint.0), 0) is on the x-axis")
          case (-2...2, -2...2):
           println("(\(somePoint.0), \(somePoint.1)) is inside the box")
          default:
           println("(\(somePoint.0), \(somePoint.1)) is outside of the box")
     }
    • 值绑定:
      case 块的模式允许将匹配的值绑定到一个临时的常量或变量,这些常量或变量在该 case 块 里就可以被引用了——这种行为被称为值绑定
     case let (x, y) where x == y:  //
     case "1", "?", "一", "?":
    • 原始值和关联值:

      • 原始值是:定义枚举的时候被预先 填充的值
      • 关联值是:当你在创建一个基于枚举成员的新常量或变量时才会被设置
    • 结构体和类:

      • 结构体 拷贝; 当值类型的实例被声明为常量的时候,其属性变为常量,不可修改
      • 类 引用已存在的实例本身而非拷贝; 把一个引用类型的实例赋给一个常量后,仍然可以修
        改实例的变量属性。

      用等价于来检测两个类是否引用同一个实例

      “等价于”(用三个等号表示,===) 与“等于”(用两个等号表示,==)的不同:
      “等价于”表示两个类类型(class type)的常量或者变量引用同一个类实例。 “等于”表示两个实例的值“相等”或“相同”,

      • 结构体的主要目的是用来封装少量相关简单数据值。
      • 有理由预计一个结构体实例在赋值或传递时,封装的数据将会被拷贝而不是被引用。
      • 结构体不需要去继承另一个已存在类型的属性或者行为。
    • 变异方法:结构体和枚举是值类型。一般情况下,值类型的属性不能在它的实例方法中被修改,mutating可以从方法内部改变它的属性。要使用变异方法, 将关键字 mutating 放到方法的 func 关键字之前就可以了。不能在结构体类型常量上调用变异方法,因为常量的属性不能被mutating改变。

    • 构造器链:

      • 指定构造器必须调用其直接父类的的指定构造器。
      • 便利构造器必须调用同一类中定义的其它构造器。
      • 便利构造器必须最终以调用一个指定构造器结束。
      • 一个更方便记忆的方法是:
      • 指定构造器必须总是向上代理
      • 便利构造器必须总是横向代理

    这里写图片描述

    其他共同的性質

    • 都可實作 protocol
    • 都可使用 extension ,擴充 method
    • 都可以使用泛型 (Generic)

    范型

    在Swift中,泛型可作用於幾個地方:

    • 1.Function

    a. 泛型Function的語法一:

     func FName<Type>(a: Type, [Arguments...])->Void
    

    b.泛型Function語法二:

     func FName<Type>([Arguments...])->Type
    

    c.泛型Function語法三:

    func FName<Type>(a: Type, [Arguments...])->Type     
    
    • 2.Classess/Structs/Enums

    所定義之型態依據不同的需求產生不同的使用

    let person = Person<Int,String>(identifier:1231,name:"23")
    
    //上述语句可以写成下面这个样子
    typealias StringPerson = Person<Int,String>
    
    let newperson = StringPerson(identifier:1231,name:"23")
    • 3.Protocol
    • 4.Extension

    optional

    类型后面加一个问号来标记这个变量的值是可选的 :如果?之前的值是 nil,?后面的东西都会被忽略,并且整个表达式返回 nil。否则,?之后的东西都会被运行。

    Optionals say either “there is a value, and it equals x” or “there isn’t a value at all”.
    Optionals are “safer and more expressive than nil pointers in Objective-C”

    為了從根本上解決 nil 不明確的問題, Swift 導入了 Optional 的概念,由 enum 實作,並在 Compiler 中加強了對 Optional 的操作性

    let intValue:Int = 0 // 合法
    let intValue2:Int = nil //不合法
    let optionalIntValue:Int? = nil //合法,這是一個Int的Optional
    

    Optional 到底是什麼?

    public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
        case None
        case Some(Wrapped)
    }
    

    從 Optional 的宣告中,不難發現 Optional 使用的是就是在 enum 的 Associated Values 的用法,並搭配了泛型,並取名泛型型別為 Wrapped ,其實 Optional 被宣告了兩種可能的 case:
    None : 無值存在
    Some : 有值存在

    如果用 Swift 語法來說明就是以下這麼一回事了:

    //產生一個 iPad 2

    let 一台iPad2 = iPad(版本: "2",  使用者名字:"Grady Zhuo")
    let 禮物包裏:Optional<iPad> = Optional.Some(一台iPad)
    let 沒有東西的包裏:Optional<iPad> = Optional.None
    

    或是也可以用以下寫法

    let 禮物包裏2:Optional<iPad> = Optional(一台iPad2)
    let 沒有東西的包裏2:Optional<iPad> = Optional()
    

    使用 ? 來宣告 Optional 吧

    let 一台iPad2 = iPad(版本: "2",  使用者名字:"Grady Zhuo")
    let 禮物包裏 : iPad? = 一台iPad2
    let 沒有東西的包裏:iPad? = nil
    

    放入 Optional 的過程,術語又叫 Wrapped ,也就是被包起來的意思。

    inout

    函数内部实现改变外部参数

         func swapTwoInts( a:Int, b:Int) {   //交换-----加上inout不再报错,在函数内部实现改变外部参数
                let temporaryA = a
                a = b
                b = temporaryA       
            }

    上述程序会报错,因为一般参数仅仅是在函数内可以改变的。用inout修改:

     func swapTwoInts(inout a:Int,inout b:Int) {  
        let temporaryA = a
        a = b
        b = temporaryA
    }
    

    调用:

     swapTwoInts(&somtInt, b: &another)  //方法参数添加上inout,这里要使用&
    

    注意:

    • 避免在闭包中使用inout参数,因为inout参数在传入闭包的作用域外时会失效

    • 一个参数一旦被inout修饰,就不能再被var和let修饰了

    闭包

    闭包详解

    数组

    map方法

    闭包强引用环

    展开全文
  • 1、柯里化 把接受多个参数的方法进行一些变形,使其更加灵活的方法。函数式特点的重要表现。 举个例子,下面的函数简单地将输入的数字加 1: func addOne(num: Int) ->...这个函数所表达的内容非常有限,如果...

    1、柯里化

    把接受多个参数的方法进行一些变形,使其更加灵活的方法。函数式特点的重要表现。

    举个例子,下面的函数简单地将输入的数字加 1:
    
    func addOne(num: Int) -> Int {
        return num + 1
    }
    这个函数所表达的内容非常有限,如果我们之后还需要一个将输入数字加 2,或者加 3 的函数,可能不得不类似地去定义返回为 num + 2 或者 num + 3 的版本。有没有更通用的方法呢?我们其实可以定义一个通用的函数,它将接受需要与输入数字相加的数,并返回一个函数。返回的函数将接受输入数字本身,然后进行操作:
    
    func addTo(adder: Int) -> Int -> Int {
        return {
            num in
            return num + adder
        }
    }
    有了 addTo,我们现在就能轻易写出像是 addOne 或者 addTwo 这样的函数了:
    
    let addTwo = addTo(2)    // addTwo: Int -> Int
    let result = addTwo(6)   // result = 8
    

    柯里化是一种量产相似方法的好办法,可以通过柯里化一个方法模板来避免写出很多重复代码,也方便了今后维护。

    2、将 PROTOCOL 的方法声明为 MUTATING

    protocol Vehicle
    {
        var numberOfWheels: Int {get}
        var color: UIColor {get set}
    
        mutating func changeColor()
    }
    万一协议使用者需要在对应方法中修改属性
    

    3、sequence

    实现一个反向序列

    Swift 的 for...in 可以用在所有实现了 SequenceType 的类型上,而为了实现 SequenceType你首先需要实现一个 GeneratorType。比如一个实现了反向的 generator 和 sequence 可以这么写:

    Swift4.0里面 GeneratorType 改成 Iterator, SequenceType 改成 Sequence
    
    class ReverseGenerator<T>: IteratorProtocol {
        typealias Element = T
        var array: [Element]
        var currentIndex = 0
        
        init(array: [Element]) {
            self.array = array
            currentIndex = array.count - 1 //反向
        }
        
        func next() -> Element? {
            if currentIndex < 0 {
                return nil
            }
            else {
                let element = array[currentIndex]
                currentIndex -= 1
                return element
            }
        }
    }
    
    class ReverseSequence<T>: Sequence {
        typealias Element = T
        var array: [Element]
        init(array: [Element]) {
            self.array = array
        }
        typealias Iterator = ReverseGenerator<Element>
        
        func makeIterator() -> ReverseSequence<Element>.Iterator {
            return ReverseGenerator(array: array)
        }
    }
    
    let arr = [1,2,3,4,5,6]
    
    for item in ReverseSequence(array: arr) {
        print(item)
    }
    
    

    4、 ?? 的实现、&& 、||的实现

    /// ?? 的实现逻辑
    
    infix operator ???
    
    func ???<T>(optional: T?, defaultValue: @autoclosure () -> T) -> T {
        switch optional {
        case .some(let value):
            print(value)
            return value
        case .none:
            return defaultValue()
        }
    }
    var demo: Demo?
    
    let result = demo ??? Demo()
    
    /// || 和 && 的实现逻辑
    
    func &&&(leftValue: @autoclosure () -> Bool, rightValue: @autoclosure () -> Bool) -> Bool {
    //    print(leftValue)
        if !leftValue() {
            return false
        } else if rightValue() {
            return true
        }
        return false
    }
    var demo: Demo?
    let result = (demo == nil) &&& (3 > 2)
    

    5、Optional Chaining , 可选链

     ()? 就是 Void?
     
     如果遇到没有返回值的闭包如何判断执行成功与否呢
     
     let playClosure = {(child: Child) -> ()? in child.pet?.toy?.play()}
     
     if let result: () = playClosure(xiaoming) {
        print("好开心~")
    } else {
        print("没有玩具可以玩")
    }
    

    6、重载操作符、自定义操作符

    重载已有操作符 : + - 
    struct Vector2D {
        var x = 0.0
        var y = 2.0
    }
    func +(left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y + right.y)
    }
    
    let v1 = Vector2D(x: 2, y: 3)
    let v2 = Vector2D(x: 1, y: 3)
    
    let v3 = v1 + v2
    print(v3)
    
    直接重载已有操作符,不会出现编译错误,因为系统已经声明,但是自定义操作符需要声明否则编译错误(把上面的 + 换成 +* 自定义操作符就编译出错)
    
    声明定义 自定义操作符, 操作符设置在Swift3做了变更:常用于设置操作符计算优先级。
    
    https://github.com/apple/swift-evolution/blob/master/proposals/0077-operator-precedence.md
    
    precedencegroup MyPrecedence  {
        associativity: none
        higherThan: LogicalConjunctionPrecedence
    }
    infix operator +* : MyPrecedence
    
    
    运算的优先级,越高的话越优先进行运算。Swift 中乘法和除法的优先级是 150,加法和减法是 140,
    higerThan用于设置优先级高低,
    
    LogicalConjunctionPrecedence 常用逻辑运算优先级
    还有  lowerThan
    
    

    7、Swift REPL 交互式解释环境

    也就是说每输入一句语句就立即执行和输出。这在很多解释型的语言中是很常见的,非常适合用来对语言的特性进行学习。

    https://swifter.tips/swift-cli/

    8、方法中嵌套方法

    避免一些可能极少使用到的方法因为方法体太长而拆出去展开。

    10、swift单例用 let是最简便有效的

    11、Any、AnyObject

    一个针对对象 一个针对所有 包括函数

    12、随机数的生成

    arc4random_uniform

    创建一个 Range 的随机数的方法
    
    func randomInRange(range: Range<Int>) -> Int {
        let count = range.endIndex - range.startIndex
        return Int(arc4random_uniform(UInt32(count)) + UInt32(range.startIndex))
    }
    
    for i in 0...100 {
        print(randomInRange(range: 0..<6))
    }
    

    13、typealias 和泛型

    protocols do not allow generic parameters; use associated types instead
    
    协议中是不可以使用泛型的,但是我们还可以在协议中约定一个typealias 要求必须实现,这样也就可以在一定范围内,对协议进行约束
    
    protocol GeneratorType {
        associatedtype Generator
        func doSth(demo: String) -> Generator
    }
    
    class SomeBody: GeneratorType {
        typealias Generator = String
        func doSth(demo: String) -> String {
            print("do Sth")
            return "sth"
        }
    }
    

    14、条件编译

    - armv7|armv7s|arm64都是ARM处理器的指令集
    - i386|x86_64 是Mac处理器的指令集
    
    i386是针对intel通用微处理器32位处理器 (模拟器)
    x86_64是针对x86架构的64位处理器 (模拟器)
    
    模拟器32位处理器测试需要i386架构,
    模拟器64位处理器测试需要x86_64架构,
    真机32位处理器需要armv7,或者armv7s架构,
    真机64位处理器需要arm64架构。
    
    以下判断依然可以使用,根据不同环境做判断
    #if FREE_VERSION
    print("do Sth")
    #else
    print("do Sth2")
    #endif
    
    在这里我们用 FREE_VERSION 这个编译符号来代表免费版本。为了使之有效,我们需要在项目的编译选项中进行设置,在项目的 Build Settings 中,找到 Swift Compiler - Custom Flags,并在其中的 Other Swift Flags 加上 -D FREE_VERSION 就可以了。
    

    15、可变参数函数

    可变参数函数写NSString format初始化
    oc里面:
    NSString *string = [NSString stringWithFormat:
                    @"Hello %@. Date: %@", name, date];
                    
    swift:
    
    extension NSString {
        convenience init(format: NSString, _ args: CVarArg...) {
            self.init()
        }
    }
    
    NSString(format: "%@%@", "on", Demo())
    

    16、reduce

    reduce: 缩减操作,参数 一个变化的初始值 A、一个闭包函数,array中的对象每一个都会与变化的初始值 A一起执行一遍闭包方法,每次的闭包返回值作为变化后的 A ,进入下一个array对象的遍历,直到全部完成遍历
    
    let numbers = [1, 2, 3, 4]
    let numberSum = numbers.reduce(0, { x, y in
        x + y
    })
    // numberSum == 10
    
    
    func sum(num: Int...) -> Int {
        return num.reduce(2, {$0 * $1})
    }
    
    sum(num: 3,4) // 24
    
    
    func combinator(accumulator: Int, current: Int) -> Int {
       return accumulator + current
    }
    [1, 2, 3].reduce(0, combine: combinator)
    // 执行步骤如下
    combinator(0, 1) { return 0 + 1 } = 1
    combinator(1, 2) { return 1 + 2 } = 3
    combinator(3, 3) { return 3 + 3 } = 6
    = 6
    

    17、map

    map 方法接受一个闭包作为参数, 然后它会遍历整个 numbers 数组,并对数组中每一个元素执行闭包中定义的操作。 相当于对数组中的所有元素做了一个映射。
    
    不同类型都分别实现了对应的map方法,下面是Optional中的实现
    
    public func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U?
    
    
    let numbers = [1, 2, 3, 4]
    
    let number_2 = numbers.map { (num) -> Int in
        num * 2
    }
    
    
    
    

    flatMap

    
    相对于map,会自动过滤nil
    
    对于二维数组,flatMap 依然会遍历数组的元素,并对这些元素执行闭包中定义的操作。 但唯一不同的是,它对最终的结果进行了所谓的 “降维” 操作。 本来原始数组是一个二维的, 但经过 flatMap 之后,它变成一维的了。
    
    public func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U?
    public func flatMap(transform: (Self.Generator.Element) -> S) -> [S.Generator.Element]
    
    let numbersCompound = [[1,2,3],[4,5,6]];
    var res = numbersCompound.map { $0.map{ $0 + 2 } }
    // [[3, 4, 5], [6, 7, 8]]
    var flatRes = numbersCompound.flatMap { $0.map{ $0 + 2 } }
    // [3, 4, 5, 6, 7, 8]
    
    
    源码
    extension Sequence {
    //...
    public func flatMap(
    @noescape transform: (${GElement}) throws -> S
    ) rethrows -> [S.${GElement}] {
    var result: [S.${GElement}] = []
    for element in self {
    result.append(contentsOf: try transform(element))
    }
    return result
    }
    //...
    }
    flatMap 首先会遍历这个数组的两个元素 [1,2,3] 和 [4,5,6], 因为这两个元素依然是数组, 所以我们可以对他们再进行 map 操作: $0.map{ $0 + 2 }。
    
    这样, 内部的 $0.map{ $0 + 2 } 调用返回值类型还是数组, 它会返回 [3,4,5] 和 [6,7,8]。
    
    然后, flatMap 接收到内部闭包的这两个返回结果, 进而调用 result.append(contentsOf:) 将它们的数组中的内容添加到结果集中,而不是数组本身。
    
    那么我们最终的调用结果理所当然就应该是 [3, 4, 5, 6, 7, 8] 了。
    
    

    filter

    public func filter(_ isIncluded: (Substring.Element) throws -> Bool) rethrows -> String
    
    
    过滤
    
    

    18、找数组最大最小

    let array = [10,-22,753,55,137,-1,-279,1034,77]
    
    array.sorted().first
    array.sorted().last
    
    也可以用reduce计算
    
    array.reduce(Int.max,min)
    
    

    19、Swift中的app入口

    swift工程中并没有 main.m文件提供明显的入口,但是在Appdelegate 有  @UIApplicationMain
    
    在一般情况下,我们并不需要对这个标签做任何修改,但是当我们如果想要使用 UIApplication 的子类而不是它本身的话,我们就需要对这部分内容 “做点手脚” 了。
    
    刚才说到,其实 Swift 的 app 也是需要 main 函数的,只不过默认情况下是 @UIApplicationMain 帮助我们自动生成了而已。和 C 系语言的 main.c 或者 main.m 文件一样,Swift 项目也可以有一个名为 main.swift 特殊的文件。在这个文件中,我们不需要定义作用域,而可以直接书写代码。这个文件中的代码将作为 main 函数来执行。比如我们在删除 @UIApplicationMain 后,在项目中添加一个 main.swift 文件,然后加上这样的代码:
    
    UIApplicationMain(CommandLine.argc, UnsafeMutableRawPointer(CommandLine.unsafeArgv).bindMemory(to: UnsafeMutablePointer<Int8>.self, capacity: Int(CommandLine.argc)), NSStringFromClass(UIApplication.self), NSStringFromClass(AppDelegate.self))
    
    现在编译运行,就不会再出现错误了。当然,我们还可以通过将第三个参数替换成自己的 UIApplication 子类,这样我们就可以轻易地做一些控制整个应用行为的事情了。比如将 main.swift 的内容换成:
    
    import UIKit
    
    class MyApplication: UIApplication {
        override func sendEvent(event: UIEvent!) {
            super.sendEvent(event)
            print("Event sent: \(event)");
        }
    }
    //可以替换@UIApplicationMain
    UIApplicationMain(CommandLine.argc, UnsafeMutableRawPointer(CommandLine.unsafeArgv).bindMemory(to: UnsafeMutablePointer<Int8>.self, capacity: Int(CommandLine.argc)), NSStringFromClass(UIApplication.self), NSStringFromClass(AppDelegate.self))
    
    这样每次发送事件 (比如点击按钮) 时,我们都可以监听到这个事件了。
    

    21、DESIGNATED,CONVENIENCE 和 REQUIRED

    DESIGNATED designated 指定初始化器
    
    CONVENIENCE
    
    REQUIRED
    
    Swift 中不加修饰的 init 方法都需要在方法中保证所有非 Optional 的实例变量被赋值初始化
    
    而在子类中也强制 (显式或者隐式地) 调用 super 版本的 designated 初始化,所以无论如何走何种路径,被初始化的对象总是可以完成完整的初始化的。
    
    class ClassA {
        let numA: Int
        init(num: Int) {
            numA = num
        }
    }
    
    class ClassB: ClassA {
        let numB: Int
    
        override init(num: Int) {
            numB = num + 1
            super.init(num: num)
        }
    }
    在上面的示例代码中,注意在 init 里我们可以对 let 的实例常量进行赋值,这是初始化方法的重要特点。在 Swift 中 let 声明的值是常量,无法被写入赋值,这对于构建线程安全的 API 十分有用。而因为 Swift 的 init 只可能被调用一次,因此在 init 中我们可以为常量进行赋值,而不会引起任何线程安全的问题。
    
    与 designated 初始化方法对应的是在 init 前加上 convenience 关键字的初始化方法。这类方法是 Swift 初始化方法中的 “二等公民”,只作为补充和提供使用上的方便。所有的 convenience 初始化方法都必须调用同一个类中的 designated 初始化完成设置,另外 convenience 的初始化方法是不能被子类重写或者是从子类中以 super 的方式被调用的。
    
    
    
    1、初始化路径必须保证对象完全初始化,这可以通过调用本类型的 designated 初始化方法来得到保证;
    
    2、子类的 designated 初始化方法必须调用父类的 designated 方法,以保证父类也完成初始化。
    
    对于某些我们希望子类中一定实现的 designated 初始化方法,我们可以通过添加 required 关键字进行限制,强制子类对这个方法重写实现。这样做的最大的好处是可以保证依赖于某个 designated 初始化方法的 convenience 一直可以被使用。一个现成的例子就是上面的 init(bigNum: Bool):如果我们希望这个初始化方法对于子类一定可用,那么应当将 init(num: Int) 声明为必须,这样我们在子类中调用 init(bigNum: Bool) 时就始终能够找到一条完全初始化的路径了:
    
    class ClassA {
        let numA: Int
        required init(num: Int) {
            numA = num
        }
    
        convenience init(bigNum: Bool) {
            self.init(num: bigNum ? 10000 : 1)
        }
    }
    
    class ClassB: ClassA {
        let numB: Int
    
        required init(num: Int) {
            numB = num + 1
            super.init(num: num)
        }
    }
    另外需要说明的是,其实不仅仅是对 designated 初始化方法,对于 convenience 的初始化方法,我们也可以加上 required 以确保子类对其进行实现。这在要求子类不直接使用父类中的 convenience 初始化方法时会非常有帮助。
    

    22、Swift初始化返回nil

    swift默认初始化器是没有返回值的
    
    public init?(string: String)
    
    可以使用这种初始化器
    
    

    23、protocol组合

    Any 相当于 protocol<> 无类型的协议
    
    在swift 后续版本 如果我们需要定义一个协议仅仅用于整合多个其他协议
    
    protocol A { }
    
    protocol B { }
    
    protocol C: A, B {
        
    }
    
    可以直接用 typealias D = A & B
    
    

    25、对于一个项目来说,外界框架是由 Swift 写的还是 Objective-C 写的,两者并没有太大区别

    26、DYNAMIC

    swift中的动态分发

    27、protocol optional 可选接口

    @objc
    protocol C: A, B {
        @objc optional func dosth()
    }
    
    用 @objc 修饰的 protocol 就只能被 class 实现了,也就是说,对于 struct 和 enum 类型,我们是无法令它们所实现的接口中含有可选方法或者属性的。
    

    28、autoreleasepool 自动释放池

    这是一种必要的延迟释放的方式,因为我们有时候需要确保在方法内部初始化的生成的对象在被返回后别人还能使用,而不是立即被释放掉。
    
    什么情况下需要使用到:
    
    一般情况ARC管理内存不需要我们再进行内存管理操作,但是在个别特殊情况:
    
    func loadBigData() {
        if let path = NSBundle.mainBundle()
            .pathForResource("big", ofType: "jpg") {
    
            for i in 1...10000 {
                autoreleasepool {
                    let data = NSData.dataWithContentsOfFile(
                        path, options: nil, error: nil)
    
                    NSThread.sleepForTimeInterval(0.5)
                }
            }
        }
    }
    
    短时间内的内存暴增,但是又并没有离开方法,内存并未释放,容易导致内存不足而崩溃,这个时候上面添加的 autoreleasepool 的作用就体现出来了,每次循环后都会将需要释放的内容主动放入了自动释放池,但是也不需要太频繁,可以间隔10次之类的进行。
    

    29、正则表达式

    
    //MARK: - 正则匹配
    
    struct MyRegex {
    
        let regex: NSRegularExpression?
        init(_ pattern: String) {
            regex = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive)
        }
        
        func match(input: String) -> Bool {
            if let matches = regex?.matches(in: input, options: [], range: NSRange(location: 0, length: input.length)) {
                return matches.count > 0
            }
            
            return false
        }[]
    }
    
    infix operator =~
    
    func =~ (str: String, pattern: String) -> Bool {
    
        return MyRegex(pattern).match(input: str)
    
    }
    
    //
    
     string =~ "^[0-9]$"
     
     
     8个常用正则表达式
     
     https://code.tutsplus.com/tutorials/8-regular-expressions-you-should-know--net-6149
     
     /^[a-z0-9_-]{3,16}$/  用户名
     
     /^[a-z0-9_-]{6,18}$/ 密码
     
     /^#?([a-f0-9]{6}|[a-f0-9]{3})$/  十六进制 比如色号
     
     /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/  邮箱
     
     /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/ url
     
     
    

    30、Swift中的字典、数组变成值类型为什么不会带来更大的消耗

    当这些值类型属性内容不发生变更的时候,进行赋值、传递操作只会移动指针,并不会重新分配内存地址,没有堆内存的分配和释放的问题,这样的运行效率可以说极高。只有当值发生变更的时候才会重新分配地址。复制会将存储在其中的值类型一并进行复制,而对于其中的引用类型的话,则只复制一份引用。(比如struct中的类对象),struct复制的时候 内属性都发生地址复制,类对象这种引用类型则复制一份引用。
    
    但以上都是针对小数据量 变更发生较少的时候。
    
    在需要处理大量数据并且频繁操作 (增减) 其中元素时,选择 NSMutableArray 和 NSMutableDictionary 会更好
    

    31、AnyClass

    public typealias AnyClass = AnyObject.Type
    
    (NSClassFromString("demo2.ViewController") as! UIViewController.Type).init()
    
    

    32、Self 到底是什么

    我们在看一些接口的定义时,可能会注意到出现了首字母大写的 Self 出现在类型的位置上:
    
    protocol IntervalType {
        //...
    
        /// Return `rhs` clamped to `self`.  The bounds of the result, even
        /// if it is empty, are always within the bounds of `self`
        func clamp(intervalToClamp: Self) -> Self
    
        //...
    }
    比如上面这个 IntervalType 的接口定义了一个方法,接受实现该接口的自身的类型,并返回一个同样的类型。
    
    这么定义是因为接口其实本身是没有自己的上下文类型信息的,在声明接口的时候,我们并不知道最后究竟会是什么样的类型来实现这个接口,Swift 中也不能在接口中定义泛型进行限制。而在声明接口时,我们希望在接口中使用的类型就是实现这个接口本身的类型的话,就需要使用 Self 进行指代。
    
    import Foundation
    
    protocol Copyable {
        func mycopy() -> Self
    }
    
    class MyClass: Copyable {
        var num = 1
        
        func mycopy() -> Self {
            let result = type(of: self).init()
            (result as! MyClass).num = 3
            return result
        }
        //通过动态类型初始化对象必须有required init 方法
        required init() {  
        }
    }
    

    33、swift中的dynamicType 被废除 替代方案为type(of:)

    34、属性观察

    willSet 和 didSet。newValue oldValue
    

    35、Swift中的KVO

    Swift 中我们也是可以使用 KVO 的,但是仅限于在 NSObject 的子类中。这是可以理解的,因为 KVO 是基于 KVC (Key-Value Coding) 以及动态派发技术实现的,而这些东西都是 Objective-C 运行时的概念。另外由于 Swift 为了效率,默认禁用了动态派发,因此想用 Swift 来实现 KVO,我们还需要做额外的工作,那就是将想要观测的对象标记为 dynamic。
    
    如果遇到需要监听没有标记dynamic的属性时,只能开子类复写属性
    
    self.addObserver(self, forKeyPath: "str", options: .new, context: nil)
    
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
            print(keyPath)
        }
    

    36、局部 scope

    让代码更清晰,模块化
    
    do {
        //do sth
    }
    
    do {
        //do sth
    }
    

    37、判等

    判断两个对象是否相等,内容上的相等 而不是内存地址上的相等
    
    swift在内存地址上的相等用 ===
    
    swift中用的是 == ,通过重写 == ,基本的类型都实现了对应的 == 
    
    对象实现 Equatable 协议,同时在外部重写对应的 == 方法,因为我们需要在任意地方都可以使用对应的比较
    class TodoItem {
        let uuid: String
        var title: String
        
        init(uuid: String, title: String) {
            self.uuid = uuid
            self.title = title
        }
    }
    extension TodoItem: Equatable {
    }
    
    func ==(lhs: TodoItem, rhs: TodoItem) -> Bool {
        return lhs.uuid == rhs.uuid
    }
    
    
    
    oc中用的是isEqual,通过重写isEqual的方式来进行对象的比较
    
    
    

    38、判等还需要考虑的一点,hash值,比较两个对象是否内存地址也一样

    所以在重写哈希方法时候所采用的策略,与判等的时候是类似的:对于非 NSObject 的类,我们需要遵守 Hashable 并根据 == 操作符的内容给出哈希算法;而对于 NSObject 子类,需要根据是否需要在 Objective-C 中访问而选择合适的重写方式,去实现 Hashable 的 hashValue 或者直接重写 NSObject 的 -hash 方法。
    
    也就是说,在 Objective-C 中,对于 NSObject 的子类来说,其实 NSDictionary 的安全性是通过人为来保障的。对于那些重写了判等但是没有重写对应的哈希方法的子类,编译器并不能给出实质性的帮助。而在 Swift 中,如果你使用非 NSObject 的类型和原生的 Dictionary,并试图将这个类型作为字典的 key 的话,编译器将直接抛出错误。从这方面来说,如果我们尽量使用 Swift 的话,安全性将得到大大增加。
    

    39、避免多重Optional

    // Never do this!
    func methodThrowsWhenPassingNegative1(number: Int) throws -> Int? {
        if number < 0 {
            throw Error.Negative
        }
        if number == 0 {
            return nil
        }
        return number
    }
    
    if let num = try? methodThrowsWhenPassingNegative1(0) {
        print(type(of:num))
    } else {
        print("failed")
    }
    // 输出:
    // Optional<Int>
    // 其实里面包装的是一个 nil
    

    40、断言

    断言的另一个优点是它是一个开发时的特性,只有在 Debug 编译的时候有效,而在运行时是不被编译执行的,因此断言并不会消耗运行时的性能。这些特点使得断言成为面向程序员的在调试开发阶段非常合适的调试判断,而在代码发布的时候,我们也不需要刻意去将这些断言手动清理掉,非常方便。
    
    虽然默认情况下只在 Release 的情况下断言才会被禁用,但是有时候我们可能出于某些目的希望断言在调试开发时也暂时停止工作,或者是在发布版本中也继续有效。我们可以通过显式地添加编译标记达到这个目的。在对应 target 的 Build Settings 中,我们在 Swift Compiler - Custom Flags 中的 Other Swift Flags 中添加 -assert-config Debug 来强制启用断言,或者 -assert-config Release 来强制禁用断言。当然,除非有充足的理由,否则并不建议做这样的改动。如果我们需要在 Release 发布时在无法继续时将程序强行终止的话,应该选择使用 fatalError。
    

    41、playground 延时执行

    https://swifter.tips/playground-delay/
    

    42、swift中的 swizzle

    尽量还是少在swift中使用runtime
    
    initialize()
    load()
    这俩个方法都不可以在swift中使用了
    
    所以如果后续需要执行swizzle交换 需要把初始化方法在Appdelegate  didFinishLaunchingWithOptions 中手动调用
    
    
    最后 swizzle如下
    
    extension UIViewController {
        static func initializeMethod() {
            DispatchQueue.once(token: "initializeMethod") {
                let originalSEL = #selector(UIViewController.viewWillAppear(_:))
                let newSEL = #selector(UIViewController.MyViewWillAppear(_:))
                
                let originalMethod = class_getInstanceMethod(self, originalSEL)
                let newMethod = class_getInstanceMethod(self, newSEL)
                
                let addResult = class_addMethod(self, originalSEL, method_getImplementation(newMethod!), method_getTypeEncoding(newMethod!))
                if addResult {
                    class_replaceMethod(self, newSEL, method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!))
                } else {
                    method_exchangeImplementations(originalMethod!, newMethod!)
                    
                }
                
            }
        }
        
        @objc func MyViewWillAppear(_ animated: Bool) {
            print("测试swizzle")
            MyViewWillAppear(animated)
        }
    }
    

    43、Swift失去了 dispatch_once

    在swift中不再支持 dispatch_once
    给Dispatch 添加 once的扩展
    
    extension DispatchQueue {
        private static var _tokens = [String]()
        public class func once(token: String, completion: () -> ()) {
            objc_sync_enter(self)
            defer {
                objc_sync_exit(self)
            }
            if _tokens.contains(token) {
                return
            } else {
                _tokens.append(token)
                completion()
            }
            
        }
    }
    

    44、lazy 和map filtter等方法合用

    let data = 1...3
    lazy var result = data.lazy.map { $0 + 1 }
    

    45、swift中的math

    
    

    46、swift调用 C 动态库

    暂时只能通过OC来调用,bridge文件桥接

    47、Mirror swift中的反射

    尽量减少生产环境对Mirror的使用,毕竟本身他在Apple的实现里不是很丰富,也可能会被后期swift改版给改动
    

    48、输出格式化

    String(format: "%.2f", 1.2222)  输出:1.22
    

    49、swift中的options

    oc中的多个option直接用 | 符号链接:
    UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionAllowUserInteraction
    为空则 nil
    swift中options为数组
    [.CurveEaseIn, .AllowUserInteraction],为空的时候即 [] 空数组
    
    

    50、判断数据类型

    比如我们存储一些数据后希望可以精确还原对应的数据类型,可以通过下面方式获取数据类型
    
    print(String(cString: NSNumber(value: 1.22).objCType))
    
    存储时使用 objCType 获取类型,然后将数字本身和类型的字符串一起存储。在读取时就可以通过匹配类型字符串和类型的编码,确定数字本来所属的类型,从而直接得到像 Int 或者 Double 这样的类型明确的量
    
    
    OC中有@encode
    

    51、特殊编译符号

    在 Swift 中,编译器为我们准备了几个很有用的编译符号,用来处理类似这样的需求,它们分别是:
    
    符号  类型  描述
    #file   String  包含这个符号的文件的路径
    #line   Int 符号出现处的行号
    #column Int 符号出现处的列
    #function   String  包含这个符号的方法名字
    
    printLog(message: "测试")
            
    func printLog<T>(message: T, file: String = #file, method: String = #function, line: Int = #line) {
        print("\((file as NSString).lastPathComponent)[\(line)], \(method): \(message)")
    }
    

    52、将C函数映射为Swift函数

    @_silgen_name("test") func test_swift(a: Int32) {
        print("")
    }
     
    

    53、swift中的sizeOf

    print(MemoryLayout.size(ofValue: data))
    

    54、swift中的合理便捷安全的资源管理方式

    例如图片的管理,当图片变更或者发生图片名称替换的时候,全局去找对应的图片 很是恶心
    
    把图片资源的字符串名称通过 有rawvalue的enum管理起来  
    
    三方库 R.swift
    
    

    55、playground 如何与项目协作

    56、swift中的锁

    func myMethod(anObj: AnyObject!) {
        objc_sync_enter(anObj)
    
        // 在 enter 和 exit 之间 anObj 不会被其他线程改变
    
        objc_sync_exit(anObj)
    }
    
    

    57、Apple 为了 iOS 平台的安全性考虑,是不允许动态链接非系统的框架的。

    虽然和 Apple 的框架的后缀名一样是 .framework,使用方式也类似,但是这些第三方框架都是实实在在的静态库,每个 app 需要在编译的时候进行独立地链接。
    
    生成framework
    https://swifter.tips/code-framework/
    

    58、枚举写链表,嵌套枚举

     indirect enum LinkedList<Element: Comparable> {
            case Empty
            case Node(Element, LinkedList<Element>)
            
            func value() {
                switch self {
                case .Node(let element, let node):
                    print(element)
                    print("-")
                    print(node.value())
                case .Empty:
                    print("end")
    
                default:
                    print("-")
                }
            }
        }
            
    print(LinkedList.Node(1, LinkedList.Node(2, LinkedList.Node(3, LinkedList.Empty))).value())
    

    59、运行时关联属性

        var music: String? {
            get {
                return objc_getAssociatedObject(self, &key) as? String
            }
            set {
                objc_setAssociatedObject(self, &key, newValue, .OBJC_ASSOCIATION_RETAIN)
            }
        }
    

    60、尾递归 经过实验好像并没有用 依然栈溢出

    普通递归:
    
    func sum(n: UInt) -> UInt {
        if n == 0 {
            return 0
        }
        return n + sum(n: n - 1)
    }
    
    print(sum(n: 1000000))
    
    运行崩溃,原因是栈溢出
    
    这是因为每次对于 sum 的递归调用都需要在调用栈上保存当前状态,否则我们就无法计算最后的 n + sum(n - 1)。当 n 足够大,调用栈足够深的时候,栈空间将被耗尽而导致错误,也就是我们常说的栈溢出了。
    
    尾递归就是让函数里的最后一个动作是一个函数调用的形式,这个调用的返回值将直接被当前函数返回,从而避免在栈上保存状态。这样一来程序就可以更新最后的栈帧,而不是新建一个,来避免栈溢出的发生。在 Swift 2.0 中,编译器现在支持嵌套方法的递归调用了 (Swift 1.x 中如果你尝试递归调用一个嵌套函数的话会出现编译错误),因此 sum 函数的尾递归版本可以写为:
    
    func tailSum(n: UInt) -> UInt {
        func sumInternal(n: UInt, current: UInt) -> UInt {
            if n == 0 {
                return current
            } else {
                return sumInternal(n - 1, current: current + n)
            }
        }
    
        return sumInternal(n, current: 0)
    }
    
    tailSum(1000000)
    
    但是如果你在项目中直接尝试运行这段代码的话还是会报错,因为在 Debug 模式下 Swift 编译器并不会对尾递归进行优化。我们可以在 scheme 设置中将 Run 的配置从 Debug 改为 Release,这段代码就能正确运行了。
    

    61、枚举遍历

    枚举实现 CaseIterable  协议
    
    print(Test.allCases)
    

    62、Swift判断安全区域(用于适配所有的无Home键屏幕)

    static func safeAreaTop() -> CGFloat {
        if #available(iOS 11.0, *) {
            //iOS 12.0以后的非刘海手机top为 20.0
            if (UIApplication.shared.delegate as? AppDelegate)?.window?.safeAreaInsets.bottom == 0 {
                return 20.0
            }
            return (UIApplication.shared.delegate as? AppDelegate)?.window?.safeAreaInsets.top ?? 20.0
        }
        return 20.0
    }
    
    static func safeAreaBottom() -> CGFloat {
        if #available(iOS 11.0, *) {
            return (UIApplication.shared.delegate as? AppDelegate)?.window?.safeAreaInsets.bottom ?? 0
        }
        return 0
    }
    
    static func hasSafeArea() -> Bool {
        if #available(iOS 11.0, *) {
            return (UIApplication.shared.delegate as? AppDelegate)?.window?.safeAreaInsets.bottom ?? CGFloat(0) > CGFloat(0)
        } else { return false }
    }
    
    static func toolBarHeight() -> CGFloat {
        return 49 + safeAreaBottom()
    }
    
    static func navigationHeight() -> CGFloat {
        return 44 + safeAreaTop()
    }
    

    63、单元测试 target 的访问级别

    当你的应用程序包含单元测试 target 时,为了测试,测试模块需要访问应用程序模块中的代码。默认情况下只有 open 或 public 级别的实体才可以被其他模块访问。然而,如果在导入应用程序模块的语句前使用 @testable特性,然后在允许测试的编译设置(Build Options -> Enable Testability)下编译这个应用程序模块,单元测试目标就可以访问应用程序模块中所有内部级别的实体。
    

    参考

    Swift Tips

    Swift 博客

    Swift - evolution

    http://swift.gg/archives/

    展开全文
  • 的交互也变得更方便了, 同时Swift编写的应用适配到iOS 7, 所以, 我们可以相信: 在未来使用swift的情况会逐渐增加了, 同时会涉及到OC和Swift在项目中并存的情况, 这里我重新读了官方的'Using swift with Coco

    投稿文章,作者:ZeroJ(Gitbub

    前言:

    Swift3.0出来后, 可以看到改变很大, 和cocoa, Foundation...的交互也变得更方便了, 同时Swift编写的应用适配到iOS 7, 所以, 我们可以相信: 在未来使用swift的情况会逐渐增加了, 同时会涉及到OC和Swift在项目中并存的情况, 这里我重新读了官方的'Using swift with Cocoa and Objective-C(swift3)'的文档, 记录了一些个人觉得比较常用的笔记, 请大家选择性阅读(里面的代码 均来自文档)

    • OC的初始化方法在Swift中被引为

    1
    2
    3
     init(...) --- 如果初始化不会失败
     init?(...) --- 如果初始化可能失败
     init!(...) --- 否则
    • oc中的property里的(getter==, setter==)将会被swift忽略

    • id对应Anyobject 但是所有的Anyobject在swift中是可选值, 如果之前的值为可选值, 在被设置为Anyobject后就是多重可选值了

    • oc中的属性被标记为

    1
    2
    3
    nullable -> 在swift中相当于 ?
    nonnull -> 在swift中相当于 非可选属性
    未标记 -> 在swift中相当于 !
    • oc中的轻量级泛型也是对应与swift中的泛型

    1
    2
    @property NSArray *dates
    对应于  var dates: [Date]
    • swift 中的闭包默认捕获变量的方式相当于 oc中block中捕获被标记为 __block的变量方式 -> 就是说 闭包捕获到的是变量的指针

    • swift中只要不是在多线程中, 建议使用[unowned self]来避免循环引用, 多线程中, 建议使用[weak self]

    • == 操作符相当于oc中的isEqual: --- 即比较内容是否相等;=== 相当于oc中的指针比较

    • 继承自NSObject的子类如果重写了isEquals:方法, 应当提供 hash 这个属性

    • 不能在oc中继承swift的class

    • 如果在swift中遇到oc中不支持的命名 可以利用 @objc(name)为他(属性 枚举, 方法名...)名个别名

    • @nonobjc 用来标记oc中不支持的

    • dynamic 将属性或者方法标记为dynamic就是告诉编译器把它当作oc里的属性或方法来使用(runtime),

    • 当需要使用 KVO 或者 runtime的时候需要这样处理

    • 当使用oc的 perform(selector, with:)方法的时候会返回一个可选值(指向AnyObject的指针);但是使用perform(:on:with:waitUntilDone:modes:) and perform(:with:afterDelay:)不会返回可选值

    • 使用 #keyPath() 可以转换为string, #keyPath(class.property) == "property"

    • 可用于KVC 例如person.value(forKey: #keyPath(Person.name)) = person.name

    • 但是测试了下不能修改swift中的只读属性 不知道有什么方便的用处

    • NSClassFromString("MyFramework.MyClass")

    • @IBDesignable 用在class(UIView的子类)声明的前面, 然后就可以在storyBoard中的inspector编辑它;@IBInspectable 用在(UIView的子类)的属性前面, 然后就可以在storyBoard中的inspector编辑它 ,就想系统提供的可以设置颜色,字体...

    • swift中的属性默认是strong类型, 只有可选类型才能被标记为weak

    • oc中的 copy属性 转换为swift中的@NSCopying 必须遵守NSCoding协议

    • 使用Core Data的时候所有的属性和方法需要标记为 @NSManaged

    • 文档中指出"The corresponding reference types can be accessed with their original NS class name prefix."但是beta版本中不能很好的使用NS开头的

    • 在oc和swift的桥接类型之间 直接使用 as 可以相互转换

    • 因为swift中的String和NSString使用的编码方式不一样,所以在swift中要对string使用索引的时候 不能直接使用 Int 或者NSRange

    • 需要使用String.Index and Range

    • swift会将Double, Int, Bool, Uint, Float和NSNumber桥接, 所以可以直接将

    • 这些类型的值使用 as NSNumber转换为NSNumber, 但是逆向进行是得到的可选值 as?

    • Foundation 和Core Foundation 之间的类型有toll-free bridge('免费桥')

    • Foundation中的常量, 在swift中被换为类嵌套的枚举:NSJSONReadingOptions ----- >> JSONSerialization.ReadingOption

    • swift中使用 Core Foundation

    • 如果使用swift处理过的函数不用我们手动管理内存分配;否则需要我们处理

    • 区分的方式: 当返回值是 Unmanaged的时候说明需要我们处理

    • 处理方法: 在使用返回的值之前调用他对应的takeUnretainedValue() 或takeRetainedValue()即可

    • 例如let memoryManagedResult = StringByAddingTwoStrings(str1, str2).takeUnretainedValue()

    • swift中Core Foundation里的类型 Ref后缀被去掉了 例如 CFTypeRef -> CFType

    • 在oc的方法 使用 NS_SWIFT_NOTHROW , 将不会使用swift的异常抛出机制

    • swift中直接使用 is 来实现oc中isKindOfClass: 的功能

    • swift中使用kvo的条件: 1.必须继承自NSObject 2. 被观察的属性 要被标记为 dynamic

    • swift 中的单例很简单:

    1
    2
    3
    class Singleton {
      static let sharedInstance = Singleton()
    }

    或者

    1
    2
    3
    4
    5
    6
    7
    class Singleton {
      static let sharedInstance: Singleton = {
          let instance = Singleton()
          // setup code
          return instance
      }()
    }
    • swift和C的交互: c的函数在swift中均为全局函数

    • 使用CF_SWIFT_NAME 这个宏可以将c中的属性或者函数转换为swift中

    eg:

    1
    Color ColorCreateWithCMYK(float c, float m, float y, float k) CF_SWIFT_NAME(Color.init(c:m:y:k:));

    对应为swift中

    1
    2
    3
    extension Color {
      init(c: Float, m: Float, y: Float, k: Float)
    }
    • c语言中的枚举 如果使用了NS_ENUM定义, 则在swift中被处理为对应的枚举

    • 如果没有使用NS_ENUM定义, 则被处理为结构体

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
     UITableViewCellStyleDefault,
     UITableViewCellStyleValue1,
     UITableViewCellStyleValue2,
     UITableViewCellStyleSubtitle
    };
    //对应与
    enum UITableViewCellStyle: Int {
      case `default`
      case value1
      case value2
      case subtitle
    }
    typedef enum {
     MessageDispositionUnread = 0,
     MessageDispositionRead = 1,
     MessageDispositionDeleted = -1,
    } MessageDisposition;

    对应与

    1
    2
    3
    4
    struct MessageDisposition: RawRepresentable, Equatable {}
    var MessageDispositionUnread: MessageDisposition { get }
    var MessageDispositionRead: MessageDisposition { get }
    var MessageDispositionDeleted: MessageDisposition { get }

    c中的被NS_OPTIONS修饰的枚举, 在swift中是OptionSet类型, -> 即可以使用数组方式选多个值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
          UIViewAutoresizingNone                 = 0,
          UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
          UIViewAutoresizingFlexibleWidth        = 1 << 1,
          UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
          UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
          UIViewAutoresizingFlexibleHeight       = 1 << 4,
          UIViewAutoresizingFlexibleBottomMargin = 1 << 5
    };

    对应与

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public struct UIViewAutoresizing : OptionSet {
      public init(rawValue: UInt)
      public static var flexibleLeftMargin: UIViewAutoresizing { get }
      public static var flexibleWidth: UIViewAutoresizing { get }
      public static var flexibleRightMargin: UIViewAutoresizing { get }
      public static var flexibleTopMargin: UIViewAutoresizing { get }
      public static var flexibleHeight: UIViewAutoresizing { get }
      public static var flexibleBottomMargin: UIViewAutoresizing { get }
    }
    • 在swift中直接使用  let resize = [. flexibleLeftMargin, . flexibleWidth...]

    • 在swift中全局变量和存储属性都被保证只初始化一次,所以用来当作OC里面的#define定义的常量。同时swift全局函数可以当作OC里#define定义的复杂宏(类似函数)

    • swift中的条件编译 自定义编译符的方法(例如: DEBUG_LOGGING)

    • 首先在project 的设置里面设置swift -D DEBUG_LOGGING to set the DEBUG_LOGGING

    • 然后使用 #if DEBUG_LOGGING // 操作 #endif

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #if arch(arm) || arch(arm64)
    #if swift(>=3.0)
    print("Using Swift 3 ARM code")
    #else
    print("Using Swift 2.2 ARM code")
    #endif
    #elseif arch(x86_64)
    print("Using 64-bit x86 code.)
    #else
    print("Using general code.")
    #endif
    • swift中使用指针的方式

    • 使用inout方式 &变量

    • 使用UnsafePointer或者UnsafeMutablePointer

    例如这个函数接受的参数可以传许多种

    1
    2
    3
    4
    5
    6
    let x: Float = 0
    func takesAPointer(_ p: UnsafePointer!) {
    // ...
    }
    takesAPointer(&x)
    takesAPointer([0.0,1.0])
    • 在swift中申明oc中的包含可选实现方法的协议时需要在协议和方法前都标记objc

    1
    2
    3
    4
    5
    @objc public protocol MySwiftProtocol {
    // 必须实现
      func requiredMethod()
      @objc optional func optionalMethod()
    }
    • 将oc的方法或者属性使用NS_SWIFT_NAME()可以为他们在swift中命一个别名

    • 将oc的方法或使用 NS_SWIFT_UNAVAILABLE()可以让他在swift中不可用

    展开全文
  • 随着移动互联网的发展,移动端iOS开发的技术选择也划分了好几个方向,有用 React-Native进行开发的,有用 Objective-C开发的,也有用 Swift开发的,或者 混合 着来。 不同的语言存在都有其可取的优秀之处,也有不足...

    bk

    个人博客

    随着移动互联网的发展,移动端iOS开发的技术选择也划分了好几个方向,有用 React-Native进行开发的,有用 Objective-C开发的,也有用 Swift开发的,或者 混合 着来。

    不同的语言存在都有其可取的优秀之处,也有不足的地方。也应了那句俗话 : 金无足赤,人无完人。

    我们都知道 2014Swift 的横空出世,其首要目的就是 剑指 Objective-C。 但是现在看来也没有完全达到其问世的目的。但是大家对于 Swift 的喜爱依然是一路包容,比如从14年以来一年一学,其情况犹如一年学习一门新语言,但是 Swift 也没有让我们失望,一路高歌猛进,今年也迎来了 Swift 4, 从这一路上来看,有一部分人好多放弃在 Swift 2.X 的路上了,一部分人没有体会到 Swift 3Swift 4 的喜悦. 也有一些人始终徘徊在 Swift 的门口; 一只脚在里面,一只脚在外面。

    从始至终坚持的人是抱着 “苹果(Swift)” 虐我千百遍,我待 苹果(Swift) 如初恋的执着,也正所谓: 拨开云雾见天日 守得云开见月明, 从目前来看 Swift 4 给了开发者很大的 支持与信心

    两种语言熟悉的人都知道 Objective-CSwift 底层是完全不同的机制。

    Objective-C

    Objective-C 的那一套跑不了 运行时, 我相信 KVC 的概念在每个面试者的脑海中都能 信手拈来

    KVC, NSKeyValueCoding,一个非正式的 Protocol,提供一种机制来间接访问对象的属性。
    KVO 就是基于 KVC 实现的关键技术之一
    以类似字典键值对的方式存储信息,以及强大的动态派发,与 C 的结合
    .......

    太多太多

    Swift

    Swif 这门语言是恰恰不同,其类型成员在编译的时候就已经决定,完全是一门安全性的语言。
    尤其对协议的支持。比如对协议的实现有:

    class struct enum

    这里不得不提到 enum 对协议的支持:

    protocol Source {}
    
    enum Action: Source {
        case handler(sender: UIButton)
        case touchUp(sender: UIButton)
    }复制代码

    由此对协议的支持可见一斑。

    苹果在15年提出 Swift 的一种面向协议编程 (Protocol Oriented Programming)

    protocol Work {}
    
    extension Work {
        func doWork() {
            print("do Work")
        }
    }
    
    class action { }
    extension action: Work {}
    
    action().doWork()
    
    // 结果
    :do Work复制代码

    我们看到这个 action 类,并没有对协议进行任何代码方面的书写,而通过 extension 可以直接为协议进行扩展,并给出默认实现。这对于大量重复的代码绝对是一个优秀的解决方案。
    虽然在 Objective-C的时代就有 对重复代码进行封装通过继承来实现也是一种解决方案。但是这种做法是有一定的风险的.

    比如通过继承的方式来:

    class Action1: base1 {
        override func doWork() {
            print("do Work")
        }
    }
    
    class Action2: base1 {
    
    }
    
    Action2().doWork()
    
    // 结果
    :do Work复制代码

    貌似结果是一样的,但是面对大量的代码的时候结构构造的好处是显而易见的,尤其是协议的扩展:根据入口提供额外实现,也可以提供默认的一个实现。往外每个类都是通过其特定的组合方式来实现不同的业务需求的,如果通过以往的继承方式构建在业务繁琐的时候是无法很好的对业务进行抽象封装了,这个时候 Swift协议就可以闪亮登场啦

    其实写过 C++ 的都知道支持多继承,其带来的风险也是显而易见的:
    比如:

    class base1 {
        func doWork() {
            print("do Work")
        }
    }
    
    class Action1: base1 {
        override func doWork() {
            print("do Work")
        }
    }
    
    class Action2: base1 {
        override func doWork() {
            print("do Work")
        }
    }
    
    Action2().doWork()
    
    class Commad: Action1,Action2 {
    
    }
    
    Commad().doWork()复制代码

    这种性质的继承是致命的。当然 SwiftObjective-C 是不支持多继承的,所以上面代码是无效的。但是很重要的是 通过协议,并对协议进行默认实现也是可以达到异曲同工的多继承作用。

    Swift 与 Objective-C 混合

    上面讲过两种语言的底层是完全不同的,但是我相信混合开发的时候怎么去处理呢?
    尤其是在 Objective-C 代码中调用 Swift 的时候,Swift 没有运行时这种东西,而OC调用混导致异常问题。这个时候我们见到的 @objc 就挺身而出了。

    @objc

    只有不是继承 NSObjectSwift 写的 Class 的非私有类型, 在 OC 中调用的时候都是需要加上这个标志

    Swift 建立模型

    基于目前的习惯用到了 ObjectMapper

    使用实例我相信官方介绍的比我详细,我这里就不做僭越 ?
    我这里代码中放出一部分实例:

    class HomeModel: Mappable {
    
        var money: NSNumber = 0      // 人民币
        var hlbAmnt: NSNumber = 0    // 合链币
        var etcAmnt: NSNumber = 0    // 以太经典
        var btcAmnt: NSNumber = 0    // 比特币
        var ltcAmnt: NSNumber = 0    // 莱特币
        var ethAmnt: NSNumber = 0    // 以太坊
    
        var btcAddress: String = ""     // 比特币提币地址
    
        var btcPercent: String = ""     // 比特币资产百分率
        var totalMoney: NSNumber = 0    // 总资产
        var etcPercent: String = ""     // 以太经典的资产百分率
        var moneyPercent: String = ""   // RMB资产百分率
        var ltcPercent: String = ""     // 莱特币的资产百分率
        var ethPercent: String = ""     // 以太坊的资产百分率
        var hlcPercent: String = ""     // 合链币的资产百分率
    
        var frozeBtcAmnt: NSNumber = 0  // 冻结的BTC 的数量
        var frozeEtcaAmnt: NSNumber = 0 // 冻结的以太经典数量
        var frozeHlcAmnt: NSNumber = 0  // 冻结的合链币数量
        var frozeEthAmnt: NSNumber = 0  // 冻结的以太坊数量
        var frozeLtcAmnt: NSNumber = 0  // 冻结的莱特币数量
    
        var btcRate: String = ""        // 比特币单价
        var etcRate: String = ""        // 以太经典单价
        var ethRate: String = ""        // 以太坊价格
        var ltcRate: String = ""        // 莱特币价格
        var hlbRate: String = ""        // 合链币价格
    
    
        required init?(map: Map) {
    
        }
    
        func mapping(map: Map) {
            money <- map["money"]
            hlbAmnt <- map["hlc_amnt"]
            etcAmnt <- map["etc_amnt"]
            btcAmnt <- map["btc_amnt"]
            ltcAmnt <- map["ltc_amnt"]
            ethAmnt <- map["eth_amnt"]
    
            btcAddress <- map["btc_address"]
    
            btcPercent <- map["btc_percent"]
            totalMoney <- map["totalMoney"]
            etcPercent <- map["etcPercent"]
            moneyPercent <- map["money_percent"]
            ltcPercent <- map["ltc_percent"]
            ethPercent <- map["eth_percent"]
            hlcPercent <- map["hlc_percent"]
    
            frozeBtcAmnt <- map["froze_btc_amnt"]
            frozeEtcaAmnt <- map["froze_etc_amnt"]
            frozeHlcAmnt <- map["froze_hlc_amnt"]
            frozeEthAmnt <- map["froze_eth_amnt"]
            frozeLtcAmnt <- map["froze_ltc_amnt"]
    
            btcRate <- map["btc_rate"]
            etcRate <- map["etc_rate"]
            ethRate <- map["eth_rate"]
            ltcRate <- map["ltc_rate"]
            hlbRate <- map["hlc_rate"]
    
        }
    }复制代码

    那么解析是这样的:

    struct ResultSum {
        var status: Bool = false
        var message: String = "未知错误"
        var property: HomeModel?
    
        init(data: Any) {
            let json = JSON(data)
            if json["errorCode"].intValue == 0 {
                status = true
                if let info = json["data"]["userWallet"].dictionaryObject {
                    property = Mapper<HomeModel>().map(JSONObject: info)
                }
            }
            message = json["message"].stringValue
        }
    }复制代码

    其中 property 是可选的,因为理想的情况下才会成功,也有不返回的情况。

    这里的网络请求用到的是 POP (Protocol Oriented Programming) 思想。

    Swift 隐式解包 多重 Optional

    SwiftOptional

    先看例子:

    var searchController: UISearchController!
    
    lazy var reducer: (State, Action) -> (state: State, command: Command?) = {
            [weak self] (state: State, action: Action) in
            var state = state
            var command: Command? = nil
            switch action {
            case .loadData:
                command = Command.loadCycleDate { self?.store.dispatch(.reloadCycleData(images: $0))}
            case .reloadCycleData(let items):
                state.cycleSource = CycleDataSource(images: items, collection: state.cycleSource.collectionView)
            case .selectCycleView(let index):
                command = Command.selectCycleView(index: index)
                break
            case .selectMenu(let index):
                command = Command.selectMenu(index: index)
            }
            return (state, command)
        }复制代码

    例如的示例中 Command?var searchController: UISearchController!

    在对其成员或者方法调用的时候,编译器会对其进行自动解包。!是用来告诉编译器它是一个可以隐式解包的 Optional 值。
    使用的时候直接可以使用,这种情况如果 searchController 如果没有被实例化的时候是 nil ,直接使用会导致程序直接崩溃,而使用的时候也会会被人误以为能直接访问

    比如使用相关操作的时候:

    var searchController: UISearchController!
    
    searchController.hidesNavigationBarDuringPresentation = false
    searchController.searchBar.searchBarStyle = .minimal
    
    没有实例化:直接崩溃 ?复制代码

    如果是 ?的时候

    var searchController: UISearchController?
    
    searchController?.searchResultsUpdater = searchResultsController
    searchController?.hidesNavigationBarDuringPresentation = false复制代码

    在示例中 Command? 表示是一个可选空值,可以是空,可以用
    if let 的 Optional Binding 来处理

                if let resul = result, let models = resul.arrModel {
                    if !resul.status { return }
                }复制代码

    像这些基本性的问题你可以去 Objc 挑几本看看。作者是一个值得敬重的人。

    我这里也没法在短时间内细致的介绍太多,还在招人,证明活比较多 (⊙﹏⊙)b

    Swift 的世界有太多的东西,有时间在说

    ......

    如果你看到这里,插播一条广告:

    公司需要 需要 两名iOS开发人员
    坐标:北京望京 Soho T3 30层
    要求是:Swift, OC 都在中等以上,扎实的计算机基础
    公司不打卡,各种补贴,技术团队来自知名企业:IBM,微软,惠普,三星,百度等等 CTO 早年跟乔布斯有过合作。
    氛围很好。 简历这里: sirBliar@gmail.com
    我直接面试,算是内推。?

    展开全文
  • Swift基础语法2

    2017-08-15 21:23:54
    //: Playground - noun: a place where people can playimport UIKit/* 类的定义 class myClass : superClass{ //定义类的属性和函数 } //注意: //1、定义类 可以没有父类, 那么该类是rootClass class ...
  • My Swift Study

    2019-07-07 01:11:52
    swift3中,闭包默认是非逃逸的。如果一个函数参数可能导致引用循环,那么它需要被显示的标记出来。 没有逃逸的闭包的作用域是不会超过函数本身的, weakSelf unowned 如果能够确定访问时不会被释放的话,尽量用...
  • IOS DB存储之Realm.swift 使用详解1. Realm简介2. Realm安装配置3. Realm数据迁移4. Realm加密5. Realm基本使用6. Realm使用注意 1. Realm简介 2. Realm安装配置 3. Realm数据迁移 4. Realm加密 5. Realm基本...
  • iOS面试总结

    2016-11-25 20:59:37
    一、深复制和浅复制的区别?1、浅复制:只是复制了指向对象的指针,即两个指针指向同一块内存单元!而不复制指向对象的本身,源对象和副本对象是同一个对象。本质:未产生新的对象2、深复制:是将一个类的指针指向的...
  • 以下文章来源于InfoQ,作者InfoQ编辑部 今天给大家推荐一批 BAT 公司常用的开发工具,个个好用,建议转发+收藏。 阿里篇 一、Java 线上诊断工具 Arthas Arthas 是阿里巴巴 2018 年 9 月开源的一款 Java 线上诊断...
  • 这个栏目将持续更新–请iOS的小伙伴关注! 1、多线程的应用 2、GCD实现多个请求都完成之后返回结果 3、A、B两个int数组,得到A数组中B数组不包含的元素 4、事件传递链,页面上一个按钮,按钮和它的superView有...
  • 100家知名企业今年来iOS面试题合集: ...1、swift和oc的区别 2、编译链接 3、synthesize & denamic 4、在项目开发中常用的开发工具有哪些? 5、UITableView & UICollection 6、NSProxy & NSObjec...
  • 前言 (在此先声明,此系列文章,仅仅个人观点,仅供参考,不喜勿喷!) 转载请备明:原文地址 目录 一:iOS开发者定位分析 二:iOS开发基本要求和技术加分项 三:个人简历信息模板分析 ...一:iOS开发者定位分析 ...
  • ios面试题

    2019-06-25 02:39:17
    1.Object-c的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么?Object-c的类不可以多重继承;可以实现多个接口,通过实现多个接口可以完成C++的多重继承;Category是...
  • 1. Object-C的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么? 答: Object-C的类不可以多重继承;可以实现多个接口,通过实现多个接口可以完成C++的多重继承;...
  • ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/)➤GitHub地址:...
  • 一YYModel的使用场景 简单的 Model 与 JSON 相互转换 Model 属性名和 JSON 中的 Key 不相同 Model 包含其他 Model 容器类属性 黑名单与白名单 数据校验与自定义转换 CodingCopyinghashequaldescription ...
  • 100家知名企业今年来iOS面试题合集: 你要的这里都有; 企业要的这里也有; 从基础开始到进阶、深入底层 整理出188个面试题,全是干货 ...1、swift和oc的区别 2、编译链接 3、synthesize & den...
1 2 3 4 5
收藏数 93
精华内容 37