Swift中如何正确创建多维数组?解决下标越界崩溃问题
你的问题核心在于多维数组的初始化方式不对,导致你直接访问下标时数组里根本没有对应的元素,自然会触发越界崩溃。
为什么你的代码会崩溃?
你初始化数组的代码是:
var multiArray = [[Tile]]()
这是一个空的二维数组——它里面没有任何子数组,也没有任何Tile元素。当你尝试执行multiArray[xItem][yItem] = tileView时,首先multiArray[xItem]就会失败:因为multiArray是空的,不存在索引为xItem的子数组,直接访问就会抛出index out of range错误。
两种可行的解决方案
方案1:先初始化固定大小的二维数组(推荐)
如果你已经确定数组是4x4的,可以直接初始化一个包含4个子数组、每个子数组有4个元素的二维数组。因为你是后续逐个赋值,建议用可选类型来初始化(避免必须提供Tile的默认初始化方法):
// 初始化4x4的可选Tile数组,初始值为nil var multiArray: [[Tile?]] = Array(repeating: Array(repeating: nil, count: 4), count: 4) var gb = gameBoard.frame.width/4 for xItem in 0...3 { for yItem in 0...3 { tileView = Tile(frame: CGRect(x: CGFloat(xItem) * gb, y: CGFloat(yItem) * gb, width: gb, height: gb)) gameBoard.addSubview(tileView) // 直接给对应下标赋值 multiArray[xItem][yItem] = tileView } }
如果你的Tile类有默认初始化方法,也可以用非可选类型初始化,但要注意:如果Tile是类(引用类型),用Array(repeating:)初始化时,所有元素会共享同一个实例!这种情况不适合你的场景,因为你需要每个位置都是独立的Tile实例,所以更推荐用可选类型的初始化方式。
方案2:循环中动态构建数组
如果你不确定数组大小,或者更习惯动态添加,可以在x轴循环中先给multiArray添加空的子数组,再往子数组里添加元素:
var multiArray = [[Tile]]() var gb = gameBoard.frame.width/4 for xItem in 0...3 { // 先添加一个空的子数组,对应当前x行 multiArray.append([]) for yItem in 0...3 { tileView = Tile(frame: CGRect(x: CGFloat(xItem) * gb, y: CGFloat(yItem) * gb, width: gb, height: gb)) gameBoard.addSubview(tileView) // 将tileView添加到当前x行的子数组中 multiArray[xItem].append(tileView) } }
这种方式下,multiArray会逐步被填充,不会出现下标越界的问题。
关于你看到的Stack Overflow初始化方法
你提到的Array(count:N, repeatedValue:...)是Swift 2及更早版本的语法,在Swift 3+已经改成了Array(repeating:count:),比如:
var array: [[Int]] = Array(repeating: Array(repeating: 0, count: N), count: N)
这种方式适合值类型(比如Int)的多维数组初始化,因为值类型会被复制,每个元素都是独立的。但对于引用类型(比如你的Tile类),这种方式会导致所有位置共享同一个实例,所以不适合你的场景。
内容的提问来源于stack exchange,提问作者tore

