【发布时间】:2021-07-25 05:01:09
【问题描述】:
我正在使用swift/swiftui 将一个数组的结构分配给另一个结构数组的属性。阵列相当小。 figureArray 大约有 4000 条记录,specificsArray 大约有 200 条记录。查找匹配 firstIndex 大约需要 20 秒。即使我注释掉specifics对figureArray的赋值,这个过程也需要20秒,这表明for/forEach中的firstIndex非常慢。
在我的 iPhone 8 上,该进程的内存约为 90M,CPU 达到 ~100%
问题是,我怎样才能让它更快? (快得多 - 即不到 2 秒)。 对我来说,这个过程似乎需要几毫秒的数组大小。
specifics 对象是唯一的,从不重叠,因此可以并行进行设置。我只是不确定如何。
specificsArray.forEach { specific in
// look for a figure
if let indexFigure = figureArray.firstIndex(where: {$0.figureGlobalUniqueId == specific.specificsFirebase.figureGlobalUniqueId}) {
figureArray[indexFigure].specifics = specific
}
}
我也尝试了以下方法。时间几乎相同,大约 20 秒
for indexSpecifics in 0 ..< specificsArray.count {
// look for a figure
if let indexFigure = figureArray.firstIndex(where: {$0.figureGlobalUniqueId == specificsArray[indexSpecifics].specificsFirebase.figureGlobalUniqueId}) {
figureArray[indexFigure].specifics = specificsArray[indexSpecifics]
}
}
具体结构
struct Specifics: Hashable, Codable, Identifiable {
var id: UUID
var specificsFirebase: SpecificsFirebase
var isSet = false
}
struct SpecificsFirebase: Hashable, Codable, CustomStringConvertible {
let seriesUniqueId: String
let figureGlobalUniqueId: String
var loose_haveCount: Int = 0
var loose_sellCount: Int = 0
var loose_wantCount: Int = 0
var new_haveCount: Int = 0
var new_orderCount: Int = 0
var new_orderText: String = ""
var new_sellCount: Int = 0
var new_wantCount: Int = 0
var notes: String = ""
var updateDate: String = ""
// print description
var description: String {
return ("SpecificsStruct: \(seriesUniqueId), \(figureGlobalUniqueId), \n LOOSE: Have \(loose_haveCount), sell \(loose_sellCount), want \(loose_wantCount) \n NEW: Have \(new_haveCount), sell \(new_sellCount), want \(new_wantCount), order \(new_orderCount) \(new_orderText) \n notes \(notes), update date \(updateDate)")
}
func saveSpecifics(userID: String) {
setFirebaseSpecifics(userID: userID)
}
func setFirebaseSpecifics(userID: String) {
let firebaseRef: DatabaseReference! = Database.database().reference()
let specificsPath = SpecificsFirebase.getSpecificsFirebaseRef(userID: userID, seriesUniqueId: SeriesUniqueIdEnum(rawValue: seriesUniqueId)!,
figureGlobalUniqueId: figureGlobalUniqueId)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = kDateFormatDatabase // firebase 2020-09-13T14:34:47.336
let updateDate = Date()
let updateDateString = dateFormatter.string(from: updateDate)
let firebaseSpecifics = [
"figureGlobalUniqueId": figureGlobalUniqueId,
"loose_haveCount": loose_haveCount,
"loose_sellCount": loose_sellCount,
"loose_wantCount": loose_wantCount,
"new_haveCount": new_haveCount,
"new_orderCount": new_orderCount,
"new_orderText": new_orderText,
"new_sellCount": new_sellCount,
"new_wantCount": new_wantCount,
"notes": notes,
"seriesUniqueId": seriesUniqueId,
"updateDate": updateDateString
] as [String: Any]
// #if DEBUG
// print("Setting firebase specifics for \(firebaseSpecifics)")
// #endif
firebaseRef.child(specificsPath).setValue(firebaseSpecifics)
}
}
人物结构
struct Figure: Hashable, Codable, Identifiable {
var id = UUID()
// var id: String { Figure_Unique_ID }
func hash(into hasher: inout Hasher) {
hasher.combine(figureUniqueId)
}
let figureGlobalUniqueId: String
let seriesUniqueId: SeriesUniqueIdEnum
let figureUniqueId: String
let sortOrder: Int
let debutYear: Int?
let phase: String
let wave: String
var figureNumber: String?
let sortGrouping: String
var uPC: String?
let figureName: String
let figurePackageName: String
// var tags = [String]()
var scene: String?
var findTerms: String
var excludeTerms: String?
var amazonASIN: String?
var amazonShortLink: String?
var walmartSKU: String?
var targetTCIN: String?
var targetDPCI: String?
var entertainmentEarthIN: String?
var retailDate: Date?
var retailPrice: Float?
var addedDate: Date
// calculated or set
let primaryFrontImageName: String
let primaryFrontImageNameNoExt: String
// generated retail links
var searchString: String
// calculated later
var amazonURL: URL?
var entertainmentEarthURL: URL?
var targetURL: URL?
var walmartURL: URL?
var eBayURL: URL?
var specifics: Specifics
init (seriesUniqueId: SeriesUniqueIdEnum,
figureUniqueId: String,
sortOrder: Int,
debutYear: Int?,
phase: String,
wave: String,
figureNumber: String?,
// sortGrouping: String,
// tags: String?,
uPC: String?,
figureName: String,
figurePackageName: String,
scene: String?,
findTerms: String,
excludeTerms: String?,
amazonASIN: String?,
amazonShortLink: String?,
walmartSKU: String?,
targetTCIN: String?,
targetDPCI: String?,
entertainmentEarthIN: String?,
retailDate: Date?,
retailPrice: Float?,
addedDate: Date) {
self.seriesUniqueId = seriesUniqueId
self.figureUniqueId = figureUniqueId
self.figureGlobalUniqueId = "\(seriesUniqueId.rawValue)_\(figureUniqueId)"
self.sortOrder = sortOrder
self.debutYear = debutYear
self.phase = phase
self.wave = wave
self.figureNumber = figureNumber
self.sortGrouping = phase // <---------- Uses Phase!
self.uPC = uPC
self.figureName = figureName
self.figurePackageName = figurePackageName
self.scene = scene
self.findTerms = findTerms
self.excludeTerms = excludeTerms
self.amazonASIN = amazonASIN
self.amazonShortLink = amazonShortLink
self.walmartSKU = walmartSKU
self.targetTCIN = targetTCIN
self.targetDPCI = targetDPCI
self.entertainmentEarthIN = entertainmentEarthIN
self.retailDate = retailDate
self.retailPrice = retailPrice
self.addedDate = addedDate
// split out the hash tags
// if let tags = tags {
// let words = tags.components(separatedBy: " ")
// for word in words{
// if word.hasPrefix("#"){
//// let hashtag = word.dropFirst()
// self.tags.append(String(word))
// }
// }
// }
// set the specifics to the default so that the pickers work. Pickers don't like optionals.
// DONT SET the isSet here as this is a default record
self.specifics = Specifics(id: UUID(), specificsFirebase: SpecificsFirebase(seriesUniqueId: seriesUniqueId.rawValue, figureGlobalUniqueId: figureGlobalUniqueId))
// built fields
self.primaryFrontImageName = "\(seriesUniqueId.rawValue)_\(figureUniqueId)\(kPrimaryFrontImageNameSuffix)\(kSmallSuffix).\(kImageJpgExt)"
self.primaryFrontImageNameNoExt = "\(seriesUniqueId.rawValue)_\(figureUniqueId)\(kPrimaryFrontImageNameSuffix)\(kSmallSuffix)"
// generated
self.searchString = "\(seriesUniqueId) \(figureUniqueId), \(phase) \(wave) \(figurePackageName)"
if let figureNumber = figureNumber {
self.searchString += " \(figureNumber)"
}
if let uPC = uPC {
self.searchString += " \(uPC)"
}
if let amazonASIN = amazonASIN {
self.searchString += " \(amazonASIN)"
}
if let targetTCIN = targetTCIN {
self.searchString += " \(targetTCIN)"
}
if let targetDPCI = targetDPCI {
self.searchString += " \(targetDPCI)"
}
if let entertainmentEarthIN = entertainmentEarthIN {
self.searchString += " \(entertainmentEarthIN)"
}
if let scene = scene {
self.searchString += " \(scene)"
}
if let debutYear = debutYear {
self.searchString += " \(debutYear)"
}
}
enum CodingKeys: String, CodingKey {
case figureUniqueId = "Figure_Unique_ID"
case seriesUniqueId = "Series_Unique_ID"
case sortOrder = "Sort_Order"
case debutYear = "Debut_Year"
case phase = "Phase"
case wave = "Wave"
case figureNumber = "Number"
// case sortGrouping = "Sort_Grouping"
// case tags = "Tags"
case uPC = "UPC"
case figureName = "Action_Figure"
case figurePackageName = "Action_Figure_Package_Name"
case scene = "Scene"
case findTerms = "Find_Terms"
case excludeTerms = "Exclude_Terms"
case amazonASIN = "Amazon_ASIN"
case amazonShortLink = "Amazon_Short_Link"
case walmartSKU = "WalmartSKU"
case targetTCIN = "Target_TCIN"
case targetDPCI = "Target_DPCI"
case entertainmentEarthIN = "EEIN"
case retailDate = "Retail_Date"
case retailPrice = "Retail_Price"
case addedDate = "Added_Date"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
let seriesUniqueIdString = try values.decode(String.self, forKey: .seriesUniqueId)
let figureUniqueId = try values.decode(String.self, forKey: .figureUniqueId)
let sortOrder = try values.decode(Int.self, forKey: .sortOrder)
let debutYear = try values.decode(Int.self, forKey: .debutYear)
let phase = try values.decode(String.self, forKey: .phase)
let wave = try values.decode(String.self, forKey: .wave)
let figureNumber = try? values.decode(String.self, forKey: .figureNumber)
// let sortGrouping = try values.decode(String.self, forKey: .sortGrouping)
// let tags = try? values.decode(String.self, forKey: .tags)
let uPC = try? values.decode(String.self, forKey: .uPC)
let figureName = try values.decode(String.self, forKey: .figureName)
let figurePackageName = try values.decode(String.self, forKey: .figurePackageName)
let scene = try? values.decode(String.self, forKey: .scene)
let findTerms = try values.decode(String.self, forKey: .findTerms)
let excludeTerms = try? values.decode(String.self, forKey: .excludeTerms)
let amazonASIN = try? values.decode(String.self, forKey: .amazonASIN)
let amazonShortLink = try? values.decode(String.self, forKey: .amazonShortLink)
let walmartSKU = try? values.decode(String.self, forKey: .walmartSKU)
let targetTCIN = try? values.decode(String.self, forKey: .targetTCIN)
let targetDPCI = try? values.decode(String.self, forKey: .targetDPCI)
let entertainmentEarthIN = try? values.decode(String.self, forKey: .entertainmentEarthIN)
let retailDateString = try? values.decode(String.self, forKey: .retailDate)
let retailPrice = try? values.decode(Float.self, forKey: .retailPrice)
let addedDateString = try? values.decode(String.self, forKey: .addedDate)
// calculated
let seriesUniqueId = SeriesUniqueIdEnum(rawValue: seriesUniqueIdString)!
// date logic
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/dd/yyyy" //Your date format
var retailDate: Date? = nil
if let retailDateString = retailDateString {
if let retailDateValid = dateFormatter.date(from: retailDateString) {
retailDate = retailDateValid
}
}
var addedDate = defaultAddedDate
if let addedDateString = addedDateString {
if let addedDateValid = dateFormatter.date(from: addedDateString) {
addedDate = addedDateValid
}
}
self.init(seriesUniqueId: seriesUniqueId,
figureUniqueId: figureUniqueId,
sortOrder: sortOrder,
debutYear: debutYear,
phase: phase,
wave: wave,
figureNumber: figureNumber,
// sortGrouping: phase,
// tags: tags,
uPC: uPC,
figureName: figureName,
figurePackageName: figurePackageName,
scene: scene,
findTerms: findTerms,
excludeTerms: excludeTerms,
amazonASIN: amazonASIN,
amazonShortLink: amazonShortLink,
walmartSKU: walmartSKU,
targetTCIN: targetTCIN,
targetDPCI: targetDPCI,
entertainmentEarthIN: entertainmentEarthIN,
retailDate: retailDate,
retailPrice: retailPrice,
addedDate: addedDate)
}
func encode( to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.seriesUniqueId, forKey: .seriesUniqueId)
try container.encode(self.figureUniqueId, forKey: .figureUniqueId)
try container.encode(self.sortOrder, forKey: .sortOrder)
try container.encode(self.debutYear, forKey: .debutYear)
try container.encode(self.phase, forKey: .phase)
try container.encode(self.wave, forKey: .wave)
try container.encode(self.figureNumber, forKey: .figureNumber)
// try container.encode(self.sortGrouping, forKey: .sortGrouping)
// try container.encode(self.tags, forKey: .tags)
try container.encode(self.uPC, forKey: .uPC)
try container.encode(self.figureName, forKey: .figureName)
try container.encode(self.figurePackageName, forKey: .figurePackageName)
try container.encode(self.scene, forKey: .scene)
try container.encode(self.findTerms, forKey: .findTerms)
try container.encode(self.excludeTerms, forKey: .excludeTerms)
try container.encode(self.amazonASIN, forKey: .amazonASIN)
try container.encode(self.amazonShortLink, forKey: .amazonShortLink)
try container.encode(self.walmartSKU, forKey: .walmartSKU)
try container.encode(self.targetTCIN, forKey: .targetTCIN)
try container.encode(self.targetDPCI, forKey: .targetDPCI)
try container.encode(self.entertainmentEarthIN, forKey: .entertainmentEarthIN)
try container.encode(self.retailDate, forKey: .retailDate)
try container.encode(self.retailPrice, forKey: .retailPrice)
try container.encode(self.addedDate, forKey: .addedDate)
}
}
【问题讨论】:
标签: arrays swift performance loops