Привет друзья! Я из тех, кому скучно просто дергать за ниточки, торчащие из черной коробки, хочется увидеть все своими глазами, как оно работает под капотом. Мы поговорим с вами про рантайм, да-да рантайм. Для наших опытов рассмотрим старого доброго дедушку Objective C и революционного, но пока еще находящегося в стадии развития, Swift. Нам с вами понадобиться нырнуть практически на самое дно абстракций, которые заботливо придумали программисты Apple. Давайте немного разберемся, зачем вообще понадобилось разрабатывать новый язык. Я слышал много негативных отзывов в самом начале, особенно от уже матерых разработчиков Objective C. Если посмотреть повнимательнее на новый язык Swift, он на мой взгляд значительнее взрослее и серьезнее. Во-первых, он написал на языке С++ в отличии от Си, который лежит в основе Objective C. Я здесь высказываю только свои сугубо личные предубеждения, с которыми можно согласиться, можно и поспорить.

На мой взгляд С++ на текущий момент, самый серьезный язык разработки, который позволяет делать все что угодно, но уже более изящнее нежели в Си. Я думаю поэтому именно он был выбран за основу написания языка Swift, LLDB и тд. Я не буду сейчас делать обзор функциональности языка Swift, просто осветим несколько моментов, все остальное можно почитать в специализированной документации. На мой взгляд он намного функциональнее и проще для начинающего программиста, собственно это и было одной из целей его разработки, снизить порог вхождения для новых разработчиков. Вспомните как у вас вставали волосы после первого знакомства со скобочками Objective C, особенно если до этого ты писал на лаконичном C# или Java. Конечно не обошлось и без изощрений, которые придумали программисты на мой взгляд только ради того, что бы выделиться. Но это все лирика, давайте к делу. Для начала разберем базовый класс дедушки Objectice C потому сравним его со Swift.

Для тех кто знает все это, можно пока выйти покурить. Как мы знаем в Objective C любой класс или косвенно или напрямую должен наследоваться от NSObject или NSProxy. Класс NSObject реализует неформальный протокол NSObject. К нему мы еще вернемся, когда будем рассматривать SwiftObject. Забегая вперед скажу, что именно этот протокол очень поможет подружить два языка в будущем, вот она сила полиморфизма! Я предлагаю сделать так, мы по кусочками рассмотрим все методы класса NSObject. А после этого я осмелюсь сделать заключение, что не так с этим великим NSObject. Не буду долго вам мучать, погнали!

@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}

+ (void)load;

+ (void)initialize;
- (instancetype)init
#if NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER
    NS_DESIGNATED_INITIALIZER
#endif
    ;

+ (instancetype)new OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
+ (instancetype)allocWithZone:(struct _NSZone *)zone OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
- (void)dealloc OBJC_SWIFT_UNAVAILABLE("use 'deinit' to define a de-initializer");

- (void)finalize;

....

С первыми двумя методами не так часто приходиться сталкиваться рядовому разработчику Objectice C. Не буду на них останавливаться, скажу лишь пару слов. Метод load вызывается по время загрузки класса или категории в исполняющую среду, не подчиняется классическим правилам наследования, блокирует работу приложения на момент выполнения. Метод initialize вызывается в отложенном режиме, перед первым использованием класса, не блокирует работу приложения, подчиняется классическим правилам наследования.

Все последующие методы отвечают за создания и инициализацию объекта. Тоже немножечко обсудим их. Метод allocWithZone отвечает за выделение памяти под наш объект. Внутри он вызывает наш любимый malloc. Он еще с бородатых времен, когда память разделялась на зоны. Сейчас все объекты создаются в одной зоне, потому появился метод alloc который внутри себя вызывает allocWithZone и передает ему зону по умолчанию — NSDefaultMallocZone.

Методы dealloc и finalize вызывается в момент удаления объекта. Именно в этих методах происходит зачистка всех связанных ресурсов и конечном итоге free и память уходит в пул свободной памяти. Отмечу что finalize обычно не используется а dealloc вызывается в том потоке, в котором произошло последнее освобождение.

Переходим к следующей пачке методов

- (id)copy;
- (id)mutableCopy;

+ (id)copyWithZone:(struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;
+ (id)mutableCopyWithZone:(struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;


Ну наверно даже тому кто никогда не писал на обжектив си нетрудно догадаться, что они для копирования. С зонами все понятно, это древние методы которые уже не актуальны. На самом деле, что бы у нас что то скопировалось надо еще реализовать протокол NSCopying, а если просто вызвать эти методы, то все упадет. Но это мы еще обсудим. А пока перейдем к следующей пачке.

+ (BOOL)instancesRespondToSelector:(SEL)aSelector;
+ (BOOL)conformsToProtocol:(Protocol *)protocol;
- (IMP)methodForSelector:(SEL)aSelector;
+ (IMP)instanceMethodForSelector:(SEL)aSelector;
- (void)doesNotRecognizeSelector:(SEL)aSelector;

+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");

- (BOOL)allowsWeakReference UNAVAILABLE_ATTRIBUTE;
- (BOOL)retainWeakReference UNAVAILABLE_ATTRIBUTE;

+ (BOOL)isSubclassOfClass:(Class)aClass;

+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);

+ (Class)superclass;
+ (Class)class OBJC_SWIFT_UNAVAILABLE("use 'aClass.self' instead");

Вот она интроспекция, собственной персоной. Ключ к безопасному коду в обжектив си. Эти методы позволяют нам оценить объект или класс, какие протоколы он реализует, на какие селекторы может реагировать, и тд. Идем дальше

- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");

Два первых метода вызовутся до того момента как у вас все грохнется если в них вы ничего не реализуете. Это происходит тогда, когда наш объект не понимает, что вы от него хотите и вызываете метод, которого у него нет. Если вы это делаете осмысленно то будьте любезны определить у себя один из этих методов и решить, что делать с вызовом селектора, куда его послать так сказать. На самом деле это называется механизм перенаправления сообщений. Об этом тоже можно почитать, куча статей, и методов там три. Я не буду на этом останавливаться, иначе мы потеряем основную суть этой статьи.

Давайте посмотрим на последние методы

+ (NSUInteger)hash;
+ (NSString *)description;
+ (NSString *)debugDescription;

Я думаю метод хеш всем известен, используется для сравнения объектов, для распределения объектов по гнездам в коллекциях и тд. Методы description и debugDescription необходимо переопределять если вы хотите видеть в логах не просто адреса по которым располагается ваш объект, а какую то осмысленную для него информацию.

Фух, что то я уже утомился описывать эти методы. Давайте теперь повнимательнее посмотрим на этот класс, вот он стоит во главе всех классов в обжектив си, в основном. Что с ним не так? Чем он мне так не нравиться? Да все с ним не так!

Как там гласят наши принципы ООП: наследование, инкапсуляция, полиморфизм! Но там почему то не сказано что наследуй все от одного класса, да простят меня разработчики Java и С#. Я думаю программисты с опытом построения архитектур, наталкивались на различные подводные камни архитектурных решений, которые изначально казались очень продуманными. Я бы сказал что наследование это как бомба замедленного действия, которая в конечном итоге сведет всю вашу продуманную архитектуру в ноль или уже свяжет вам руки для развития. Во многих книгах по архитектуре написано, что выбирайте композицию вместо наследования. Это более гибкий подход, но наследование тоже очень мощная штука, с которой надо уметь обращаться. А именно, правильно разделять абстракции и закладывать на дальнейшее расширение. Забегая вперед скажу, что у Swift пропал этот базовый класс, доступный для программиста.

На самом деле он есть, и называется он SwiftObject от которого наследуются все классы которым необходимо взаимодействовать с Objective C, написанные на свифте. Мы еще поговорим о нем. Многие наверно скажут: что несет этот парень! Наследование это же крутая вещь, переиспользование кода, что в нем плохого. Я думаю эту тему я вынесу в отдельную статью, пока поговорим о другом. Вот например зачем мне метод copy, если я не хочу ничего копировать? Тем не мене я могу его вызывать, и само собой все упадет, если я не реализую прокол NSCopying. Давайте еще поговорим про наследование. Есть метод init который я должен вызывать, если хочу проинициализовать объект, а есть метод dealloc, который вызывается сам! Ибо это метод жизненного цикла объекта и не надо вызывать его руками, никогда! Но никто ведь не мешает мне это сделать, не правда ли здорово? Да вот совсем не здорово. Выходит сам по себе класс NSObject позволяет нам делать то чего делать не надо, или знать то, о чем нам знать не обязательно. Я могу развивать тему дальше и дальше, но я думаю уже понятно, что NSObject это уродство, которого быть не должно и поэтому он пропал в языке Swift для программиста. Формально конечно базовый класс остался и только для IOS платформы, но уже для того что бы можно было подружить между собой, эти два языка: старичка Objective C и амбициозного Swift. Давайте наверно посмотрим на него одним глазком

@implementation SwiftObject
+ (void)initialize {}

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
  assert(zone == nullptr);
  return _allocHelper(self);
}

+ (instancetype)alloc {
  // we do not support "placement new" or zones,
  // so there is no need to call allocWithZone
  return _allocHelper(self);
}

+ (Class)class {
  return self;
}
- (Class)class {
  return (Class) _swift_getClassOfAllocated(self);
}
+ (Class)superclass {
  return (Class) _swift_getSuperclass((const ClassMetadata*) self);
}
- (Class)superclass {
  return (Class) _swift_getSuperclass(_swift_getClassOfAllocated(self));
}

+ (BOOL)isMemberOfClass:(Class)cls {
  return cls == (Class) _swift_getClassOfAllocated(self);
}

- (BOOL)isMemberOfClass:(Class)cls {
  return cls == (Class) _swift_getClassOfAllocated(self);
}

- (instancetype)self {
  return self;
}
- (BOOL)isProxy {
  return NO;
}

- (struct _NSZone *)zone {
  auto zone = malloc_zone_from_ptr(self);
  return (struct _NSZone *)(zone ? zone : malloc_default_zone());
}

- (void)doesNotRecognizeSelector: (SEL) sel {
  Class cls = (Class) _swift_getClassOfAllocated(self);
  fatalError(/* flags = */ 0,
             "Unrecognized selector %c[%s %s]\n",
             class_isMetaClass(cls) ? '+' : '-', 
             class_getName(cls), sel_getName(sel));
}

- (id)retain {
  auto SELF = reinterpret_cast<HeapObject *>(self);
  swift_retain(SELF);
  return self;
}
- (void)release {
  auto SELF = reinterpret_cast<HeapObject *>(self);
  swift_release(SELF);
}
- (id)autorelease {
  return _objc_rootAutorelease(self);
}
- (NSUInteger)retainCount {
  return swift::swift_retainCount(reinterpret_cast<HeapObject *>(self));
}
- (BOOL)_isDeallocating {
  return swift_isDeallocating(reinterpret_cast<HeapObject *>(self));
}
- (BOOL)_tryRetain {
  return swift_tryRetain(reinterpret_cast<HeapObject*>(self)) != nullptr;
}
- (BOOL)allowsWeakReference {
  return !swift_isDeallocating(reinterpret_cast<HeapObject *>(self));
}
- (BOOL)retainWeakReference {
  return swift_tryRetain(reinterpret_cast<HeapObject*>(self)) != nullptr;
}

// Retaining the class object itself is a no-op.
+ (id)retain {
  return self;
}
+ (void)release {
  /* empty */
}
+ (id)autorelease {
  return self;
}
+ (NSUInteger)retainCount {
  return ULONG_MAX;
}
+ (BOOL)_isDeallocating {
  return NO;
}
+ (BOOL)_tryRetain {
  return YES;
}
+ (BOOL)allowsWeakReference {
  return YES;
}
+ (BOOL)retainWeakReference {
  return YES;
}

- (void)dealloc {
  swift_rootObjCDealloc(reinterpret_cast<HeapObject *>(self));
}

- (BOOL)isKindOfClass:(Class)someClass {
  for (auto isa = _swift_getClassOfAllocated(self); isa != nullptr;
       isa = _swift_getSuperclass(isa))
    if (isa == (const ClassMetadata*) someClass)
      return YES;

  return NO;
}

+ (BOOL)isSubclassOfClass:(Class)someClass {
  for (auto isa = (const ClassMetadata*) self; isa != nullptr;
       isa = _swift_getSuperclass(isa))
    if (isa == (const ClassMetadata*) someClass)
      return YES;

  return NO;
}

+ (BOOL)respondsToSelector:(SEL)sel {
  if (!sel) return NO;
  return class_respondsToSelector((Class) _swift_getClassOfAllocated(self), sel);
}

- (BOOL)respondsToSelector:(SEL)sel {
  if (!sel) return NO;
  return class_respondsToSelector((Class) _swift_getClassOfAllocated(self), sel);
}

+ (BOOL)instancesRespondToSelector:(SEL)sel {
  if (!sel) return NO;
  return class_respondsToSelector(self, sel);
}

- (BOOL)conformsToProtocol:(Protocol*)proto {
  if (!proto) return NO;
  auto selfClass = (Class) _swift_getClassOfAllocated(self);
  
  // Walk the superclass chain.
  while (selfClass) {
    if (class_conformsToProtocol(selfClass, proto))
      return YES;
    selfClass = class_getSuperclass(selfClass);
  }

  return NO;
}

+ (BOOL)conformsToProtocol:(Protocol*)proto {
  if (!proto) return NO;

  // Walk the superclass chain.
  Class selfClass = self;
  while (selfClass) {
    if (class_conformsToProtocol(selfClass, proto))
      return YES;
    selfClass = class_getSuperclass(selfClass);
  }
  
  return NO;
}

- (NSUInteger)hash {
  return (NSUInteger)self;
}

- (BOOL)isEqual:(id)object {
  return self == object;
}

- (id)performSelector:(SEL)aSelector {
  return ((id(*)(id, SEL))objc_msgSend)(self, aSelector);
}

- (id)performSelector:(SEL)aSelector withObject:(id)object {
  return ((id(*)(id, SEL, id))objc_msgSend)(self, aSelector, object);
}

- (id)performSelector:(SEL)aSelector withObject:(id)object1
                                     withObject:(id)object2 {
  return ((id(*)(id, SEL, id, id))objc_msgSend)(self, aSelector, object1,
                                                                 object2);
}

- (NSString *)description {
  return _getDescription(self);
}
- (NSString *)debugDescription {
  return _getDescription(self);
}

+ (NSString *)description {
  return _getClassDescription(self);
}
+ (NSString *)debugDescription {
  return _getClassDescription(self);
}

- (NSString *)_copyDescription {
  // The NSObject version of this pushes an autoreleasepool in case -description
  // autoreleases, but we're OK with leaking things if we're at the top level
  // of the main thread with no autorelease pool.
  return [[self description] retain];
}

- (CFTypeID)_cfTypeID {
  // Adopt the same CFTypeID as NSObject.
  static CFTypeID result;
  static dispatch_once_t predicate;
  dispatch_once_f(&predicate, &result, [](void *resultAddr) {
    id obj = [[NSObject alloc] init];
    *(CFTypeID*)resultAddr = [obj _cfTypeID];
    [obj release];
  });
  return result;
}

// Foundation collections expect these to be implemented.
- (BOOL)isNSArray__      { return NO; }
- (BOOL)isNSDictionary__ { return NO; }
- (BOOL)isNSSet__        { return NO; }
- (BOOL)isNSOrderedSet__ { return NO; }
- (BOOL)isNSNumber__     { return NO; }
- (BOOL)isNSData__       { return NO; }
- (BOOL)isNSDate__       { return NO; }
- (BOOL)isNSString__     { return NO; }
- (BOOL)isNSValue__      { return NO; }

@end

Та дамм! Что мы видим, а видим мы что класс SwiftObject имплементирует неформальный протокол NSObject, но реализация методов уже совсем иная. Вот теперь мы знаем врага в лицо, все классы Swift которые явно не наследуются от NSObject теперь неявно наследуются от SwiftObject класса. Сразу сделаю поправку, что это имеет место только для платформы в которой необходимо взаимодействие с Objectice C. На non-Objective C платформах (Linux например) такого нет так как нет необходимости.

Как мы это узнали это уже другая история. Я думаю можно тоже немного рассказать. Как известно язык Swift лежит в открытом доступе на репозитории Apple. Никто не мешает скачать его и собрать самому из исходников, что собственно мы и сделали. Но мы пошли немножечко дальше. Всеми известный Xcode, начиная с версии 8 позволяет подсовывать свой toolchain. Чувствуете да, что это значит? Это значит что можно собрать Swift с дебаг информацией и подложить его в Xcode.

image

Мой коллега так и сделал, что позволило нам дебажить прямо из Xcode исходники Swift.

image

Мы немного отвлеклись, продолжим наши рассуждения. Уже очевидно можно сделать вывод, что метаданные которые генерировались в Objective C и которые генерируются в Swift имеют разную природу. Любой программист который долгое время писал на Objectice C и хоть немного ковырял рантайм, знает вот эту структуру

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

Все мы знаем, что все объекты в конечном итоге в той или иной степени выглядят в виде этой структуры. Наш любимый с вами NSObject представляет собой абстракцию, которая уберегает программиста от прямого взаимодействия с этой структурой. Подробнее про нее можно почитать, есть куча статей написанных за время существования языка, даже на русском. Давайте вернемся к нашему Swift. Теперь для хранения метаданных появился специальный класс Metadata, который достаточно объемный и представляет собой основу для всех метаданных в Swift. Более подробное описание его структуры вынесу в отдельную статью. Еще момент, несмотря на то, что все объекты Swift умеют свою структуру метаданных, они все равно генерируют еще метаданные Objective C для совместимости. То есть каждый обьект Swift имеет два набора метаданных.

Давайте немного подведем итоги. Мы разобрались что NSObject уродлив, и не место ему в новом языке. Поэтому в Swift можно создавать классы не наследуя их ни от чего, но на самом деле для совместимости, они все равно наследуются от SwiftObject. Подружить класс SwiftObject и класс NSObject позволил неформальный протокол NSObject. Который позволяет кастить объект Swift в id и передавать в Objective C. Но было бы неплохо, что бы он там работал, поэтому каждый объект Swift генерирует помимо своих метаданных, еще метаданные Objective C. Вот как то так! Всем спасибо! Здоровья и хорошего настроения!
Поделиться с друзьями
-->

Комментарии (17)


  1. staticlab
    28.01.2017 16:36

    Во-первых, он написал на языке С++ в отличии от Си, который лежит в основе Objective C.

    Вы про компилятор языка или про сам язык?


    1. CocaineMaster
      29.01.2017 02:48

      Сам язык Swift и LLVM по-большей части написаны на С++


      1. staticlab
        29.01.2017 18:34

        А компилятор Swift на чём? :)


  1. yury-dymov
    28.01.2017 17:46

    Я просто оставлю это здесь


    http://harmful.cat-v.org/software/c++/linus


    1. CocaineMaster
      29.01.2017 02:47
      +1

      Странно почему только все поголовно не пишут на Си, он же такой великолепный)) Я помню высказывания одного программиста Си: «Раньше мужчины еще чувствовали себя мужчинами и сами писали драйверы для устройств»!!! Наверно это из этой серии. Почему то появляются высокоуровневые языки. Обратите внимание на год этого сообщения, 2004?) Наверно кое что уже поменялось с того времени, все например)


      1. yury-dymov
        29.01.2017 11:39
        -3

        Во-первых, 2007, и я не понимаю, как этого можно было не заметить.
        Во-вторых, все поголовно пишут на Си. Драйверы, СУБД, системные вещи, серверные приложения.
        В-третьих, ниша С++ — это небольшая доля клиентских приложений, небольшая доля серверных приложений, геймдев и всякого рода компиляторы.


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


        Серверные и веб приложения сейчас уходят в функциональную парадигму, среди мобильных приложений тренд на react-native + native extensions, у десктопных — в Electron.


        Я закончил писать на С++ 5 лет назад и с тех пор он мне был нужен только для спортивного программирования, хотя я писал и десктоп, и серверные, и веб, и мобильные приложения. То есть, для чего угодно C++ — это не first choice.


        1. CocaineMaster
          29.01.2017 12:08
          +1

          Я очень рад за вас, что вам удалось получить такой обширный опыт в разработке! Кому то нравятся женщины с белыми волосами, кто то предпочитает брюнеток. Специально для вас я оставил пометку, что это сугубо мое личное мнение. Можно бесконечно спорить об этом, но я считаю что если использовать любой инструмент с головой, то все у вас получиться. Выстрелить себе в ногу можно на любом языке, и говорить потом что он уродлив. Мне понравилось ваше высказывание:" небольшая ниша всякого рода компиляторов". Еще странно почему gcc почти польностью переписали на С++ )) Это наверно тоже потому что он для спортивного программирования. Вообще я ничего не имеют против Си, это замечательный язык. Просто все имеет свое применение и важно понимать где это оправданно, а где можно подняться на абстракцию выше, не потеряв в этом значительную часть производительности


          1. yury-dymov
            29.01.2017 13:29
            -1

            В спортивном программировании от всего C++ мне нужно только STL.


            Я лишь хотел обратить внимание, что в контексте статьи не имеет никакой разницы, на чем написан язык. С++ здесь не является конкурентным преимуществом. "Серьезнее и взрослее" тоже очень спорное утверждение, по крайней мере в статье я не нашел аргументов, которые бы подкрепляли эти слова.


            Swift принес синтаксический сахар, который дал JS'никами и C-шарперам более простой вход в нативную iOS разработку. Примерно тоже самое сделал Elixir с Erlang'ом в вебе. Сейчас Swift постепенно выбирается из своей ниши и становится языком общего назначения. Станет ли — большой вопрос. Apple планирует на него переходить только через два года, что говорит о многом.


            Сам контент статьи весьма годный, но своим введением ты сильно подрываешь к себе доверие. Иметь свое мнение это хорошо и здорово, но его либо нужно чем-то подкреплять, либо публиковаться в собственном бложике.


            Anyway, добро пожаловать на хабр. Надеюсь мои комментарии не обидели, и помогут сделать следующие статьи лучше :-)


            1. ad1Dima
              30.01.2017 07:01

              Swift принес синтаксический сахар, который дал JS'никами и C-шарперам более простой вход в нативную iOS разработку.

              Вот уж не знаю, все эти try/try?/try!, generics без возможности явно указать тип при вызове, замыкания с параметрами внутри {}. После c# это дается чуть не сложнее, чем Obj-C.


  1. WEStor
    28.01.2017 18:07
    +2

    Круто, что собрали свой тулчейн и поковырялись в исходнидниках свифта, но в чем суть то оказалась? Доказать, что NSObject уродлив? (с этого началось и этим же закончилось) Или показать Ваше исследование? В итоге получилось немного сумбурно.

    Ззаметил в статье несколько неточностей как по мне:

    Формально конечно базовый класс остался и только для IOS платформы

    все классы Swift которые явно не наследуются от NSObject теперь неявно наследуются от SwiftObject класса. Сразу сделаю поправку, что это имеет место только для IOS платформы. На non-IOS платформах (Linux например) такого нет так как нет необходимости.


    А как же тогда macOS, watchOS, tvOS? Под них тоже вполне можно использовать Objective-C с тем же Foundation и NSObject. Писали бы тогда что это справедливо для платформ, в которых есть необходимость взаимодействовать с Objective-C рантаймом.

    все классы Swift которые явно не наследуются от NSObject теперь неявно наследуются от SwiftObject класса


    Разве? Больше похоже, что это класс, который представляет Swift классы которые наследуются от NSObject в Objective-C рантайме.

    На сколько мне известно, не NSObject классы в рантайме Objective-C вообще не могут быть представлены. Пробовали ли Вы посмотреть во что компилится чистый Swift класс есть ли в нем все эти методы? Может у свифтовых структур и енамов они тоже есть?

    Зачем эпплу ко всем свифт классам привязывать NSObject методы, если в рантайме Objective-C эти классы представлены быть не могут и в будущем, возможно, свифт будет жить без наследия objc? Как то недальновидно ведь.


    1. CocaineMaster
      29.01.2017 03:01

      По первой претензии согласен, поправил. Второй вопрос не совсем понял, SwiftObject добавлен для того что бы можно было обьекты Swift кастить в id и передавать в Objective C. С перечислениями написанными чисто на свифт само собой это не работает, так как они реализованы совсем по другому, и компилятор заботливо скажет об этом.


      1. WEStor
        29.01.2017 14:07

        Возможно я не прав. Пока не стал ковырятся с собственным тулчейном, но скажу, что в качестве Any (id) оказалось возможным передавать в objective-c функцию и чистые swift классы, и енамы, и структуры, и даже кортежи и дженерики. Внутри функции все это спокойно кастится к NSObject и можно вызвать любой метод NSObject протокола. Больше, конечно, ничего сделать нельзя толкового, так как такой тип в objc не может быть представлен, но факт есть факт. Сдается мне, что SwiftObject представляет в objc не только классы, но и абстракцию над типами, которые не могут быть в нем представлены другим образом. Интересно было бы изучить этот вопрос.


        1. V1tol
          29.01.2017 20:26
          +1

          Насколько я ковырял рантайм, там не всё так однозначно. И свифтовые классы точно не наследуются от nsobject. Например, чистый свифтовый класс можно использовать в обжси коде, но его нельзя будет унаследовать. https://github.com/apple/swift/blob/master/test/stdlib/Bridgeable.swift


  1. katleta
    28.01.2017 18:59

    Спасибо за статью! Было интересно читать.
    Начало было захватывающее и не поверхностное, а вот концовки нет. Была хорошая завязка, сюжет, но нет конца…


    1. CocaineMaster
      29.01.2017 03:05

      Согласен с вами, очень хотелось поделиться уже, не дотерпел. В след раз буду писать более развернуто и логическим завершением


  1. mkll
    28.01.2017 19:44
    +4

    … жаждущей новых выделений


    Э-э-э, ну да, каждый, конечно, воспринимает в меру своей испорченности, но лучше бы вы писали сухо и по делу — к цветистым «литературным» оборотам (особенно на фоне ужасающей грамматики) вам прибегать еще рановато.


    1. CocaineMaster
      29.01.2017 03:07

      Убрал, что бы не резало глаза.) Что касаемо грамматики каюсь, текст оч сырой и не проверялся на ошибки. Я очень хотел донести суть, надеюсь что то удалось сделать. В след раз буду писать чище и приятнее )