草庐IT

Codable发布我就不学,摸鱼爽歪歪!

iOS是大鑫呀 2023-03-28 原文

前言

对于大多数的应用程序来说,最常见的任务就是进行网络数据的发送和接收,但是在执行此操作之前,我们需要通过编码或者序列化的方式将数据转换为合适的格式来发送,然后还需要将收到的网络数据转换为合适的格式,这样才能在应用中使用它们,这样的过程叫做解码或着叫反序列化。

那如何去定义这个格式呢!这里就不得不提 JSON 了,JSON 目前是网络通信发送和接收数据最常用的格式,但是在 Swift4.0 之前,大家都是用一些第三方的开源库来对 JSON 格式进行解析。

终于, Apple 在 Swift4.0 的 Foundtion 模块中添加了对 JSON 解析的原生支持,它的功能强大而且易于使用,接下来就让我带大家 了解下在 swift 里如何来对你的数据进行 encoding 和 decoding 吧!

基础知识介绍

在 swift 里要对 JSON 进行处理的话,首先需要了解的概念就是:Codable, Codable 其实它不是一个协议,而是另外俩个协议的组合:Decodable 和 Encodable,它的源码如下所示:

public typealias Codable = Decodable & Encodable 所以聪明的你一定可以猜到,只要数据模型遵行了 Codable 协议,那么就可以方便的进行 JSON 和数据模型的相互转换了。

在 Swift4.0 中,Apple 提供了 JSONEncoder 和 JSONDecoder 俩对象来处理 JSON 的编码和解码,核心代码如下:

let encoder = JSONEncoder() let decoder = JSONDecoder() 相关的概念已介绍完毕,你准备好迎接挑战了吗?

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发交流群:130 595 548,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)

JSON 转数据模型

TASK 1:简单的数据结构

如果你的 JSON 结构和你使用的数据模型结构一致的话,那么解析过程将会非常简单,请看下面内容:

下面给出的是一个歌曲的 JSON 数据,我现在要将其转换为 SongModel。

let song = """ { "singer": "The Chainsmokers", "name": "Something Just Like This", } """ SongModel

/// SongModel模型,遵循 Codable 协议 struct SongModel: Codable { var singer: String? var name: String? } 转换过程如下

if let jsonData = song.data(using: String.Encoding.utf8) { if let sSong = try? JSONDecoder().decode(SongModel.self, from: jsonData) { dump(sSong) } } 输出结果如下

▿ JSONDecoderDemo.SongModel ▿ singer: Optional("The Chainsmokers") - some: "The Chainsmokers" ▿ name: Optional("Something Just Like This") - some: "Something Just Like This" 这样就完成了解析,是不是很简单!

NOTE:在数据模型的成员变量中,基本数据类型如:String、Int、Float等都已经实现了 Codable 协议,因此如果你的数据类型只包含这些基本数据类型的属性,只需要在类型声明中加上 Codable 协议就可以了,不需要写任何实际实现的代码。

TASK 2: 解析数组

假如这是我们收到的一张专辑 Album 的 JSON 数据,现在要把它转化成 AlbumModel 数据模型。

let album = """ { "singer": "The Chainsmokers", "name": "Something Just Like This", "songs":[ "Something Just Like This", "Closer", "Young", "All We Know" ] } """ AlbumModel

struct AlbumModel: Codable { var singer: String? var name: String? var songs: [String]? } 转换过程如下

if let jsonData = album.data(using: String.Encoding.utf8) { if let sSong = try? JSONDecoder().decode(AlbumModel.self, from: jsonData) { dump(sSong) } } 输出结果为

▿ JSONDecoderDemo.AlbumModel ▿ singer: Optional("The Chainsmokers") - some: "The Chainsmokers" ▿ name: Optional("Something Just Like This") - some: "Something Just Like This" ▿ songs: Optional(["Something Just Like This", "Closer", "Young", "All We Know"]) ▿ some: 4 elements - "Something Just Like This" - "Closer" - "Young" - "All We Know" 和上面的转换如出一辙,想必你现在心里已经在默默的嘀咕:这么简单还用你讲?

那接下来就请准备迎接新的挑战把!

TASK 3:结构不一致

上面所演示的 JSON 数据格式都是与数据模型里的成员变量一一对应的,但是,在实际开发中,你会经常遇到数据源的格式和数据模型结构 不一致的情况,很多情况下可能是服务端与客户端没有统一好接口的格式,然后各自就开始开发,到需要进行调试的时候,客户端一收到消息,就懵逼了:

NOTE: 所以在这里我非常建议大家在做功能开发之前一定要先把接口文档定义好,定义好,定义好,重要的事情讲三遍。

这样服务端和客户端之间定义的数据格式就存在了差异,无论怎样当然总有一方需要作出让步来做到兼容,那么当客户端想要做兼容时,该怎么处理呢!我们先来看个例子:

例如服务端返回的数据为:

let album = """ { "singer": "The Chainsmokers", "name": "Something Just Like This", "songList":[ "Something Just Like This", "Closer", "Young", "All We Know" ] } """ 可以看到,原来的 songs 换成了 songList,我们执行下原先的代码,看下输出:

▿ JSONDecoderDemo.AlbumModel ▿ singer: Optional("The Chainsmokers") - some: "The Chainsmokers" ▿ name: Optional("Something Just Like This") - some: "Something Just Like This" - songs: nil 发现 songs 字段变成了 nil, 这个解析就失败了,那如何做到不修改我之前定义的数据模型的成员变量,来做到兼容呢!这时候就需要用到 CodingKey 协议了, 借助 CodingKey 可以用来映射数据模型的成员变量,首先在数据模型中添加一个特殊的枚举类型:

private enum CodingKeys: String, CodingKey 添加完后的数据模型代码如下:

struct AlbumModel: Codable { var singer: String? var name: String? var songs: [String]? enum CodingKeys: String, CodingKey { case singer = "singer" case name = "name" case songs = "songList" } } 输出结果为

▿ JSONDecoderDemo.AlbumModel ▿ singer: Optional("The Chainsmokers") - some: "The Chainsmokers" ▿ name: Optional("Something Just Like This") - some: "Something Just Like This" ▿ songs: Optional(["Something Just Like This", "Closer", "Young", "All We Know"]) ▿ some: 4 elements - "Something Just Like This" - "Closer" - "Young" - "All We Know" 这样,我们是不是就可以正常解析 JSON 数据了,是不是很强大。

接下来再增加一个难度!

当给你的唱片的 JSON 结构是这样的,你该怎么解析呢!

let album = """ { "singer": "The Chainsmokers", "name": "Something Just Like This", "songs": "Something Just Like This" } """ 根据上面给出的例子,你会发现它依然与你的数据模型不匹配,原来的 songs 字段不是数组格式了,那如何才能正常的解析到数据模型上去呢,这时候就需要我们自己来实现编码解码的逻辑了。

首先在 AlbumModel 中加入以下代码:

struct AlbumModel: Codable { var singer: String? var name: String? var songs: [String]? // 1 enum CodingKeys: String, CodingKey { case singer = "singer" case name = "name" case songs = "songs" } // 解码: JSON 转 Model init(from decoder: Decoder) throws { // 2 let container = try decoder.container(keyedBy: CodingKeys.self) // 3 singer = try container.decode(String.self, forKey: .singer) name = try container.decode(String.self, forKey: .name) let ss = try container.decode(String.self, forKey: .songs) songs = [ss] } // 编码: Model 转 JSON func encode(to encoder: Encoder) throws { // 4 var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(singer, forKey: .singer) try container.encode(name, forKey: .name) try container.encode(songs, forKey: .songs) } } 解释如下

  1. 创建 CodingKeys 枚举,用于映射 JSON 字段。
  2. 创建一个解码器容器,来存储 JSON 里的属性。
  3. 使用适当的类型和编码键从容器中提取歌手和专辑名和歌单,由于歌单是数组类型的,所以需要将提取到的歌转换成数组。
  4. 创建 KeyedEncodingContainer 容器来对数据模型里的属性进行编码。
转换过程如下

if let jsonData = album.data(using: String.Encoding.utf8) { if let aAlbum = try? JSONDecoder().decode(AlbumModel.self, from: jsonData) { dump(aAlbum) } } 结果如下

▿ JSONDecoderDemo.AlbumModel ▿ singer: Optional("The Chainsmokers") - some: "The Chainsmokers" ▿ name: Optional("Something Just Like This") - some: "Something Just Like This" ▿ songs: Optional(["Something Just Like This"]) ▿ some: 1 element - "Something Just Like This" 可以看到通过上面的代码,已经可以将 JSON 中原先的 String 转换成数据模型中的数组类型了。

注意:如果需要借助 CodingKeys 解决字段不一致的情况,即使其他的属性不需要映射,也必须将其包含在枚举中,譬如:singer, name,否则会报错。

TASK 4: 复杂的嵌套

除了处理简单的数据模型,Codable 还可以处理复杂的嵌套数据模型,首先解释下什么是嵌套数据模型:

譬如我有个专门处理专辑的数据模型叫 AlbumModel,它里面内嵌了 SongModel 的属性,这就是嵌套。这里必须要说明的就是嵌套的数据模型以及嵌套的子模型都必须遵循 Codable 协议,下面我们举个嵌套的数据模型的例子来说明一下:

/// 专辑模型 struct AlbumModel: Codable { // 专辑名 var albumName: String? // 发布时间 var releaseTime: String? // 歌单(嵌套) var songs: [SongModel]? } /// 歌曲模型 struct SongModel: Codable { // 歌手(嵌套) var singer: SingerModel? // 歌曲 var name: String? } /// 歌手模型 struct SingerModel: Codable { // 姓名 var name: String? // 年龄 var age: Int? } JSON 数据结构

let album = """ { "albumName": "Something Just Like This", "releaseTime": "2017-02-22", "songs":[ { "singer": { "name":"The Chainsmokers", "age": 30 }, "name": "Something Just Like This" }, { "singer": { "name":"The Chainsmokers", "age": 30 }, "name": "Closer" }, { "singer": { "name":"The Chainsmokers", "age": 30 }, "name": "Young" }, { "singer": { "name":"The Chainsmokers", "age": 30 }, "name": "All We Know" } ] } """ 转换过程如下

if let jsonData = album.data(using: String.Encoding.utf8) { if let aAlbum = try? JSONDecoder().decode(AlbumModel.self, from: jsonData) { dump(aAlbum) } } 输出结果为

JSONDecoderDemo.AlbumModel ▿ albumName: Optional("Something Just Like This") - some: "Something Just Like This" ▿ releaseTime: Optional("2017-02-22") - some: "2017-02-22" ▿ songs: Optional([JSONDecoderDemo.SongModel(singer: Optional(JSONDecoderDemo.SingerModel(name: Optional("The Chainsmokers"), age: Optional(30))), name: Optional("Something Just Like This")), JSONDecoderDemo.SongModel(singer: Optional(JSONDecoderDemo.SingerModel(name: Optional("The Chainsmokers"), age: Optional(30))), name: Optional("Closer")), JSONDecoderDemo.SongModel(singer: Optional(JSONDecoderDemo.SingerModel(name: Optional("The Chainsmokers"), age: Optional(30))), name: Optional("Young")), JSONDecoderDemo.SongModel(singer: Optional(JSONDecoderDemo.SingerModel(name: Optional("The Chainsmokers"), age: Optional(30))), name: Optional("All We Know"))]) ▿ some: 4 elements ▿ JSONDecoderDemo.SongModel ▿ singer: Optional(JSONDecoderDemo.SingerModel(name: Optional("The Chainsmokers"), age: Optional(30))) ▿ some: JSONDecoderDemo.SingerModel ▿ name: Optional("The Chainsmokers") - some: "The Chainsmokers" ▿ age: Optional(30) - some: 30 ▿ name: Optional("Something Just Like This") - some: "Something Just Like This" ▿ JSONDecoderDemo.SongModel ▿ singer: Optional(JSONDecoderDemo.SingerModel(name: Optional("The Chainsmokers"), age: Optional(30))) ▿ some: JSONDecoderDemo.SingerModel ▿ name: Optional("The Chainsmokers") - some: "The Chainsmokers" ▿ age: Optional(30) - some: 30 ▿ name: Optional("Closer") - some: "Closer" ▿ JSONDecoderDemo.SongModel ▿ singer: Optional(JSONDecoderDemo.SingerModel(name: Optional("The Chainsmokers"), age: Optional(30))) ▿ some: JSONDecoderDemo.SingerModel ▿ name: Optional("The Chainsmokers") - some: "The Chainsmokers" ▿ age: Optional(30) - some: 30 ▿ name: Optional("Young") - some: "Young" ▿ JSONDecoderDemo.SongModel ▿ singer: Optional(JSONDecoderDemo.SingerModel(name: Optional("The Chainsmokers"), age: Optional(30))) ▿ some: JSONDecoderDemo.SingerModel ▿ name: Optional("The Chainsmokers") - some: "The Chainsmokers" ▿ age: Optional(30) - some: 30 ▿ name: Optional("All We Know") - some: "All We Know" 这样这个嵌套就被解决了,接下来再挑战一个难度更大的,请看代码:

let album = """ { "albumName": "Something Just Like This", "releaseTime": "2017-02-22", "songs": { "favorite":[ { "singer": { "name":"The Chainsmokers", "age": 30 }, "name": "Something Just Like This" }, { "singer": { "name":"The Chainsmokers", "age": 30 }, "name": "Closer" }, { "singer": { "name":"The Chainsmokers", "age": 30 }, "name": "Young" }, { "singer": { "name":"The Chainsmokers", "age": 30 }, "name": "All We Know" } ] } } """ 可以看到,在歌单 Songs 中又嵌套了一个 favorite 字段,这个 JSON 结构相比 AlbumModel 这个数据模型又加深了一层,那该如何解析呢!

这里我们就要用到 nestedContainer 来处理这种嵌套,首先在 AlbumModel 加入如下代码:

/// 专辑模型 struct AlbumModel: Codable { // 专辑名 var albumName: String? // 发布时间 var releaseTime: String? // 歌单 var songs: [SongModel]? // 1 enum CodingKeys: String, CodingKey { case albumName, releaseTime, songs } // 2 enum favoriteKeys: CodingKey { case favorite } // 解码: JSON 转 Model init(from decoder: Decoder) throws { // 3 let container = try decoder.container(keyedBy: CodingKeys.self) albumName = try container.decode(String.self, forKey: .albumName) releaseTime = try container.decode(String.self, forKey: .releaseTime) // 4 let favoriteContainer = try container.nestedContainer(keyedBy: favoriteKeys.self, forKey: .songs) songs = try favoriteContainer.decode([SongModel].self, forKey: .favorite) } // 编码: Model 转 JSON func encode(to encoder: Encoder) throws { // 5 var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(albumName, forKey: .albumName) try container.encode(releaseTime, forKey: .releaseTime) // 6 var favoriteContainer = container.nestedContainer(keyedBy: favoriteKeys.self, forKey: .songs) try favoriteContainer.encode(songs, forKey: .favorite) } } /// 歌曲模型 struct SongModel: Codable { // 歌手 var singer: SingerModel? // 歌曲 var name: String? } /// 歌手模型 struct SingerModel: Codable { // 姓名 var name: String? // 年龄 var age: Int? } 解析如下

  1. 首先创建最顶层的 CodingKeys
  2. 创建嵌套层的 CodingKeys
  3. 创建顶层 CodingKeys 对应的容器,并对其解码
  4. 创建嵌套层的容器,并对 favorite 解码
  5. 创建编码容器,并对 albumName 和 releaseTime 编码
  6. 获取嵌套容器,并对 favorite 编码
转换过程

if let jsonData = album.data(using: String.Encoding.utf8) { if let aAlbum = try? JSONDecoder().decode(AlbumModel.self, from: jsonData) { dump(aAlbum) } } ***输出如下***:

▿ JSONDecoderDemo.AlbumModel ▿ albumName: Optional("Something Just Like This") - some: "Something Just Like This" ▿ releaseTime: Optional("2017-02-22") - some: "2017-02-22" ▿ songs: Optional([JSONDecoderDemo.SongModel(singer: Optional(JSONDecoderDemo.SingerModel(name: Optional("The Chainsmokers"), age: Optional(30))), name: Optional("Something Just Like This")), JSONDecoderDemo.SongModel(singer: Optional(JSONDecoderDemo.SingerModel(name: Optional("The Chainsmokers"), age: Optional(30))), name: Optional("Closer")), JSONDecoderDemo.SongModel(singer: Optional(JSONDecoderDemo.SingerModel(name: Optional("The Chainsmokers"), age: Optional(30))), name: Optional("Young")), JSONDecoderDemo.SongModel(singer: Optional(JSONDecoderDemo.SingerModel(name: Optional("The Chainsmokers"), age: Optional(30))), name: Optional("All We Know"))]) ▿ some: 4 elements ▿ JSONDecoderDemo.SongModel ▿ singer: Optional(JSONDecoderDemo.SingerModel(name: Optional("The Chainsmokers"), age: Optional(30))) ▿ some: JSONDecoderDemo.SingerModel ▿ name: Optional("The Chainsmokers") - some: "The Chainsmokers" ▿ age: Optional(30) - some: 30 ▿ name: Optional("Something Just Like This") - some: "Something Just Like This" ▿ JSONDecoderDemo.SongModel ▿ singer: Optional(JSONDecoderDemo.SingerModel(name: Optional("The Chainsmokers"), age: Optional(30))) ▿ some: JSONDecoderDemo.SingerModel ▿ name: Optional("The Chainsmokers") - some: "The Chainsmokers" ▿ age: Optional(30) - some: 30 ▿ name: Optional("Closer") - some: "Closer" ▿ JSONDecoderDemo.SongModel ▿ singer: Optional(JSONDecoderDemo.SingerModel(name: Optional("The Chainsmokers"), age: Optional(30))) ▿ some: JSONDecoderDemo.SingerModel ▿ name: Optional("The Chainsmokers") - some: "The Chainsmokers" ▿ age: Optional(30) - some: 30 ▿ name: Optional("Young") - some: "Young" ▿ JSONDecoderDemo.SongModel ▿ singer: Optional(JSONDecoderDemo.SingerModel(name: Optional("The Chainsmokers"), age: Optional(30))) ▿ some: JSONDecoderDemo.SingerModel ▿ name: Optional("The Chainsmokers") - some: "The Chainsmokers" ▿ age: Optional(30) - some: 30 ▿ name: Optional("All We Know") - some: "All We Know" 挑战成功,看到这里是不是已经有点晕了,说实话其实我自己也不知道我在表达啥,我也晕了,哈哈!

Task 6:处理派生类

下面我们来看下一个特殊的数据模型结构,它应该怎么去转换呢!

当一个类遵循了 Codable 协议,那么它自身是可以很方便的使用 JSONEncoder 和 JSONDecoder 来 JSON 化和反 JSON 化的,但是如果有别的类继承了它,那么对该子类的 JSON 化和反 JSON 化就不是那么方便了。

首先来看个例子, SongMusic 的子类:

class Music: Codable { var kind: String? } class Song: Music { var name: String? } JSON 数据为:

let jsonString = """ { "kind": "popular", "name": "Something Just Like This" } """ 数据解析

if let jsonData = jsonString.data(using: String.Encoding.utf8) { if let song = try? JSONDecoder().decode(Song.self, from: jsonData) { dump(song) } } 结果

▿ JSONDecoderDemo.Song #0 ▿ super: JSONDecoderDemo.Music ▿ kind: Optional("popular") - some: "popular" - name: nil 通过上面的结果发现,Song 类的实例只解析出了父类中的 kind 字段,而自己的 name 未能解析,这说明 Codable 在继承中是无效的,当你在派生类中声明遵循该协议时,会报如下错误:

Redundant conformance of 'Song' to protocol 'Decodable' Redundant conformance of 'Song' to protocol 'Encodable' 那如何才能解决这个问题呢!

这时候,就需要我们自行实现 Codable 协议了,代码如下:

class Song: Music { var name: String? enum CodingKeys: String, CodingKey { case type case name } init(type: String, name songName:String) { self.name = songName super.init(type: type) } required init(from decoder: Decoder) throws { try super.init(from: decoder) let container = try decoder.container(keyedBy: CodingKeys.self) type = try container.decode(String.self, forKey: .type) name = try container.decode(String.self, forKey: .name) } override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(type, forKey: .type) try container.encode(name, forKey: .name) } } 转换结果

▿ JSONDecoderDemo.Song #0 ▿ super: JSONDecoderDemo.Music ▿ type: Optional("popular") - some: "popular" ▿ name: Optional("Something Just Like This") - some: "Something Just Like This" 通过上面的结果显示,我已经成功将 JSON 转成了相应的数据模型,那么对派生类的处理,我们只需要参考上面的代码,自行实现 Codable 协议,就可以避免上述的错误。

数据模型转 JSON

当实现 Codable 协议的某个对象想要转为 JSON 时,则可以借助 JSONEncoder 编码器来实现。

这个转换相对来说就比较简单了,这里就举个简单的例子吧!

let song = Song(type: "popular", name: "Something Just Like This") if let jsonData = try? JSONEncoder().encode(song){ if let jsonString = String.init(data: jsonData, encoding: String.Encoding.utf8){ print("jsonString:" + "\(jsonString)") } } 输出结果

jsonString:{"type":"popular","name":"Something Just Like This"} 数据模型转 JSON 就完成了,So Easy

结语

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发交流群:130 595 548,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)

到这里本篇文章就结束了,首先非常感谢大家能耐着性子看到这里,说实话我在准备这篇文章的时候也有点痛苦,越写越无聊,时常在写的过程中脑子一直在想:这么无聊的内容连我自己都写不下去了,会有读者愿意看吗?但是开弓没有回头箭,毕竟我也花了几天时间准备了素材,所以还是耐着寂寞写完了,内容过于枯燥,希望大家别嫌弃。

有关Codable发布我就不学,摸鱼爽歪歪!的更多相关文章

  1. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  2. ruby-on-rails - 如何在发布新的 Ruby 或 Rails 版本时收到通知? - 2

    有人知道在发布新版本的Ruby和Rails时收到电子邮件的方法吗?他们有邮件列表,RubyonRails有一个推特,但我不想听到那些随之而来的喧嚣,我只想知道什么时候发布新版本,尤其是那些有安全修复的版本。 最佳答案 从therailsblog获取提要.http://weblog.rubyonrails.org/feed/atom.xml 关于ruby-on-rails-如何在发布新的Ruby或Rails版本时收到通知?,我们在StackOverflow上找到一个类似的问题:

  3. ruby-on-rails - 获取 ActionController::RoutingError(当尝试使用 AngularJS 将数据发布到 Rails 服务器时,没有路由匹配 [OPTIONS] "/users" - 2

    尝试从我的AngularJS端将数据发布到Rails服务器时出现问题。服务器错误:ActionController::RoutingError(Noroutematches[OPTIONS]"/users"):actionpack(4.1.9)lib/action_dispatch/middleware/debug_exceptions.rb:21:in`call'actionpack(4.1.9)lib/action_dispatch/middleware/show_exceptions.rb:30:in`call'railties(4.1.9)lib/rails/rack/logg

  4. iNFTnews | 周杰伦18年前未发布的作品Demo,藏在了区块链技术里 - 2

    当音乐碰上区块链技术,会擦出怎样的火花?或许周杰伦已经给了我们答案。8月29日下午,B站独家首发周杰伦限定珍藏Demo独家访谈VCR,周杰伦在VCR里分享了《晴天》《青花瓷》《搁浅》《爱在西元前》四首经典歌曲Demo背后的创作故事,并首次公布18年前未发布的神秘作品《纽约地铁》的Demo。在VCR中,方文山和杰威尔音乐提及到“多亏了区块链技术,现在我们可以将这些Demos,变成独一无二具有收藏价值的艺术品,这些Demos可以在薄盒(国内数藏平台)上听到。”如何将音乐与区块链技术相结合,薄盒方面称:“薄盒作为区块链技术服务方,打破传统对于区块链技术只能作为数字收藏的理解。聚焦于区块链技术赋能,在

  5. ruby - 如何在 Ruby 中从内存中 HTTP 发布流数据? - 2

    我想上传我在运行时用Ruby生成的数据,就像从block中提供上传数据一样。我找到的所有示例仅展示了如何流式传输必须在请求之前位于磁盘上的文件,但我不想缓冲该文件。除了滚动我自己的套接字连接之外,最好的解决方案是什么?这是一个伪代码示例:post_stream('127.0.0.1','/stream/')do|body|generate_xmldo|segment|body 最佳答案 有效的代码。require'thread'require'net/http'require'base64'require'openssl'class

  6. IDEA 2023.1 正式发布,新特性简介 - 2

     昨晚看到IDEA官推宣布IntelliJIDEA2023.1正式发布了。简单看了一下,发现这次的新版本包含了许多改进,进一步优化了用户体验,提高了便捷性。至于是否升级最新版本完全是个人意愿,如果觉得新版本没有让自己感兴趣的改进,完全就不用升级,影响不大。软件的版本迭代非常正常,正确看待即可,不持续改进就会慢慢被淘汰!根据官方介绍:IntelliJIDEA2023.1针对新的用户界面进行了大量重构,这些改进都是基于收到的宝贵反馈而实现的。官方还实施了性能增强措施,使得Maven导入更快,并且在打开项目时IDE功能更早地可用。由于后台提交检查,新版本提供了简化的提交流程。IntelliJIDEA

  7. Unity数据可视化图表插件XCharts3.0发布 - 2

    Unity数据可视化图表插件XCharts3.0发布历时8个多月,业余时间,断断续续,XCharts3.0总算发布了。如果要打个满意度,我给3.0版本来个80分。对于代码框架结构设计的调整改动,基本符合预期,甚是满意。相比之前的1.0和2.0版本,我认为3.0才是一个拿得出手给广大开发者使用的版本。1.0发布的时候,很兴奋,从0.1到1.0,也磨了一年,真的等不及想给大家试用了,还特地写过一篇文章以示庆祝。那个时候,1.0虽然还还不够完善,功能也不够丰富,但它是XCharts的开始,没有1.0,也就没有后面的2.0和3.0。后面的2.0发布,做了很多改进和优化,随着版本迭代,慢慢的发现有不少硬

  8. ruby-on-rails - 在 Rails 中是否有比 Observers 更直接的方式来执行发布/订阅模式? - 2

    我有一个模型依赖于一个单独的、联合的模型。classMagazine图像是多态的,可以附加到许多对象(页面和文章),而不仅仅是杂志。杂志需要在相关图像发生任何变化时自行更新该杂志还保存了一张自己的截图,可用于宣传:classMagazine现在如果图像发生变化,杂志也需要更新其截图。所以杂志真的需要知道图片什么时候出了问题。所以我们可以天真地直接从封面图片触发屏幕截图更新classImage...但是图片不应该代表杂志做事然而,图片可以用于许多不同的对象,实际上不应该对杂志进行特定的操作,因为这不是图片的责任。该图像也可能附加到页面或文章,并且不需要为它们做各种事情。“正常”的rail

  9. ruby - 我怎样才能将这段代码转换为元编程,这样我就可以停止复制它了? - 2

    我有一个用于building.netsystemswithruby/rake的小但不断增长的框架,我已经研究了一段时间了。在此代码库中,我有以下内容:require'rake/tasklib'defassemblyinfo(name=:assemblyinfo,*args,&block)Albacore::AssemblyInfoTask.new(name,*args,&block)endmoduleAlbacoreclassAssemblyInfoTask此代码遵循的模式在框架中重复了大约20次。每个版本的区别在于正在创建/调用的类的名称(而不是AssemblyInfoTask,它可

  10. 《ChatGPT实用指南》(精编版)重磅发布,全网免费分享,快上车,别掉队 - 2

    文/高扬(微信公众号:量子论)据上次3月18号发布的V1.8版,已经过去十天,这期间AI领域发生了很多重大变化。因此,我们对《ChatGPT实用指南》进行了重大改版,增加了大量实用的操作和详细的讲解,保证小白可以轻松上手,快速驾驭ChatGPT。V2.0版本亮点:1、结构更合理。分为基础篇、进阶篇、高级篇,从易到难,由浅入深,符合学习规律。2、内容更充实。扩充了27页的内容,尽量看图说话,将操作步骤一步步地展示出来。3、排版更美观。按图书出版的规范制作,便于知识点查阅。后记:2022年11月底,我们在HackerNews上看到了关于ChatGPT的新闻报道后,开始意识到,人工智能的春天来了,这

随机推荐