#arrays #swift #class #contains #equality
#массивы #swift #класс #содержит #равенство
Вопрос:
У меня есть класс с именем Movie, который на данный момент имеет только строковое свойство с именем movieTitle .
У меня есть массив Movie, и использование метода .contains возвращает false, даже если объект с тем же названием находится в массиве. Интересно, что .contains работает на игровой площадке, которую я создал, но не в настройках приложения.
Спасибо за помощь! Я довольно новичок в программировании, поэтому, если вы и ELI5 что-то делаете, это было бы здорово!
Вот фрагмент кода, который у меня есть. В итоге происходит то, что он просто продолжает добавлять одни и те же 10 записей в массив.
do{
let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String: AnyObject]
if let movieSearch = json["Search"] as? [[String: AnyObject]] {
for movie in movieSearch {
if let title = movie["Title"] as? String {
let newMovie = Movie(movieTitle: title)!
if (!self.movieList.contains(newMovie)) {
self.movieList.append(newMovie)
}
self.tableView.reloadData()
}
}
}
}catch {
print("Error with Json: (error)")
}
Класс Movie
import UIKit
class Movie: NSObject, NSCoding {
// MARK: Properties
struct PropertyKey {
static let movieTitleKey = "title"
}
// MARK: Archiving Paths
static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
static let ArchiveURL = DocumentsDirectory.appendingPathComponent("Movies")
var movieTitle: String
// MARK: Initialization
init?(movieTitle: String) {
// Initialize stored properties.
self.movieTitle = movieTitle
super.init()
// Initialization should fail if there is no itemName
if movieTitle.isEmpty {
return nil
}
}
// MARK: NSCoding
func encode(with aCoder: NSCoder) {
aCoder.encode(movieTitle, forKey: PropertyKey.movieTitleKey)
}
required convenience init?(coder aDecoder: NSCoder) {
let title = aDecoder.decodeObject(forKey: PropertyKey.movieTitleKey) as! String
//Must call designated initializer.
self.init(movieTitle: title)
}
}
// MARK: Equatable
func ==(lhs: Movie, rhs: Movie) -> Bool { // Implement Equatable
return lhs.movieTitle == rhs.movieTitle
}
Что работает на игровых площадках
class Movie: NSObject {
var movieTitle: String
init?(movieTitle: String) {
// Initialize stored properties.
self.movieTitle = movieTitle
super.init()
// Initialization should fail if there is no itemName
if movieTitle.isEmpty {
return nil
}
}
}
var movieList = [Movie]()
var movie1 = Movie(movieTitle: "Batman")
var movie2 = Movie(movieTitle: "Batman")
var movie3 = Movie(movieTitle: "Superman")
movieList.append(movie1!)
movieList.append(movie2!)
movieList.contains(movie1!) // Returns True
movieList.contains(movie3!) // Returns False
Ответ №1:
Поскольку ваш Movie
класс (почему это класс?) наследует от NSObject
(почему?), Он наследует NSObject
соответствие Equatable
протокола NSObject
реализации ==
. По умолчанию выполняется сравнение идентификаторов (сравнение ссылок), а не сравнение значений.
Вот пример:
let movie1 = Movie(movieTitle: "Batman")
let movie2 = Movie(movieTitle: "Batman")
var movieList = [Movie1]
movieList.contains(movie1!) // True, because movie1 was added in
movieList.contains(movie2!) // False, movie2 was never added
Поскольку Movie
он не переопределяется ==
реализацией, которая сравнивает его значения (значения) (например, movieTitle
), он откладывает реализацию по умолчанию, которая сравнивает ссылки. Несмотря movie2
на то, что имеет то же value
самое, это отдельный объект со своей собственной (отдельной) ячейкой памяти. Таким образом, сравнение идентификаторов завершается неудачно, и оно не найдено.
Чтобы решить эту проблему, реализуйте ==
возврат true, если все поля Movie
совпадают. Однако то, что вы пытаетесь сделать, может быть лучше реализовать с помощью структур.
Комментарии:
1. Вероятно, это потому, что происходит «сравнение ссылок», потому что func ==(lhs: Movie, rhs: Movie) -> Bool { // Реализовать эквивалентный возврат lhs.movieTitle == rhs.movieTitle }
2. @Alexander вы спрашиваете, почему это класс… Было
struct
бы лучше для этого случая? Мне просто любопытно3. @Alexander Что касается того, почему это класс, я расширяю учебник, созданный Apple, и они случайно использовали класс. Мне любопытно, почему структура также была бы лучше
Ответ №2:
вы должны попробовать этот способ.
var filtered = [Movie]()
filtered = movieList.filter({$0.movieTitle == "Superman"})
if filtered.count == 1 {
//so,"Superman" movie contained in array..
}
дайте мне знать результаты … спасибо.
Комментарии:
1. Гораздо предпочтительнее правильно реализовать
==
Ответ №3:
Просто попробуйте этот код, он работает отлично.
do{
let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String: AnyObject]
if let movieSearch = json["Search"] as? [[String: AnyObject]] {
for movie in movieSearch {
if let title = movie["Title"] as? String {
let newMovie = Movie(movieTitle: title)!
let movieTitles = (self.movieList as NSArray).value(forKeyPath: "movieTitle") as? [String]
if movieTitles == nil || movieTitles!.contains(title) == false {
self.movieList.append(newMovie)
}
self.tableView.reloadData()
}
}
}
}catch {
print("Error with Json: (error)")
}
Ответ №4:
Попробуйте переопределить isEqual
метод NSObject
, поскольку он уже соответствует Equatable
протоколу. Вы можете протестировать приведенный ниже код на игровой площадке. Надеюсь, это поможет.
class Movie: NSObject {
var movieTitle: String
init?(movieTitle: String) {
// Initialize stored properties.
self.movieTitle = movieTitle
super.init()
// Initialization should fail if there is no itemName
if movieTitle.isEmpty {
return nil
}
}
override func isEqual(_ object: Any?) -> Bool {
guard let theMovie = (object as? Movie) else { return false }
return movieTitle == theMovie.movieTitle
}
}
var movieList = [Movie]()
func appendToList(newMovie: Movie) {
if (!movieList.contains(newMovie)) {
movieList.append(newMovie)
}
}
var movie1 = Movie(movieTitle: "Batman")
var movie2 = Movie(movieTitle: "Batman")
var movie3 = Movie(movieTitle: "Superman")
appendToList(newMovie: movie1!)
movieList.count // count is 1
appendToList(newMovie: movie2!)
movieList.count // count is still 1 not incremented
movieList.contains(movie1!) // Returns true
movieList.contains(movie2!) // Returns true
movieList.contains(movie3!) // Returns false