SpriteKit:使用SKTexture(rect:CGRect)导致SKTileMap内存泄漏
问题分析与解决方案
核心问题
通过SKTexture(rect:in:)从terrain.png裁剪纹理、手动生成SKTileSet并创建100×100的SKTileMapNode后,每次切换场景都会出现内存持续增长:内存图显示残留3个无法释放的SKTexture实例,同时Xcode报告Jet和Metal分配泄漏,排查代码未发现不合理引用,疑似SpriteKit内部逻辑导致。
可行修复方向
1. 缓存核心资源,避免重复创建
将基础纹理、裁剪后的瓦片纹理及SKTileSet全局缓存,避免每次场景初始化都重复生成资源:
class TileResourceCache { static let shared = TileResourceCache() let baseTerrainTexture = SKTexture(imageNamed: "terrain") var tileTextures: [SKTexture]! var tileSet: SKTileSet! private init() { // 一次性完成纹理裁剪与瓦片集创建 tileTextures = (0..<9).map { index in let x = CGFloat(index % 3) / 3 let y = CGFloat(index / 3) / 3 let rect = CGRect(x: x, y: y, width: 1/3, height: 1/3) return SKTexture(rect: rect, in: baseTerrainTexture) } let tileGroups = tileTextures.map { SKTileGroup(tileDefinition: SKTileDefinition(texture: $0)) } tileSet = SKTileSet(tileGroups: tileGroups) } }
后续场景直接复用缓存的tileSet创建SKTileMapNode,减少重复资源生成带来的内存隐患。
2. 场景销毁时主动清理关联资源
在场景即将被移除时,强制断开SKTileMapNode的资源关联,帮助ARC回收内存:
override func willMove(from view: SKView) { tileMap.tileSet = nil tileMap.removeFromParent() tileMap = nil // 清理基础纹理的冗余动作 TileResourceCache.shared.baseTerrainTexture.removeAllActions() }
手动切断SpriteKit内部可能存在的隐式引用,降低资源滞留概率。
3. 替换手动裁剪方式,使用SKTextureAtlas
改用SKTextureAtlas加载瓦片图集,替代SKTexture(rect:in:)的手动裁剪逻辑:
let terrainAtlas = SKTextureAtlas(named: "terrain") let tileTextures = terrainAtlas.textureNames.sorted().map { terrainAtlas.textureNamed($0) }
SpriteKit对官方图集的资源管理逻辑更完善,可规避手动裁剪引发的内存泄漏问题。
4. 升级环境验证兼容性
检查当前使用的iOS/macOS、Xcode版本,部分SpriteKit内存泄漏问题已在新版本系统中修复,尝试升级到最新稳定版后重新测试。
内容的提问来源于stack exchange,提问作者sanderFrenken
相关产品推荐
相关产品推荐

