2017-08-15 11:23:18 xiangzhihong8 阅读数 2624
  • Swift 2.1 基础实操高清视频教程

    我们通过一些代码片段学习Swift中的constant和variable、各种基本types和tuple以及Swift中重要的语言特性之一: type inference。并录制了泊学高清视频,同步搭配了泊阅文档,为开发者朋友们的学习提供了一种全新的学习视觉享受。

    8445 人正在学习 去看看 崔轶

WWDC 2017 带来了很多惊喜,在这次大会上,Swift 4 也伴随着 Xcode 9 测试版来到了我们的面前,虽然正式版要8月底9月初才会公布,但很多强大的新特性正吸引我们去学习它。根据大会上已经开放的新特性,先一睹为快。

体验

Swift 4包含在Xcode 9中,您可以从Apple的开发者门户下载最新版本的Xcode 9(您必须拥有一个活跃的开发者帐户)。 每个Xcode测试版将在发布时捆绑最新的Swift 4快照。在阅读时,您会注意到[SE-xxxx]格式的链接。 这些链接将带您到相关的Swift Evolution提案。 如果您想了解有关任何主题的更多信息,请务必查看。

版本迁移

由于Swift 4新增了很多的新的语法特性,这些语法和思想完全区别于Swift 3及以下版本。因此,使用Swift迁移工具将为您处理大部分更改,在Xcode中,您可以导航到编辑/转换/到当前Swift语法…以启动转换工具。

语法改进

extension 中可以访问 private 的属性

例如有如下代码:

struct Date: Equatable, Comparable {
    private let secondsSinceReferenceDate: Double
    static func ==(lhs: Date, rhs: Date) -> Bool {
        return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate
    }
    static func <(lhs: Date, rhs: Date) -> Bool {
        return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate
    }
}

上面代码定义了一个 Date 结构体,并实现 Equatable 和 Comparable 协议。为了让代码更清晰,可读性更好,一般会把对协议的实现放在单独的 extension 中,这也是一种非常符合 Swift 风格的写法。

struct Date {
    private let secondsSinceReferenceDate: Double
}
extension Date: Equatable {
    static func ==(lhs: Date, rhs: Date) -> Bool {
        return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate
    }
}
extension Date: Comparable {
    static func <(lhs: Date, rhs: Date) -> Bool {
        return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate
    }
}

但是在 Swift 3 中,编译就报错了,因为 extension 中无法获取到 secondsSinceReferenceDate 属性,因为它是 private 的。所以,在 Swift 3 中必须把 private 改为 fileprivate。但是如果用 fileprivate,属性的作用域就会更大,可能会不小心造成属性的滥用。

struct Date {
    fileprivate let secondsSinceReferenceDate: Double
}
...

而在 Swift 4 中,private 的属性的作用域扩大到了 extension 中,并且被限定在了 struct 和 extension 内部,这样就不需要再改成 fileprivate 了。

类型和协议的组合类型

考虑以下如下代码:

protocol Shakeable {
    func shake()
}

extension UIButton: Shakeable { /* ... */ }
extension UISlider: Shakeable { /* ... */ }

func shakeEm(controls: [???]) {
    for control in controls where control.state.isEnabled {
    }
    control.shake()
}

???处怎么写呢?在Swift 3中可以这么写。

func shakeEm(controls: [UIControl]) {
    for control in controls where control.isEnabled {
        if control is Shakeable {
            (control as! Shakeable).shake()
        }
    }
}

在Swift 4中,如果将类型和协议用 & 组合在一起使用,代码就可以这么写了。

protocol Shakeable {
    func shake()
}

extension UIButton: Shakeable { /* ... */ }
extension UISlider: Shakeable { /* ... */ }

func shakeEm(controls: [UIControl & Shakeable]) {
    for control in controls where control.state.isEnabled {
        control.shake()
    }// Objective-C API
@interface NSCandidateListTouchBarItem<CandidateType> : NSTouchBarItem
@property (nullable, weak) NSView <NSTextInputClient> *client;
@end
}

Associated Type 追加Where 约束语句

在 Swift 4 中可以在 associated type 后面声明的类型后追加 where 语句,其语法格式如下:

associatedtype Element where <xxx>

下面是 Swift 4 标准库中 Sequence 中 Element 的声明:

protocol Sequence {
    associatedtype Element where Self.Element == Self.Iterator.Element
    // ...
}

它限定了 Sequence 中 Element 这个类型必须和 Iterator.Element 的类型一致。通过 where 语句可以对类型添加更多的约束,使其更严谨,避免在使用这个类型时做多余的类型判断。

Key Paths 语法

先来看看Swift 3的Key Paths语法:

@objcMembers class Kid: NSObject {
    dynamic var nickname: String = ""
    dynamic var age: Double = 0.0
    dynamic var friends: [Kid] = []
}

var ben = Kid(nickname: "Benji", age: 5.5)

let kidsNameKeyPath = #keyPath(Kid.nickname)

let name = ben.valueForKeyPath(kidsNameKeyPath)
ben.setValue("Ben", forKeyPath: kidsNameKeyPath)

在Swift 4中上面的代码可以这样写:

struct Kid {
    var nickname: String = ""
    var age: Double = 0.0
    var friends: [Kid] = []
}

var ben = Kid(nickname: "Benji", age: 8, friends: [])

let name = ben[keyPath: \Kid.nickname]
ben[keyPath: \Kid.nickname] = "BigBen"

相比 Swift 3,Swift 4 的 Key Paths 具有以下优势:

  1. 类型可以定义为 class、struct;
  2. 定义类型时无需加上 @objcMembers、dynamic 等关键字;
  3. 性能更好;
  4. 类型安全和类型推断,例如 ben.valueForKeyPath(kidsNameKeyPath) 返回的类型是
    Any,ben[keyPath: \Kid.nickname] 直接返回 String 类型;
  5. 可以在所有值类型上使用;

下标支持泛型

Swift 支持通过下标来读写容器中的数据,但是如果容器类中的数据类型定义为泛型,以前的下标语法就只能返回 Any,在取出值后需要用 as? 来转换类型。在Swift 4中,下标也可以使用泛型。

struct GenericDictionary<Key: Hashable, Value> {
    private var data: [Key: Value]

    init(data: [Key: Value]) {
        self.data = data
    }

    subscript<T>(key: Key) -> T? {
        return data[key] as? T
    }
}

let dictionary = GenericDictionary(data: ["Name": "Xiaoming"])

let name: String? = dictionary["Name"] // 不需要再写 as? String

字符串

Unicode 字符串

在 Unicode 中,有些字符是由几个其它字符组成的,比如 é 这个字符,它可以用 \u{E9} 来表示,也可以用 e 字符和上面一撇字符组合在一起表示 \u{65}\u{301}。例如:

这里写图片描述
这个 family 是一个由多个字符组合成的字符,打印出来的结果为 一个家庭。上面的代码在 Swift 3 中打印的 count 数是 4,在 Swift 4 中打印出的 count 是 1。

更快的字符处理速度

Swift 4 的字符串优化了底层实现,对于英语、法语、德语、西班牙语的处理速度提高了 3.5 倍。对于简体中文、日语的处理速度提高了 2.5 倍。

去掉了 characters

Swift 3 中的 String 需要通过 characters 去调用的属性方法,在 Swift 4 中可以通过 String 对象本身直接调用,例如:

let values = "one,two,three..."
var i = values.characters.startIndex

while let comma = values.characters[i...<values.characters.endIndex].index(of: ",") {
    if values.characters[i..<comma] == "two" {
        print("found it!")
    }
    i = values.characters.index(after: comma)
}

在Swift 4 可以把上面代码中的所有的 characters 都去掉:

let values = "one,two,three..."
var i = values.startIndex

while let comma = values[i...<values.endIndex].index(of: ",") {
    if values[i..<comma] == "two" {
        print("found it!")
    }
    i = values.index(after: comma)
}

One-sided Slicing

Swift 4 新增了一个语法糖 … 可以对字符串进行单侧边界取子串。例如:

let values = "abcdefg"
let startSlicingIndex = values.index(values.startIndex, offsetBy: 3)
let subvalues = values[startSlicingIndex...] // One-sided Slicing
// defg

将String 当做 Collection 来用

Swift 4 中 String 可以当做 Collection 来用,并不是因为 String 实现了 Collection 协议,而是 String 本身增加了很多 Collection 协议中的方法,使得 String 在使用时看上去就是个 Collection。例如:
翻转字符串

let abc: String = "abc"
print(String(abc.reversed()))
// cba

遍历字符

let abc: String = "abc"
for c in abc {
    print(c)
}
/*
a
b
c
*/

Map、Filter、Reduce

// map
let abc: String = "abc"
_ = abc.map {
    print($0.description)
}

// filter
let filtered = abc.filter { $0 == "b" }

// reduce
let result = abc.reduce("1") { (result, c) -> String in
    print(result)
    print(c)
    return result + String(c)
}
print(result)

Substring

这里写图片描述

在 Swift 中,String 的背后有个 Owner Object 来跟踪和管理这个 String,String 对象在内存中的存储由内存其实地址、字符数、指向 Owner Object 指针组成。Owner Object 指针指向 Owner Object 对象,Owner Object 对象持有 String Buffer。当对 String 做取子字符串操作时,子字符串的 Owner Object 指针会和原字符串指向同一个对象,因此子字符串的 Owner Object 会持有原 String 的 Buffer。当原字符串销毁时,由于原字符串的 Buffer 被子字符串的 Owner Object 持有了,原字符串 Buffer 并不会释放,造成极大的内存浪费。
在 Swift 4 中,做取子串操作的结果是一个 Substring 类型,它无法直接赋值给需要 String 类型的地方。必须用 String() 包一层,系统会通过复制创建出一个新的字符串对象,这样原字符串在销毁时,原字符串的 Buffer 就可以完全释放了。例如:

let big = downloadHugeString()
let small = extractTinyString(from: big)

mainView.titleLabel.text = small // Swift 4 编译报错

mainView.titleLabel.text = String(small) // 编译通过

多行字符串字面量

Swift 3 中写很长的字符串只能写在一行。

func tellJoke(name: String, character: Character) {
    let punchline = name.filter { $0 != character }
    let n = name.count - punchline.count
    let joke = "Q: Why does \(name) have \(n) \(character)'s in their name?\nA: I don't know, why does \(name) have \(n) \(character)'s in their name?\nQ: Because otherwise they'd be called \(punchline)."
    print(joke)
}
tellJoke(name: "Edward Woodward", character: "d")

字符串中间有换行只能通过添加 \n 字符来代表换行。Swift 4 可以把字符串写在一对 “”” 中,这样字符串就可以写成多行。

func tellJoke(name: String, character: Character) {
    let punchline = name.filter { $0 != character }
    let n = name.count - punchline.count
    let joke = """
        Q: Why does \(name) have \(n) \(character)'s in their name?
        A: I don't know, why does \(name) have \(n) \(character)'s in their name?
        Q: Because otherwise they'd be called \(punchline).
        """
    print(joke)
}
tellJoke(name: "Edward Woodward", character: "d")

Swift 标准库

Encoding and Decoding

当需要将一个对象持久化时,需要把这个对象序列化,往常的做法是实现 NSCoding 协议,写过的人应该都知道实现 NSCoding 协议的代码写起来很痛苦,尤其是当属性非常多的时候。Swift 4 中引入了 Codable 帮我们解决了这个问题,这和Java等面向对象语言有异曲同工之妙。例如:

struct Language: Codable {
    var name: String
    var version: Int
}

想让这个 Language 对象的实例持久化,只需要让 Language 符合 Codable 协议即可,Language 中不用写别的代码。符合了 Codable 协议以后,可以选择把对象 encode 成 JSON 或者 PropertyList。

Encode操作

let swift = Language(name: "Swift", version: 4)
if let encoded = try? JSONEncoder().encode(swift) {
    // 把 encoded 保存起来
}

Decode操作

if let decoded = try? JSONDecoder().decode(Language.self, from: encoded) {
    print(decoded.name)
}

Sequence

在Swift 3中,

protocol Sequence {
    associatedtype Iterator: IteratorProtocol
    func makeIterator() -> Iterator
}

由于 Swift 4 中的 associatedtype 支持追加 where 语句,所以 Sequence 做了这样的改进。

protocol Sequence {
    associatedtype Element
    associatedtype Iterator: IteratorProtocol where Iterator.Element == Element
    func makeIterator() -> Iterator
}

Swift 4 中获取 Sequence 的元素类型可以不用 Iterator.Element,而是直接取 Element。例如:

protocol Sequence {
    associatedtype SubSequence: Sequence 
        where SubSequence.SubSequence == SubSequence,
              SubSequence.Element == Element
}

通过 where 语句的限定,保证了类型正确,避免在使用 Sequence 时做一些不必要的类型判断。

Protocol-oriented integers

整数类型的协议也做了修改,新增了 FixedWidthInteger 等协议,具体的协议继承关系如下:
这里写图片描述

Dictionary and Set enhancements

这里简单的罗列了Dictionary 和 Set 增强的功能点:

  • 通过 Sequence 来初始化;
  • 可以包含重复的 Key
  • Filter 的结果的类型和原类型一致
  • Dictionary 的 mapValues 方法
  • Dictionary 的默认值
  • Dictionary 可以分组
  • Dictionary 可以翻转

NSNumber

在 Swift 4 中,把一个值为 999 的 NSNumber 转换为 UInt8 后,能正确的返回 nil,而在 Swift 3 中会不可预料的返回 231。

let n = NSNumber(value: 999)
let v = n as? UInt8 // Swift 4: nil, Swift 3: 231

MutableCollection.swapAt(::)

MutableCollection 现在有了一个新方法 swapAt(::) 用来交换两个位置的值,例如:

var mutableArray = [1, 2, 3, 4]
mutableArray.swapAt(1, 2)
print(mutableArray)
// 打印结果:[1, 3, 2, 4]

Xcode改进

New Build System

Xcode 9 引入了 New Build System,可在 Xcode 9 的 File -> Project Settings… 中选择开启。
这里写图片描述

预编译 Bridging Headers 文件

对于 Swift 和 Objective-C 混合的项目,Swift 调用 Objective-C 时,需要建立一个 Bridging Headers 文件,然后把 Swift 要调用的 Objective-C 类的头文件都写在里面,编译器会读取 Bridging Headers 中的头文件,然后生成一个庞大的 Swift 文件,文件内容是这些头文件内的 API 的 Swift 版本。然后编译器会在编译每一个 Swift 文件时,都要编译一遍这个庞大的 Swift 文件的内容。

有了预编译 Bridging Headers 以后,编译器会在预编译阶段把 Bridging Headers 编译一次,然后插入到每个 Swift 文件中,这样就大大提高了编译速度(苹果宣称 Xcode 9 和 Swift 4 对于 Swift 和 Objective-C 混合编译的速度提高了 40%)。

COW Existential Containers

Swift 中有个东西叫 Existential Containers,它用来保存未知类型的值,它的内部是一个 Inline value buffer,如果 Inline value buffer 中的值占用空间很大时,这个值会被分配在堆上,然而在堆上分配内存是一个性能比较慢的操作。

Swift 4 中为了优化性能引入了 COW Existential Containers,这里的 COW 就代表 “Copy-On-Write”,当存在多个相同的值时,他们会共用 buffer 上的空间,直到某个值被修改时,这个被修改的值才会被拷贝一份并分配内存空间。

移除未调用的协议实现

struct Date {
    private let secondsSinceReferenceDate: Double
}

extension Date: Equatable {
    static func ==(lhs: Date, rhs: Date) -> Bool {
        return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate
    }
}

extension Date: Comparable {
    static func <(lhs: Date, rhs: Date) -> Bool {
        return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate
    }
}

例如,上面的代码中,Date 实现了 Equatable 和 Comparable 协议。编译时如果编译器发现没有任何地方调用了对 Date 进行大小比较的方法,编译器会移除 Comparable 协议的实现,来达到减小包大小的目的。

减少隐式 @objc 自动推断

在项目中想把 Swift 写的 API 暴露给 Objective-C 调用,需要增加 @objc。在 Swift 3 中,编译器会在很多地方为我们隐式的加上 @objc,例如当一个类继承于 NSObject,那么这个类的所有方法都会被隐式的加上 @objc。这样很多并不需要暴露给 Objective-C 也被加上了 @objc。大量 @objc 会导致二进制文件大小的增加。

class MyClass: NSObject {
    func print() { ... } // 包含隐式的 @objc
    func show() { ... } // 包含隐式的 @objc
}

在 Swift 4 中,隐式 @objc 自动推断只会发生在很少的当必须要使用 @objc 的情况,比如:

  1. 复写父类的 Objective-C 方法
  2. 符合一个 Objective-C 的协议

其它大多数地方必须手工显示的加上 @objc。减少了隐式 @objc 自动推断后,Apple Music app 的包大小减少了 5.7%。

兼容

Xcode 9 中同时集成了 Swift 3.2 和 Swift 4。

  1. Swift 3.2 完全兼容 Swift 3.1,并会在过时的语法或函数上报告警告。
  2. Swift 3.2 具有 Swift 4 的一些写法,但是性能不如 Swift 4。
  3. Swift 3.2 和 Swift 4 可以混合编译,可以指定一部分模块用 Swift 3.2 编译,一部分用 Swift 4 编译。
  4. 迁移到 Swift 4 后能获得 Swift 4 所有的新特性,并且性能比 Swift 3.2 好。

当 Xcode 正式版发布后,现有的 Swift 代码可以直接升级到 Swift 3.2 而不用做任何改动,后续可以再迁移到 Swift 4。或者直接迁移到 Swift 4 也可以,Swift 4 相比 Swift 3 的 API 变化还是不大的,很多第三方库都可以直接用 Swift 4 编译。Swift 1 到 2 和 Swift 2 到 3 的迁移的痛苦在 3 到 4 的迁移上已经大大改善了。

参考资料:

2018-03-12 19:30:02 u014760573 阅读数 116
  • Swift 2.1 基础实操高清视频教程

    我们通过一些代码片段学习Swift中的constant和variable、各种基本types和tuple以及Swift中重要的语言特性之一: type inference。并录制了泊学高清视频,同步搭配了泊阅文档,为开发者朋友们的学习提供了一种全新的学习视觉享受。

    8445 人正在学习 去看看 崔轶

Swift,苹果于2014年在苹果开发者大会发布的新开发语音,可与OC共同运行于Mac OS 和ios平台,用于搭建基于苹果平台的应用程序。之前Swift一直不稳定,看过书,读过blog,今天开始,我以对比(OC:Swift)的形式将swift中常见的语法,以及注意事项,欢迎大家跟我一起学习Swift。

// 1.导入框架
// OC导入框架   #import <UIKit/UIKit.h>
// Swift 导入框架   import UIKit

// 2.定义变量
// OC 定义变量int a = 10; a = 5;
// Swift 定义变量  var a:Int = 10

// 3.定义常量
// OC:  const int a = 5;
// Swift:   let a:Int = 5;

// 4.创建对象
// OC:  UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
//      [view setBackgroundColor:[UIColor redColor]];
// Swift:  let view:UIView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
//      view.backgroundColor = UIColor.red;

// 5.控制台输出
// OC:   int const a = 10; NSLog(@"a=%zd",a);
// Swift:   let a:Int = 10; print("a=\(a)");


    


2018-02-01 12:49:21 ZY_FlyWay 阅读数 216
  • Swift 2.1 基础实操高清视频教程

    我们通过一些代码片段学习Swift中的constant和variable、各种基本types和tuple以及Swift中重要的语言特性之一: type inference。并录制了泊学高清视频,同步搭配了泊阅文档,为开发者朋友们的学习提供了一种全新的学习视觉享受。

    8445 人正在学习 去看看 崔轶

我们在用OC开发的时候通过宏定义自定义打印,swift没有宏定义,那么我们只能这么自定义了。


先贴下代码(因为容易理解没有备注):

let APP_NAME = Bundle.main.infoDictionary!["CFBundleName"] as! String


func ZYLOG<T>(message:T,file:String = #file,funcName:String = #function,lineName:Int = #line){
    
    //这个DEBUG 是一个debug时候的一个flag需要自己去xcode设置项设置。
    #if DEBUG
   
    let  flieName = (file as NSString).lastPathComponent
    print("\(APP_NAME)/\(flieName)/\(funcName)/\(lineName):\(message)")

    #endif
}


区分DEBUG/RELEASE 标识设置:




这样你就可以全局调用打印log了。

2018-03-15 09:15:29 qq_35612929 阅读数 293
  • Swift 2.1 基础实操高清视频教程

    我们通过一些代码片段学习Swift中的constant和variable、各种基本types和tuple以及Swift中重要的语言特性之一: type inference。并录制了泊学高清视频,同步搭配了泊阅文档,为开发者朋友们的学习提供了一种全新的学习视觉享受。

    8445 人正在学习 去看看 崔轶

动态获取命名空间


前提知识点: 先说一下获取命名空间的作用,在Swift的项目中,打印当前类的时候,会发现,相比OC,类名前多了一个命名空间。因此,在调用NSClassFromString(“”)这类方法的时候,在类名前,必须加上命名空间. 。举个小例子如下:

//项目名称:starProduct
//要获取的类:AAAViewController
let aaa = NSClassFromString("starProduct.AAAViewController")

但是,命名空间在Build Settings 里面的 Product Name 里是可以修改的。所以,为了保证项目的稳定性,必须要动态的获取命名空间:

//动态获取命名空间
let ns = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String
//所以按上面的要求,可以这么写
let aaa = NSClassFromString(ns + ".AAAViewController")
//实际应用中,一般类名也是传过来的,所以假设传过来的参数名为className。
let aaa = NSClassFromString(ns + "." + className)
2017-04-01 16:14:32 qq_22879593 阅读数 3125
  • Swift 2.1 基础实操高清视频教程

    我们通过一些代码片段学习Swift中的constant和variable、各种基本types和tuple以及Swift中重要的语言特性之一: type inference。并录制了泊学高清视频,同步搭配了泊阅文档,为开发者朋友们的学习提供了一种全新的学习视觉享受。

    8445 人正在学习 去看看 崔轶

Swift3.0 指针

最近试着用Swift基于AudioQueue写一个音乐播放器,但是AudioQueue的API都是C函数,避免不了操作指针。经常卡在指针上,于是决定先静下心来学习一下Swift中的指针。baidu了很多,但是没有系统讲解Swift指针的文章,一边学一边记录,写下这些作为积累。这次学习分为四个部分:UnsafePointer, UnsafeMutablePointer, UnsafeRawPointer, UnsafeMutableRawPointer。有不对的地方欢迎指出!

在Swift中,指针由结构体 struct UnsafePointer<Pointee>struct UnsafeMutablePointer<Pointee> 表示,但是不能通过”&”获取一个UnsafePointerUnsafeMutablePointer的实例,只能作为inout参数使用。UnsafePointer相对于UnsafeMutablePointer,其所指向的地址的值是不可变的

一、UnsafeMutablePointer

UnsafeMutablePointe引用的内存有三种状态:

1. Not Allocated

2. Allocated but not initialized

3. Allocated and initialized

只有在状态3时,可以安全的使用pointee属性来setget

1.初始化

allocate() initialize() deinitialize deallocate() 这四个方法来管理这个指针,并确保指针的pointee不会出错

(1) allocate

let a_unsafe_mutable_pointer = UnsafeMutablePointer<Int>.allocate(capacity: 0)

Tips:

1. 需要指明要声明的指针类型

2. allocate是静态方法

(2) initialize

a_unsafe_mutable_pointer.initialize(to: 5)
//或
a_unsafe_mutable_pointer.pointee = 5

这两种方法的结果是相同的

(3) deinitialize

a_unsafe_mutable_pointer.deinitialize(count: 1)

该方法貌似没有真正deintialize,只是返回了一个指向相同地址的UnsafeMutableRawPointer,并且a_unsafe_mutable_pointer.pointee仍然是可以正常使用

(4) deallocate

a_unsafe_mutable_pointer.deallocate(capacity: 1)

此时a_unsafe_mutable_pointer不可用了。

2.互相转化

(1) UnsafeMutablePointer -> Swift

let a : Int = a_unsafe_mutable_pointer.pointee

(2) Swift -> UnsafeMutablePointer

let a : UnsafeMutablePointer = &a 

以上代码是错误的,只能作为函数参数使用,如下所示

func printPointer(ptr:UnsafeMutablePointer<Int>) {
        print("UnsafeMutablePointer:\(ptr)")
        print("pointee:\(ptr.pointee)")
}
printPointer(ptr:&a)

3.暂时绑定

将该内存暂时绑定为一种类型,例子中将Int型绑定为Int8

a_unsafe_mutable_pointer.initialize(to: 0x0102030A)
a_unsafe_mutable_pointer.withMemoryRebound(to: Int8.self, capacity: 1, {
    ptr in
    print(String(ptr.pointee,radix:16)) //1
    print(String(ptr.advanced(by: 1).pointee,radix:16)) //2
    print(String(ptr.advanced(by: 2).pointee,radix:16)) //3
    print(String(ptr.advanced(by: 3).pointee,radix:16)) //a
})

二、UnsafePointer

  1. UnsafePointer中的pointee属性只能get不能set
  2. UnsafePointer中没有allocate方法。

1.初始化

let a_unsafe_pointer = UnsafePointer<Int>.init(a_unsafe_mutable_pointer)

可以由UnsafeMutablePointerOpaquePointer或其他UnsafePointer创建一个UnsafePointer指针。其他与UnsafeMutablePointer类似

2.互相转换

(1) UnsafeMutablePointer -> UnsafePointer

let a_unsafe_pointer = UnsafePointer<Int>.init(a_unsafe_mutable_pointer)

(2) UnsafePointer -> UnsafeMutablePointer

let a_unsafe_mutable_pointer = UnsafePointer<Int>.init(mutating:a_unsafe_pointer)

(3) UnsafePointer -> Swift

let a : Int = a_unsafe_pointer.pointee

(4) Swift -> UnsafePointer

UnsafeMutablePointer类似,只能作为函数参数引用

3.withUnsafePointer方法

withUnsafePointer方法可以直接使用指针,但是不能改变pointee的值

var a = 0
var b = a
a = withUnsafePointer(to: &a, {
        ptr in
        return ptr.pointee + 2 
        //此时,新开辟空间,令a指向该地址,其值为2
        //b仍指向a原来的地址,值为0
})

var _ = withUnsafePointer(to: &b, {
            ptr in
            let size = MemoryLayout<Int>.size //8
            //advance(by:Int)移动指针到a
            let movedPointer = ptr.advanced(by: -size) 
            print("b:\(ptr.pointee)") //b = 0
            print("a:\(movedPointer.pointee)") //a = 2
        })

三、UnsafeMutableRawPointer

UnsafeMutableRawPointer按我的理解就是无类型的原始指针

1.分配内存

var size = MemoryLayout<Int>.size
//其中alignTo是开辟内存中的对齐,不是很了解,貌似会影响效率,太底层了,不太懂
var a_unsafe_mutable_raw_pointer = UnsafeMutableRawPointer.allocate(bytes: size, alignedTo: size)

这时,这个a_unsafe_mutable_raw_pointer感觉没啥用,还是需要转换为UnsafeMutablePointer来对内存进行操作。

2.初始化

初始化内存为Int类型,并赋值为1

a_unsafe_mutable_raw_pointer.initializeMemory(as: Int.self, to: 1)

3.绑定UnsafeMutablePointer

绑定了UnsafeMutablePointer,使用UnsafeMutablePointer才能对内存进行赋值,有两种方法绑定:

(1) bindMemory()

该方法绑定内存为指定类型并返回一个UnsafeMutablePointer<指定类型>的指针

var a_unsafe_mutable_pointer = a_unsafe_mutable_raw_pointer.bindMemory(to: Int.self, capacity: 1)

(2) assumingMemoryBound()

该方法意思是直接转换这个原始指针为一个UnsafeMutablePointer<指定类型>的指针

var a_unsafe_mutable_pointer = a_unsafe_mutable_raw_pointer.assumingMemoryBound(to: Int.self)

-
这样,就可以使用这个a_unsafe_mutable_pointer进行其他操作了。

4.deallocate

最后,需要释放内存,首先要deinitialize,再deallocate

a_unsafe_mutable_pointer.deinitialize()
a_unsafe_mutable_raw_pointer.deallocate(bytes: size, alignedTo: size)

5.转换

(1) Swift -> UnsafeMutableRawPointer

var a = 10
var ptr = UnsafeMutableRawPointer(&a)

(2) UnsafeMutableRawPointer -> Swift

其过程应当为UnsafeMutableRawPointer转换为UnsafeMutablePointer,再由UnsafeMutablePointer转换为Swift指针

四、UnsafeRawPointer

UnsafeRawPointer只能由其他指针用init方法得到,与UnsafePointer类似,没有allocate静态方法。但是,与UnsafeMutableRawPointer类似的有两种绑定方法,绑定成UnsafePointer指针。

没有更多推荐了,返回首页