Подтвердить что ты не робот

Как правильно проверить, недействительно ли возвращаемое значение?

Я столкнулся с нечетным случаем при попытке проверить возвращаемое значение, и мне интересно, как это сделать "правильно" в смысле Swift.

У меня есть NSStatusItem (named item), и я пытаюсь назначить NSStatusItem a NSImage. Когда я делаю NSImage, так как я передаю ему строковое значение для имени изображения, я хочу убедиться, что значение NSImage действительно допустимо (что, если я ошибочно назову строку имени изображения?).

Первое, что я пробовал, это:

if let image: NSImage? = NSImage(named: "CorrectIconName") {
    item.image = image
}

Но это дает ошибку "Связанное значение в условной привязке должно быть необязательного типа". Я подумал, что говоря image: NSImage? дал понять, что это необязательно, но я не думаю.

Я изменил это на следующее:

let image: NSImage? = NSImage(named: "CorrectIconName")
if image {
     item.image = image
}

Это работает отлично. Но я не понимаю, почему это работает, а в первом примере этого нет. Кажется более или менее точной. А так как первый не компилировался, я думал, что попробую другие маршруты...

Так как NSImage(named:) возвращает NSImage, а не NSImage?, я думал, что увижу, что произошло, если я назначил возвращаемое значение конструктора непосредственно item:

item.image = NSImage(named: "CorrectIconName")

Что работает, но не позволяет проверить ошибку, которую я хочу сделать. Если строка неверна, NSStatusItem получает nil для изображения, что приводит к тому, что у меня есть элемент невидимой строки состояния.

Далее я пробовал это:

let image: NSImage = NSImage(named: "CorrectIconName")
if image {
    item.image = image
}

Но это дает ошибку "Тип" NSImage "не подтверждает протокол" LogicValue ", который, я думаю, означает, что вам не разрешено проверять, не является ли оно nil или нет с помощью оператора if.

Однако вы можете проверить, является ли это nil, выполнив следующие действия:

let image: NSImage = NSImage(named: "CorrectIconName")
if image != nil {
    item.image = image
}

Итак, вот вопрос: как точно следует проверять возвращаемое значение, если оно необязательно?

4b9b3361

Ответ 1

На самом деле это необязательно, компилятор просто не показывает его вам.

В документации Apple о работе с объектами Objective-C говорится, что все объекты, импортированные из API Objective-C, фактически неявно разворачиваются необязательно (например, мы объявляем вручную с помощью !):

В некоторых случаях вы можете быть абсолютно уверены, что Objective-Cметод или свойство никогда не возвращает ссылку на объект nil. Делать объекты в этом специальном сценарии, более удобные для работы, Swift импортирует типы объектов как неявно развернутые опциональные. молчаливо развернутые необязательные типы включают в себя все функции безопасности необязательные типы. Кроме того, вы можете получить доступ к значению напрямую без проверка на nil или разворачивание его самостоятельно. [источник]

К сожалению, средство проверки компилятора/синтаксиса не рассматривает их как таковые. Поэтому правильным способом проверки было бы объявить image как тип, который фактически возвращает инициализатор NSImage, неявно развернутый необязательный NSImage:

let image: NSImage! = NSImage(named: "CorrectIconName")
if image {
    // do something
}

Альтернативный метод (через @vacawama):

if let image = NSImage(named: "CorrectIconName") as NSImage! {
    // do something
}

Ответ 2

Существует несколько несоответствий между инициализаторами Objective-C, которые могут возвращать семантику nil и Swift. Если вы вызываете SomeObject (...), он определяет язык для создания экземпляра SomeObject NOT SomeObject?

Есть несколько примеров инициализаторов в рамках Cocoa/Foundation, которые с точки зрения Swift генерируют NSSomething? не подразумеваемый NSSomething

Есть много способов для Apple решить это без ущерба для системы Swift, но до тех пор, пока они этого не сделают, есть несколько способов для вас: "Делайте правильную вещь".

Переверните объект в условный - это работает, но неуклюже:

var maybeImage: NSImage? = NSImage(named: "CorrectIconName")
if let image = maybeImage {

}

Лучше сделать свою собственную функцию для загрузки NSImage

func loadImageNamed(name: String) -> NSImage? {
    return NSImage(named: name)
}

func doSomethingWithAnImage() {
    if let image = loadImageNamed( "CorrectIconName") {
        ....
    }
}

В качестве альтернативы вы можете расширить NSImage.

extension NSImage {
    class func imageWithName(name: String) -> NSImage? {
        return NSImage(named: name)
    }
}

Теперь вы можете сделать естественную вещь.

if let image = NSImage.imageWithName("CorrectIconName") {
}

Это кажется правильным для API Swift/ Cocoa, и я надеюсь, что Apple сделает что-то подобное в будущем.

Ответ 3

Если мне не хватает чего-то совершенно очевидного, это скорее вопрос. Вероятно, вы должны сохранить возврат в переменной, а затем проверить содержимое этой переменной. Я думаю, что это в основном потому, что если эта переменная не была объявлена, если возвращение было необязательным? Утверждение if будет соответствовать!

Это относится к первой части вашего вопроса, которая также кажется остальной частью вашего вопроса, но я могу быть смущен...

Ответ 4

У меня была аналогичная проблема в IOS, и я решил ее использовать с помощью -оператора:

if let im = UIImage(named: "Meetup.png")? {
    imageLayer.contents = im.CGImage
}

Без? он не войдет в ветку if, но при использовании? он делает!