2021-09-18

MemorizeApp.swift

import SwiftUI

@main
struct MemorizeApp: App {
    let game = EmojiMemoryGame()
    
    var body: some Scene {
        WindowGroup {
            ContentView(viewModel: game)
        }
    }
}
//struct MemorizeApp: App {
//    //用let,因为EmojiMemoryGame()是个class,这里是一个引用类型,game就是一个指针,let game意思是我不会改变这个指针,但他指向哪我是会改变的,游戏会变,但路径不变。
//    //viewmodel EmojiMemoryGame可以通过指针更改
//    //创建一个EmojiMemoryGame()开闭括号因为类获得了免费的init,EmojiMemoryGame是个class,调用它获得一个什么都不做的init,里面只有一个变量获取免费的init。
//    //我们正在用那个免费的init来创建表情符号游戏
//    //MARK:3,搞个viewmodel,为的是给下面的ContentView的变量viewModel赋值
//    let game = EmojiMemoryGame()
//    var body: some Scene {
//        WindowGroup {
//            //给ContentView的变量viewModel赋值
//            //把名字为game的viewModel传给ContentView,viewModel的类型是EmojiMemoryGame,EmojiMemoryGame是个class,是引用类型的,传递指针
//            ContentView(viewModel: game)
//        }
//    }
//}

ContentView.swift

import SwiftUI


struct ContentView: View {
    @ObservedObject var viewModel: EmojiMemoryGame
    @State var emojiCount = 4
    
    var body: some View {
        VStack {
            ScrollView {
                LazyVGrid(columns: [GridItem(.adaptive(minimum: 65))]){
                    ForEach(viewModel.cards) { card in
                        CardView(card: card)
                            .aspectRatio(2/3, contentMode: .fit)
                            .onTapGesture {
                                viewModel.choose(card)
                            }
                    }
                }
            }
            .foregroundColor(.red)
            Spacer()
            
        }
        .padding(.horizontal)
        
    }
}

struct CardView: View {
    let card: MemoryGame<String>.Card
    
    var body: some View {
        ZStack {
            let shape = RoundedRectangle(cornerRadius: 20)
            if card.isFaceUp {
                shape.fill().foregroundColor(.white)
                shape.strokeBorder(lineWidth: 3)
                Text(card.content).font(.largeTitle)
                
            } else {
                shape.fill()
            }
        }
    }
}








struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        let game = EmojiMemoryGame()
        ContentView(viewModel: game)
            .preferredColorScheme(/*@START_MENU_TOKEN@*/.dark/*@END_MENU_TOKEN@*/)
        ContentView(viewModel: game)
            .preferredColorScheme(.light)
    }
}


//struct ContentView: View {
//    var emojis = ["🚙", "🚗", "⛱", "🎡", "🪝", "🗿", "🛖", "⏱", "☎️", "🎰","🚜","🛴", "✈️"]
//    @State var emojiCount = 4
//
//    var body: some View {
//        VStack {
//            ScrollView {
//                LazyVGrid(columns: [GridItem(.adaptive(minimum: 65))]){
//                    ForEach(emojis[0..<emojiCount], id: \.self) { emoji in
//                        CardView(content: emoji).aspectRatio(2/3, contentMode: .fit)
//                    }
//                }
//            }
//            .foregroundColor(.red)
//            Spacer()
//            HStack {
//                remove
//                Spacer()
//                add
//            }
//            .font(.largeTitle)
//            .padding(.horizontal)
//        }
//        .padding(.horizontal)
//
//    }
//    var remove: some View {
//        Button(action:{
//            if emojiCount > 1{
//                emojiCount -= 1
//            }
//        }, label: {
//            Image(systemName: "minus.circle")
//        })
//    }
//    var add: some View {
//        Button(action:{
//            if emojiCount < emojis.count {
//                emojiCount += 1
//            }
//        }, label: {
//            Image(systemName: "plus.circle")
//        })
//    }
//}
//
//struct CardView: View {
//    var content: String
//    @State var isFaceUp: Bool = true
//
//    var body: some View {
//        ZStack {
//            let shape = RoundedRectangle(cornerRadius: 20)
//            if isFaceUp {
//                shape.fill().foregroundColor(.white)
//                shape.strokeBorder(lineWidth: 3)
//                Text(content).font(.largeTitle)
//
//            } else {
//                shape.fill()
//            }
//        }
//        .onTapGesture {
//            isFaceUp = !isFaceUp
//        }
//    }
//}


//struct ContentView: View {
//    //这是用viewModel浏览卡片
//    //MARK:26,@ObservedObject意思是当viewModel有变化时,重建整个body
//    //MARK: 1,把View和viewModel链接:ContentView必须能看到model,它才能绘制model的UI
//    //MARK: 2,因为ContentView多了一个变量viewModel,需要把用到ContentView的地方把变量viewModel都赋值
//    //MARK: 8,把var改成let
//    @ObservedObject var viewModel: EmojiMemoryGame
//    //var viewModel: EmojiMemoryGame, ContentView通过EmojiMemoryGame来显示,现在只用让var body工作来展示
//    //var声明变量,body是变量的名字,some View是变量的类型.{}内的是个没有名字的函数,隐藏了return,返回一个Text
//    //content 最后一个参数,可以从括号里删除,{}里的是ZStack的content参数
//    //用ForEach创造数组中每个事物的CardView,content是我们的CardView,ForEach需要数组emojis,有content这个内容参数,在hstack中的视图组合器,ForEach本身不是视图组合器,是乐高制造商,他让一袋乐高装满了东西。提供为数组中的每个东西做点事情。
//    //给函数{}加参数{参数名1, 参数名2, in};CardView(content: emoji)为每个CardView指定emoji;
//    //id: \.self 用结构体本身做唯一标识符
////    var emojis = ["🚝","🏍","🚖","🚀","🚕","🚎","🚑","🚜","🦽","🚔","🛸","🚂","🚞","🚨"]
////
////    @State var emojiCount = 14
//
//    //var body:给我个uI让我看看model里有什么
//    var body: some View {
//
//            ScrollView{
//                //LazyVGrid会制作许多列,不能放数字,要放GridItem数组,GridItem可以控制列,GridItem(.fixed(200))第一个列有200宽度
//                //LazyVGrid水平使用所有宽度,垂直尽可能小以便多放东西。LazyVGrid的Lazy意思是不滚动不加载
//                //.adaptive(minimum: 65)最小宽度65
//                LazyVGrid(columns: [GridItem(.adaptive(minimum: 65))]){
//                    //让ForEach认为string是可识别的,id: \.self
//                    //emoji是这个函数的参数,用in将它分开,从执行的代码返回CardView
//
//                    //MARK:5,因为有了viewmodel,所以视图要现实viewmodel中的东西
//                    //ForEach(emojis[0..<emojiCount], id: \.self){ emoji in
//                    //出错是因为cards不可识别?卡片数组必须Identifiable
//
//                    //如果viewModel中是private(set) var model,下面的var cards:注释掉没有,则ForEach(viewModel.model.cards)
//                    //MARK:6,把emoji in 改成card in
//                    ForEach(viewModel.cards){ card in
//
//                        //用ForEach遍历viewModel中的所有卡片,cards是viewmodel中的var cards: Array<MemoryGame<String>.Card>,model.cards是个数组。ForEach遍历出每一张卡片,要给它做个CardView视图展示
//                        //人们创建struct时会设置值
//                        //MARK:9,CardView(content: emoji)改为CardView(card: card)
//                        //CardView是单个卡片视图
//                        CardView(card: card)
//                            .aspectRatio(2/3, contentMode: .fit)
//
//                            //MARK: - intent(s)添加注释
//                            //intent都记录在ViewModel中,通过它来表达用户意图
//                            //MARK:12, .onTapGesture 给卡片添加翻面功能
//                            .onTapGesture {
//                                viewModel.choose(card)
//                            }
//                    }
//                }
//            }
//
//            .foregroundColor(.red)
//            //Spacer占有其他人不要的所有空间
////            Spacer()
////            HStack{
////                add
////                Spacer()
////                remove
////            }
////            .font(.largeTitle)
////            .padding(.horizontal)
//
//
//        .padding(.horizontal)
//
//    }
//
////加减卡片
////    var remove: some View {
////        Button {
////            if emojiCount > 1 {
////                emojiCount -= 1
////            }
////        } label: {
////           Image(systemName: "minus.circle")
////        }
////    }
////    var add: some View {
////        Button {
////            if emojiCount < emojis.count{
////                emojiCount += 1
////            }
////        } label: {
////            Image(systemName: "plus.circle")
////
////        }
////    }
//
//
//}
//
////CardView单个卡片视图,@State重建视图,指针不变;content是卡片的内容
////CardView正在显示这张卡片,为这张卡构建一个UI
//struct CardView: View {
//    //MARK:7,在这个CardView创建card并且为他建立一个body,只传一张卡
//    //MARK:7-1,强烈推荐:这里没有将整个model传给他,只传model的一部分,当你构建一个view时,只传人他完成工作所需的最低限度的model,CardView的工作是为我构建一个显示卡片的UI在model中,只把卡传给他。这里用let,因为卡片视图不会改变
//    //@State在视图中非常罕见,所以你视图中不会有很多变量。
//    let card: MemoryGame<String>.Card
//    //struct中变量可以没有初始值
////    var content: String
////   @State var isFaceUp: Bool = true
//
//    var body: some View {
//        ZStack{
//            let shape = RoundedRectangle(cornerRadius: 20)
//            //MARK:10,if isFaceUp 改成if card.isFaceUp,Text(card.content)
//            if card.isFaceUp {
//                //stroke边缘 fill默认填充
//                shape.fill().foregroundColor(/*@START_MENU_TOKEN@*/.white/*@END_MENU_TOKEN@*/)
//                //strokeBorder笔划边框
//                shape.strokeBorder(lineWidth: 3)
//                Text(card.content).font(.largeTitle)
//            } else {
//                shape.fill()
//            }
//
//        }
////反转卡片
////        .onTapGesture {
////            isFaceUp = !isFaceUp
////        }
//
//    }
//}
//
//struct ContentView_Previews: PreviewProvider {
//    static var previews: some View {
//        //MARK:4,给用到ContentView地方的变量赋值,game是个指针,创造一个游戏,把它传递给viewModel的明暗两个视图
//        let game = EmojiMemoryGame()
//        //ContentView()
//        ContentView(viewModel: game)
//            .preferredColorScheme(.dark)
//        ContentView(viewModel: game)
//            .preferredColorScheme(.light)
//
//
//
//    }
//}

MemoryGame.swift

//Foundation是个数组,字符串,字典组成的基础包,不是按钮或视图api
//前变量后值或类型
import Foundation

struct MemoryGame<Content> {
    private(set) var cards: Array<Card>
    
    mutating func choose(_ card: Card){
        let chosenIndex = index(of: card)
        cards[chosenIndex].isFaceUp.toggle()
 //print("chosenCard = \(chosenCard)")
        print("\(cards)")
    }
    
    func index(of card: Card) -> Int {
        for index in 0..<cards.count {
            if cards[index].id == card.id {
            return index
            }
        }
        return 0
    }
    
    init(numberOfPairOfCards: Int,createCardContent:(Int) ->Content ) {
        cards = Array<Card>()
        for pairIndex in 0..<numberOfPairOfCards{
            let content: Content = createCardContent(pairIndex)
            cards.append(Card(content: content, id: pairIndex*2))
            cards.append(Card(content: content, id: pairIndex*2+1))
        }
    }
    
    struct Card: Identifiable {
        var isFaceUp: Bool = true
        var isMatched: Bool = false
        var content: Content
        var id: Int
    }
}
//
//struct MemoryGame<CardContent> {
//    private(set) var cards: Array<Card>
//
//    //MARK:19 mutating放在func前面告诉全世界调用这个函数将改变整个struct,所以这个函数只能在MemoryGame作为变量var的地方调用
//    //MARK:21,所以说这个函数可变,可以改变整个struct
//    mutating func choose(_ card: Card) {
//
//        //card.isFaceUp.toggle()//函数里的参数是常数,会报错
//        //MARK:13,编写一个名为index(of: )的函数,这张卡片的索引的函数,在array中找到这张卡片
//        let chosenIndex = index(of: card)
//        //MARK:15,
////        var chosenCard = cards[chosenIndex]
////        chosenCard.isFaceUp.toggle()
//        //MARK:18
//        cards[chosenIndex].isFaceUp.toggle()
//        //MARK:16,
//        //print("chosenCard = \(chosenCard)")
//        print("\(cards)")
//    }
//
//    //MARK:14, 实现上面的函数index(of: card),卡片的索引,这是该数组的索引
//    func index(of card: Card) -> Int {
//        // MARK:17,如果卡片索引的ID==卡片ID,返回int类型的index
//        for index in 0..<cards.count {
//            if cards[index].id == card.id {
//                return index
//            }
//        }
//        return 0 //bogus!
//    }
//
//    init(numberOfPairOfCards: Int, creatCardContent:(Int) -> CardContent) {
//        cards = Array<Card>()
//        for pairIndex in 0..<numberOfPairOfCards{
//            let content: CardContent = creatCardContent(pairIndex)
//            //MARK:11,给卡片组加ID参数
//            cards.append(Card(content: content, id: pairIndex*2))
//            cards.append(Card(content: content, id: pairIndex*2+1))
//        }
//    }
//
//    //MARK:10-1,让卡片可识别Identifiable
//    struct Card:Identifiable {
//        var isFaceUp: Bool = false
//        var isMatched: Bool = false
//        var content: CardContent
//        var id: Int
//    }
//
//}
//----------------------------------------------------------

















//struct MemoryGame<CardContent> {
//    private(set) var cards: Array<Card>
//
//    //mutating 这个函数可以改变整个struct
//    mutating func choose(_ card: Card){
//        //card.isFaceUp.toggle()
//        //编写一个index(of:)的函数
//        let chosenIndex = index(of: card)
//        cards[chosenIndex].isFaceUp.toggle()
//        print("\(cards)")
//    }
//
//    func index(of card: Card) -> Int {
//        for index in 0..<cards.count {
//            if cards[index].id == card.id {
//                return index
//            }
//        }
//        return 0
//    }
//
//    init(numberOfPairOfCards: Int, createCardContent:(Int) ->CardContent) {
//        cards = Array<MemoryGame.Card>()
//        for pairIndex in 0..<numberOfPairOfCards{
//
//            let content: CardContent = createCardContent(pairIndex)
//            cards.append(Card(content: content,id: pairIndex*2))
//            cards.append(Card(content: content,id: pairIndex*2+1))
//        }
//    }
//
//    struct Card: Identifiable {
//        var isFaceUp: Bool = true
//        var isMatched: Bool = false
//        var content: CardContent
//        var id: Int
//    }
//}
//
//
//





////model是个简单的MemoryGame模型
////我们使用范型<CardContent> 必须向全世界宣布,放在<>里,
////当有人用我们的MemoryGame时,他们将不得不告诉我们这个范型是什么,不能一直don’t care,我们不关心,但谁用谁关心。我们做表情游戏时,它将成为一个String
//struct MemoryGame<CardContent> {
//    //有卡片:放在数组里<卡片阵列>
//    //Model保护其卡片免受ViewModel的影响,只看不动
//    //这个cards:Array<Card>在使用前要先用下面的init进行初始化
//    //var cards: Array<Card> 由Card类型组成的数组
//    private(set) var cards: Array<Card>
//
//    //有选择卡片
//    func choose(_ card: Card) {
//
//    }
//
//    //init初始化未设置的变量,numberOfPairsOfCards初始化的时候你得知道有几对卡,一共有10对卡,创建第一对卡的内容,返回第一对卡的内容。
//    init(numberOfPairsOfCards: Int, createCardContent: (Int) -> CardContent) {
//        //创建空的<卡片阵列>,初始化了一个空的卡片数组,所以上面能用
//        cards = Array<Card>()
//        //add numberOfPairsOfCards x 2 cards to cards array,卡的对数乘以2就是有多少卡,(for 第几对 in 对数)
//        for pairIndex in 0..<numberOfPairsOfCards {
//            //制作一对卡,cards.append(Card(isFaceUp: false, isMatched: false, content: <#T##CardContent#>)),因为struct Card中有默认值,所以isFaceUp: false, isMatched: false不需要了
//            //创建一个函数createCardContent,把pairIndex传给它,createCardContent这个函数从哪来?
//            //let content: CardContent = createCardContent(pairIndex) //类型推断,干掉CardContent
//            //let 卡的内容 = 创建卡的内容(第几对)
//            //var content: CardContent = ... 创建一个变量 它是CardContent类型的。让createCardContent这个函数来创建卡片内容
//            //createCardContent: (Int) -> CardContent 传入要创建第几对卡的数组,比如1,返回第一对卡的内容,把函数直接写到参数里。
//            let content = createCardContent(pairIndex)
//            //给每张卡设置内容。(content: CardContent),CardContent是个范型,不确定它是什么类型,(content: content)前面是参数,后面值。
//            cards.append(Card(content: content))
//            cards.append(Card(content: content))
//        }
//    }
//
//    //MemoryGame.Card,卡片有哪些属性
//    struct Card {
//        var isFaceUp: Bool = false //是否正面朝上
//        var isMatched: Bool = false //是否匹配
//        var content: CardContent //卡片上有什么,String?可以做的更好,未来有任意图像而不是表情符号;CardContent范性--随便啥:don't care
//    }
//}

EmojiMemoryGame.swift

// #ViewModel

//虽然ViewModel是我们UI的一部分,但它不是swiftUI视图,是用户界面的一部分
//ViewModel是个中介,在Model和View之间;它需要和Model建立连接,实际上ViewModel会重建自己的Model。有时候Model是数据库
//你玩游戏它就在那里,如果ViewModel消失了,然后游戏结束了。
//因为很多时候ViewModel创建自己的Model,我们会经常说ViewModel是程序中的truth。Model是app的truth,如果ViewModel在创建自己的模型,则本质上ViewModel是truth
//添加三个关键字让我们的UI变成完全反应式的UI,我们model的任何更改都会更新匹配UI
//ObservableObject,viewmodel向世界发布something changed,model变化了,view知道viewmodel改变时重绘,@ObservedObject,

import SwiftUI


class EmojiMemoryGame: ObservableObject {
    static var emojis = ["🚝","🏍","🚖","🚀","🚕","🚎","🚑","🚜","🦽","🚔","🛸","🚂","🚞","🚨"]
    
    static func makeMemoryGame() -> MemoryGame<String> {
        MemoryGame<String>(numberOfPairOfCards: 4) { pairIndex in emojis[pairIndex]}
    }
    @Published private var model: MemoryGame<String> = makeMemoryGame()
    
    var cards: Array<MemoryGame<String>.Card> {
        return model.cards
    }
    
    //MARK: - Intent(s)
    
    func choose(_ card: MemoryGame<String>.Card) {
        model.choose(card)
    }
}

//MARK:22,ObservableObject 向全世界发布“有所改变”
//class EmojiMemoryGame: ObservableObject {
//    static var emojis = ["🚝","🏍","🚖","🚀","🚕","🚎","🚑","🚜","🦽","🚔","🛸","🚂","🚞","🚨"]
//
//    static func makeMemoryGame() -> MemoryGame<String> {
//        MemoryGame<String>(numberOfPairOfCards: 4 ){ pairIndex in emojis[pairIndex]}
//    }
//
//    //MARK:24,@Published 无论model发生任何变化,它都会执行objectWillChange.send()
//    //倾向于让model完全私有,通过var和func将其暴露给view
//    //MARK:20,如果这里为let model 则下面model.choose(card)就不能用,这就是swift的写时复制的方式,它强制事物的可变性
//    @Published private var model: MemoryGame<String> = makeMemoryGame()
//
//    var cards: Array<MemoryGame<String>.Card> {
//        model.cards
//    }
//
//    //MARK:12-1,实现viewModel.choose(card)
//    func choose(_ card: MemoryGame<String>.Card) {
//        //MARK:23,向全世界发布objectWillChange 在model前面加上@Published
//        //objectWillChange.send()
//        //13,
//        //MARK:25,choose是mutating func,所以swiftUI会自动执行objectWillChange.send()
//        model.choose(card)
//    }
//}
//-----------------------------------------------------------------


//ObservableObject向全世界宣布有东西改变了!
//class EmojiMemoryGame: ObservableObject {
//    static var emojis = ["🚝","🏍","🚖","🚀","🚕","🚎","🚑","🚜","🦽","🚔","🛸","🚂","🚞","🚨"]
//    
//    static func makeMemoryGame() -> MemoryGame<String> {
//        MemoryGame<String>(numberOfPairOfCards: 4) { pairIndex in emojis[pairIndex] }
//    }
//    
//    //让model完全私密,通过var func暴露给view
//    //在任何变量前加关键字@Published,无论model发生任何变化,它都会自动执行objectWillChange.send()
//    @Published private var model: MemoryGame<String> = makeMemoryGame()
//
//    //viewModel.cards
//    var cards: Array<MemoryGame<String>.Card> {
//        model.cards
//    }
//    
//    // MARK: - Intent(s)
//    //用户的意图,这个Intent是我们必须勾在UI上的东西,我们的UI会表达这个Intent
//    //choose是个mutating func,swiftUI知道这改变了它,会自动执行objectWillChange.send()
//    func choose(_ card: MemoryGame<String>.Card) {
//        //向世界发布这个objectWillChange,model changed
//        //objectWillChange.send()
//        model.choose(card)
//    }
//}

//
//class EmojiMemoryGame {
//
//    let emojis = ["🚝","🏍","🚖","🚀","🚕","🚎","🚑","🚜","🦽","🚔","🛸","🚂","🚞","🚨"]
//
//    private var model: MemoryGame<String> =
//        MemoryGame<String>(numberOfPairsOfCards: 4) { pairIndex in
//            "🚙" }
//
//    var cards: Array<MemoryGame<String>.Card> {
//        return model.cards
//    }
//}

//createCardContent: (Int) -> CardContent,createCardContent: (Int) -> String 接收int返回string
//func makeCardContent(index: Int) -> String {
//    return "🎳"
//}

//类可以继承
//class EmojiMemoryGame {
//
//    //从视图中获取表情符号,static意味着基本上是全局的,emojis相对于EmojiMemoryGame是全局的
//    static let emojis = ["🚝","🏍","🚖","🚀","🚕","🚎","🚑","🚜","🦽","🚔","🛸","🚂","🚞","🚨"]
//
//    //创建记忆游戏 类型函数
//    static func createMemoryGame() -> MemoryGame<String> {
//       return MemoryGame<String>(numberOfPairsOfCards: 4)  { pairIndex in
//        emojis[pairIndex]
//       }
//    }
//
//
//     private let model: MemoryGame<String> = createMemoryGame()
//
//    //我们的模型是个结构体struct,所以我们要创建一个名为model的变量var,它是MemoryGame类型的
//    //因为MemoryGame<CardContent>是范型的,所以我们要指明CardContent是什么类型,这是一个EmojiMemoryGame,所以它是string,表情符号是字符串
//    //ViewModel是Model的看门人,ViewModel保护model免受不良行为的影响,谁有权访问ViewModel;我们的做法是制作私有model
//    //private意味着只有ViewModel代码本身可以看到Model,它保护Model,阻止任何访问修改;private是访问控制的一部分。
//    //有时候完全私有有点太严格了,例如在我们的记忆游戏中人们需要看到这些卡片,否则我们的视图将怎么绘制卡片?
//    //所以还有另外一个private(set)私有,它告诉swift其他类和struct可以查看model,但无法改变它。它不能调用func choose,因为那会改变MemoryGame,最特别是他们无法读取Card并更改isFaceup、isMatched等。
//    //private var model: MemoryGame<String>
//    //class中所以的变量都要有值,MemoryGame<String>(numberOfPairsOfCards: 4)设置默认4张卡

////    private var model: MemoryGame<String> = MemoryGame<String>(numberOfPairsOfCards: 4)  { pairIndex in
////        //pairIndex 第几对表情
////        //{ pairIndex in emojis[pairIndex] } = {99 in 表情数组[99]}
////        //不能使用带有属性初始化的实例成员,属性初始值设定项在“self”可用前运行。
////        //实例成员意味着在EmojiMemoryGame中的任何函数或变量,每当我建立一个EmojiMemoryGame,我会得到一个emojis和一个Model,不能像emojis使用在初始值设定项中,属性初始化器?property这个词只是指类或结构中的变量或常量属性,使用等号的属性。
////        //私有属性只在本文件内可用访问。
////        //model和emojis这两个属性初始化顺序是随机的,你在后面这个函数里emojis[pairIndex]使用它 它可能还没初始化。可以用init来初始化emojis
////        emojis[pairIndex]
////
////    }
//
//
//    //1,model完全私有则必须有自己的cards,model.cards
//    //2,views可以调用ViewModeel自己的var,拿到cards。
//    //Model中的cards是个struct结构体,他们是一组卡片,我们传递struct时我们复制他们
//    var cards: Array<MemoryGame<String>.Card> {
//        return model.cards
//    }
//}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容

  • 今日体验,客户满意度是企业的发展使命。 核心,客户满意,源自于我们的严谨。 用,流程,标准,细致化的服务。
    王海博阅读 102评论 0 0
  • 晚上暂时放弃了换手机的想法,购物车差点付款,理由不充分。
    NemoX阅读 156评论 0 0
  • 微信的第二天 To stop the tears,明天的太阳仍然从东方升起。调整哈心态,周六了,儿子放学回家过中秋...
    3d205c6e1af7阅读 86评论 1 1
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 8,532评论 28 53
  • 首先介绍下自己的背景: 我11年左右入市到现在,也差不多有4年时间,看过一些关于股票投资的书籍,对于巴菲特等股神的...
    瞎投资阅读 5,721评论 3 8