Что такое экспорт * в файле module.modulemap внутри каждого фреймворка?

Я создал один фреймворк с именем CommunicationВнутри рамочного контейнера есть один module.modulemap файл.

module.modulemap

framework module Communication {
  umbrella header "Communication.h"

  export *
  module * { export * }
}

Я могу понять, что модуль требовал зонтичного заголовка, чтобы выставить его содержащему приложение / цель.

Но в чем смысл двух других строк кода?

  export *
  module * { export * }

Если у кого есть идеи, что эти строки экспортируют?

3 ответа

История

Modular Framework содержит .modulemap файл по следующему пути

module_name.framework/Modules/module_name.modulemap

Что касается именования, мое правило

ProductName -> Product Module Name -> <name>.modulemap -> framework module <name>

Когда вы создаете библиотеку, вы должны создать и настроить ее вручную [Custom modulemap], когда вы создаете фреймворк, Xcode генерирует его автоматически, еслиBuild Settings -> Defines Module -> YES.

Когда вы создаете свой .modulemap файл вы должны обновить путь в Build Settings -> Module Map File

Давайте посмотрим на пример:
Примечание: я используюexplicit module чтобы показать, что Xcode будет генерировать ошибки

//ClassA.h
#import "ClassAA.h"

//ClassB.h
//ClassAA.h

//Module.h
#import "ClassA.h"
#import "ClassB.h"

//Module.modulemap
framework module Module {
    //Expose all imports from Module.h for Module module recursively
    //    import Module - you can use ClassA, ClassB, ClassAA
    umbrella header "Module.h" 

    //explicit module * { }
    //    1. `module` works only with `umbrella`
    //    2. Generates a submodule for every header inside Module.h recursively
    //        import Module.ClassA, import Module.ClassB, import Module.ClassC
    //explicit module * { export * }
    //export all imports from <submodule_name>.h for <submodule_name> module
    //    import Module.ClassA - you can use ClassA, ClassAA (ClassAA was added)
    explicit module * { export * }

    //export all imports from Module.h for Module module
    //    import Module.ClassA - you can use ClassA, ClassAA, ClassB(ClassB was added)
    export *
}

Что помогло мне разобраться в ключевых словах и в файлах, так это поиграть с ними. Я нашел документацию Clang &amp;gt; Modules сложной для понимания. Я суммирую некоторые эксперименты, которые я проводил 1 , чтобы дать вам представление о том, чтоmoduleключевые слова означают на практике. Я обнаружил, что поведение файла в коде Objective-C и Swift отличается, поэтому я рассмотрю оба варианта отдельно.

Предположим, ваш заголовочный файл состоит из следующих двух импортов:

      #import <Communication/Class1.h>
#import <Communication/Class2.h>

Предположим также, чтоClass1.hопределяет один класс с именем иClass2.hопределяет один класс с именем .

Импорт модулей в классы Objective-C

Предположим, для начала, что ваш файл определен минимально следующим образом:


В классах Objective-C ваших целей, которые связаны с платформой, вы можете импортировать модуль и вызывать классы, импортированные через зонтичный заголовочный файл, следующим образом:

      @import Communication;

+ (void)someMethodThatDependsOnTheCommunicationModule {
    [Class1 someOperation];
    [Class2 someOperation];
}

Чего вы сейчас не можете сделать, так это импортировать подмодуль. Чтобы это стало возможным, вам нужно изменить файл на следующее:

      framework module Communication {
  umbrella header "Communication.h"
  
  module Class1 {
      header "Class1.h"
  }
}

Теперь вы можете импортировать подмодуль и вызвать его следующим образом:

      @import Communication.Class1;

+ (void)someMethodThatDependsOnTheCommunicationModule {
    [Class1 someOperation];
}

Конечно, вы по-прежнему не можете импортировать подмодуль. Чтобы сделать это возможным, вы можете либо добавить объявление модуля в файл, как мы это сделали, либо использовать подстановочный знак 2. Вот как это можно сделать с помощью подстановочного знака:


Благодаря этому изменению вы теперь можете импортировать подмодули и и вызывать их классы следующим образом:

      @import Communication.Class1;
@import Communication.Class2;

+ (void)someMethodThatDependsOnTheCommunicationModule {
    [Class1 someOperation];
    [Class2 someOperation];
}

Я изо всех сил старался найти применение этому ключевому слову в контексте модулей, которые будут импортированы в код Objective-C, но мне это не удалось.

Импорт модулей в классы Swift

Предположим, для начала, что ваш файл определен минимально следующим образом:

      framework module Communication {
  umbrella header "Communication.h"
}

В классах Swift ваших целей, которые связаны с фреймворком, вы можете импортироватьCommunicationмодуль, и вы можете вызывать классы, импортированные черезCommunication.hзаголовочный файл зонтика, например:

      import Communication

func someFunctionThatDependsOnTheCommunicationModule {
    Class1.someOperation()
    Class2.someOperation()
}

Чего вы сейчас не можете сделать, так это импортировать подмодуль. Чтобы это стало возможным, вам нужно изменить файл на следующее:

      framework module Communication {
  umbrella header "Communication.h"
  
  module Class1 {}
}

Теперь вы можете импортировать подмодуль и вызвать его следующим образом:

      import Communication.Class1

func someFunctionThatDependsOnTheCommunicationModule {
    Class1.someOperation()
}

В отличие от того, что я испытал при использовании Objective-C3 , я обнаружил, что все еще могу звонить, несмотря на импортCommunication.Class1только.

Если вы все еще хотите импортироватьCommunication.Class2изолированно 4 , вы можете либо добавитьClass2объявление модуля вmodulemapфайл, как мы это сделали дляClass1или используйте подстановочный знак 2. Вот как это можно сделать с помощью подстановочного знака:

      framework module Communication {
  umbrella header "Communication.h"
  
  module * {}
}

Я изо всех сил старался найти применение этомуexportключевое слово в контексте модулей, которые будут импортированы в код Swift, но мне это не удалось.

1 Я проводил эти эксперименты в Xcode 13.4.1. Если вы столкнетесь с другим поведением в более поздней версии Xcode, дайте мне знать, и я повторю свои эксперименты и обновлю свой ответ.
2 Подстановочный знак будет определять модуль для каждого класса, определенного заголовками, импортированными в зонтичный заголовочный файл.
3 И вопреки моим ожиданиям.
4 Несмотря на то, что это совершенно бессмысленно, как объяснено в предыдущем абзаце.
framework module Communication { // Define a module with framework semantics
    umbrella header "Communication.h" // Use the imported headers in the umbrella header to define the module
    export * // Re-export all symbols from submodules into the main module
    module * { export * } // Create a submodule for every header in the umbrella header
}

module * указывает, что все заголовочные файлы, импортированные в зонтик Communication.h, сами являются подмодулями.

export * внутри module * объявляет, что любой файл заголовка, импортированный из этих файлов заголовка - которые импортированы в зонтике - также является субмодулями.

Узнайте больше здесь, здесь, здесь

Другие вопросы по тегам