UIColor-подкласс вылетает при касте из Any? - PullRequest
2 голосов
/ 15 января 2020

Я знаю, подклассы UIColor не рекомендуется. Apple говорит,

Большинству разработчиков не нужно подкласс UIColor

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

Допустим, у меня есть этот пользовательский класс цвета:

class MyColor:UIColor{
    convenience init(test:String){
        self.init(red: 0, green: 0, blue: 0, alpha: 1)
    }
}

//Then do this anywhere:
let myColor = MyColor(test: "test")
let temp:Any? = myColor
let c = temp as! MyColor

Это дает сбой. Это происходит сбой, потому что он не может привести temp к MyColor:

Не удалось преобразовать значение типа 'UIDeviceRGBColor' (0x ..) в 'MyColor' (0x ..)

myColor является экземпляром MyColor. Этот же экземпляр сохраняется в переменной типа Any?, а затем возвращается к MyColor. Но это не может.

Хотя, если я приведу его к UIColor, все будет работать. Но я не могу сделать это в моем случае (объяснил в предыдущем вопросе).

Почему это не работает?

Ответы [ 2 ]

2 голосов
/ 15 января 2020

Проблема в том, что UIColor реализован в виде так называемого кластера классов . Это своего рода фабрика класса, но фабрика работает неявно под капотом. В вашем примере, если вы имеете в виду для создания экземпляра MyColor, внутренне происходит следующее:

  • MyColor.init вызывает инициализатор суперкласса
  • Суперкласс затем делегирует фабрику внутреннего класса и меняет конкретную реализацию с MyColor на нечто, соответствующее параметрам, в вашем случае UIDeviceRGBColor.
  • Это означает, что UIColor.init возвращает экземпляр, отличный от того, который вы намеревались создать. Это подкласс UIColor, но больше не MyColor.

В Objective C вы можете отследить это поведение проще:

UIColor *color = [UIColor alloc];
NSLog(@"Address after alloc: %p - class: %@", color, [color class]);
color = [color initWithRed:1.0, green:1.0, blue:1.0, alpha:1.0];
NSLog(@"Address after init:  %p - class: %@", color, [color class]);

После вызова инициализатора вы должны получить другой адрес и класс.

1 голос
/ 15 января 2020

UIColor - это кластер классов, использующий ассоциативные ссылки в категории для добавления свойств! Все пользовательские методы init в UIColor возвращают UIColor *, а не идентификатор, поэтому вы не можете легко создать подкласс UIColor, и вам не следует пытаться.

Ниже приведены две рекомендации:

1- Расширения

extension UIColor {

convenience init(test:String){
    self.init(red: 0, green: 0, blue: 0, alpha: 1)
   }
}

let myColor = UIColor(test: "test")

2- Композиция

class MyColor {

private(set) var color: UIColor

init(test:String) {
    color = UIColor(red: 0, green: 0, blue: 0, alpha: 1)
}

}

let myColor = MyColor(test: "test") 
let temp:Any? = myColor 
let c = temp as! MyColor
...