在Swift对象中未正确引用随机数

时间:2018-04-24 21:21:42

标签: ios swift swift4

我有一个对象数组,其中每个对象都有一个练习名称和一个随机数量的代表。

然后我有一个生成随机锻炼的功能(其中有3到6个练习)

然而,当我打印它时,代表几乎总是1,2或偶尔14,尽管加载它30次左右。

我在这里做错了吗?

这是我的对象和结构:

struct exerciseInWorkout {

    let name : String
    let reps : Int

}

let exerciseBankArray = [

    exerciseInWorkout(name: "Squat", reps: (Int(arc4random_uniform(10)))),
    exerciseInWorkout(name: "Push Ups", reps: (Int(arc4random_uniform(5)))),
    exerciseInWorkout(name: "Viking Press", reps: (Int(arc4random_uniform(20)))),

]

这是我的功能:

func generateWorkout(){

        let possibleExercises = exerciseBankArray
        let numberOfExercisesKey = Int(arc4random_uniform(4) + 3)

let workoutSet : [exerciseInWorkout] = (1...numberOfExercisesKey).map { _ in

let randomKey = Int(arc4random_uniform(UInt32(possibleExercises.count)))
            return exerciseInWorkout(name: exerciseBankArray[randomKey].name, reps: exerciseBankArray[randomKey].reps)}
        print (workoutSet)


        }

    }

另外,有没有办法创建一组这些以避免两次相同的练习?我尝试使用Set,但似乎根本没用。

最后,当我打印它时,每个对象前面加上“project.exerciseInWorkout ...有没有办法只打印/返回干净的数组,即[[”name:“press ups”,reps:12], [姓名:xyz,代表:30]]?

原因是我想将它传递给一个新的VC,然后放入一个表视图,并假设我需要一个干净的数组来做到这一点。

1 个答案:

答案 0 :(得分:3)

简答

您的数组exerciseBankArray似乎全局存储,这意味着exerciseInWorkout组件会为整个应用初始化一次。话虽如此,然后正常的是,rep的数量总是相同的,arc4random_uniform仅在第一次访问时执行。

快速修复

如果你想保持相同的结构,我推荐这个

  1. arc4random_uniform移除exerciseBankArray,然后写下您想要的最大代表数量

    let exerciseBankArray = [
    
        exerciseInWorkout(name: "Squat", maxReps: 10),
        exerciseInWorkout(name: "Push Ups", maxReps: 5),
        exerciseInWorkout(name: "Viking Press", maxReps:  20)
    
    ]
    
  2. 像{/ p>一样调用generateWorkout()内的随机函数

    func generateWorkout(){
    
        let possibleExercises = exerciseBankArray
        let numberOfExercisesKey = Int(arc4random_uniform(4) + 3)
    
        let workoutSet : [exerciseInWorkout] = (1...numberOfExercisesKey).map { _ in
    
            let randomKey = Int(arc4random_uniform(UInt32(possibleExercises.count)))
            return exerciseInWorkout(
                name: exerciseBankArray[randomKey].name, 
                reps: Int(arc4random_uniform(exerciseBankArray[randomKey].maxReps))
             )
        } 
    }
    
  3. 更长的改进

    如果您愿意为代码制作更好的架构,请参阅以下建议

    1. 删除全局变量
    2. 将锻炼模型拆分为两个类/结构:

      • 代表实际练习的

        struct WorkoutExercise {
            let name: String
            let reps: Int
        }
        
      • 代表锻炼发生器的

        struct WorkoutExerciseGenerator {
            let name: String
            let maxReps: Int
            // Add any other parameter you need to generate a good exercise 
            func generate() -> WorkoutExercise {
                return WorkoutExercise(
                    name: name,
                    reps: Int(arc4random_uniform(maxReps))
                )
            }
        }
        
    3. 更多改进/ Q& A

        

      当你说删除全局变量时,你的意思是在每个需要它们的VC中存储练习数组吗?我只是认为那会“重复自己”(从DRY原则等?)

      我完全同意DRY指南,但有很多方法可以不重复。全局变量(一个不在任何类中,只是自由浮动的变量)的问题很多:

      1. 当你想把它包含在不同的目标中时会很尴尬
      2. 它不是任何命名空间的一部分,所以它可能会从另一个文件库中重载另一个,它会扰乱自动完成等...
      3. 等...您可以在this thread
      4. 中找到更多文档
          

        另外,如果我改为上面的第二个例子,那我怎么称呼呢?   适量的那些?只需将“return exerciseInWorkout”替换为   新功能?或者它会保持不变,因为功能是   包含在结构中?

        所以我理解正确,你真正想要创建的是一组默认生成器,用于具有名称和最大重复次数的练习,这些应该在整个项目中可用(因此你使用全局变量的原因)

        改进此代码的一个好方法是定义静态生成器,例如,您可以将WorkoutExerciseGenerator更新为

                struct WorkoutExerciseGenerator {
        
                    let name: String
                    let maxReps: Int
                    // Add any other parameter you need to generate a good exercise 
        
                    func generate() -> WorkoutExercise {
                        return WorkoutExercise(
                            name: name,
                            reps: Int(arc4random_uniform(maxReps))
                        )
                    }
        
                    // Generates a "Squat" workout
                    static var squat {
                         return WorkoutExerciseGenerator(name: "Squat", maxReps: 10)
                    }
        
                    // Generates a "Push Up" workout
                    static var pushUp {
                         return WorkoutExerciseGenerator(name: "Push Ups", maxReps: 5)
                    }
        
                    // Generates a "Viking Press" workout
                    static var vikingPress {
                         return WorkoutExerciseGenerator(name: "Viking Press", maxReps: 20)
                    }
                }
        

        现在您已拥有这些特定的生成器,看起来您还希望有一种方法来生成整个锻炼。如果是这种情况,那么除了我所写的内容之外,您还可以简单地创建一些表示锻炼和锻炼发生器的对象。

        /// This represents a whole workout that contains 
        /// multiple exercises
        struct Workout {
        
            let exercises: [WorkoutExercise]
        
        }
        
        /// This allows to dynamically creates a Workout
        struct WorkoutGenerator {
        
            // This is the "pool" of exercises from
            // which it generates a workout (similar to your
            // `exerciseBankArray`)
            let exercisePool: [ExerciseGenerators]
        
            // Min and max amount of workouts
            let minCount: Int
            let maxCount: Int
        
            // Generates a workout from the generator
            func generate() -> WorkoutGenerator {
        
                let amount = Int(arc4random_uniform(maxCount - minCount)) + minCount
                let exercises = (0..<amount).map { _ in
                   // Selects an exercise generator at random
                   let index = Int(arc4random_uniform(exercisePool.count))
                   // Generates a random workout exercise from this generator
                   return exercisePool[index].generate()
                }
        
                return Workout(exercises: exercises)
            }
        
            // Same thing here, you can use a static variable to create
            // a "default" workout generator that contains the exercises
            // you had inside your `exerciseBankArray`
            static var default: WorkoutGenerator {
                return WorkoutGenerator(
                    exercisePool: [.squat, .pushUp, .vikingPress],
                    minCount: 3,
                    maxCount: 6
                )
            }
        }
        

        现在您拥有所有这些,根据您的要求创建完全随机的锻炼,您唯一需要做的就是

        let myWorkout = WorkoutGenerator.default.generate()
        

        如果你想添加更多的运动类型,只需创建更多的静态ExerciseGenerator,如果你想创建不同类型的运动(可能有不同的运动池,有些很难或有些容易),只需创建额外的静态WorkoutGenerator。 (请注意,您不需要静态,您也可以直接在VC中创建对象)。

        希望有所帮助!