UIColor-подкласс вылетает при касте из Any?

Я знаю, подкласс 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 обратно к MyColor . Но это не может.

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

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

Всего 2 ответа


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

  • MyColor.init вызывает инициализатор суперкласса
  • MyColor затем делегирует внутреннюю фабрику классов и меняет конкретную реализацию из 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]);

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


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

Есть идеи?

10000