Objective C изменяется между ОС 2.2.1 и ОС 3?

По умолчанию ShutdownMode приложения WPF имеет вид OnLastWindowClose. В своем коде вы показываете одно окно, а затем закрываете его. Таким образом, последнее окно закрывается, и приложение закрывается. Затем при закрытии вы показываете другое окно. Поскольку приложение закрывается, окно немедленно закрывается.

Так что все работает так, как вы спроектировали и запрограммировали.

Однако вы хотите сделать что-то другое: окно, которое вы сначала показываете как единственное окно, должно быть «специальным окном», и после его закрытия вы хотите продолжить выполнение, показать свое «главное окно» и затем выйти После закрытия приложения (или всех окон, связанных с приложением).

Самый простой способ: сначала установить режим выключения OnExplicitShutdown, затем, после показа главного окна, установить его в OnLastWindowClose или OnMainWindowClose. В коде:

public static void Main()
{
    var app = new App();
    app.InitializeComponent();

    app.ShutdownMode = ShutdownMode.OnExplicitShutdown;
    new DialogWindow().ShowDialog();

    var mainWindow = new MainWindow();
    app.MainWindow = mainWindow;
    app.Run(mainWindow);
    // When the window has loaded, it should then set the app.ShutdownMode to what you actually want.
}

РЕДАКТИРОВАТЬ: Я не уверен, что именно вы делаете. Код, который вы дали, не скомпилируется, поскольку при правильном использовании класса приложения WPF (с действием сборки App.xaml в качестве ApplicationDefinition) метод Main уже определен. Если у вас просто есть класс, производный от Application, у вас нет метода InitializeComponent (). Единственный способ получить код для компиляции - вручную изменить действие сборки на Page. Однако в этом случае Application.Current == app.

Итак, происходит следующее:

  1. Приложение запускается. Поскольку до сих пор не было создано ни одного WPF-приложения, Application.Current имеет значение null. Это также означает, что диспетчерский цикл не выполняется, а диспетчерские сообщения не обрабатываются (обратите внимание, что диспетчерский цикл также обрабатывает сообщения Windows).
  2. Новый App-объект создан. Поскольку Application.Current имеет значение null, он устанавливает себя как Application.Current.
    • Application.Current.MainWindow имеет значение null, а Application.Current.Windows - пустой список.
    • Поскольку ShutdownMode имеет значение OnLastWindowClose, после закрытия последнего окна текущего приложения (т.е. приложения) начинается отключение.
  3. DialogBox отображается модально. Поскольку не работает ни один диспетчерский цикл, ShowDialog () сам выполняет «локальный» диспетчерский цикл.
    • На самом деле это две части: сначала создается окно. Он принадлежит текущему приложению, поэтому он добавляет себя в Application.Current.Windows. Так как это первое показанное окно и Application.Current.MainWindow имеет значение null, оно также устанавливает себя как главное окно. Во-вторых, окно показывается модально.
    • Поскольку приложение.Current.Windows теперь не пусто, после его запуска начнется отключение.
  4. Пользователь закрывает диалоговое окно. Как часть закрытия, окно удаляет себя из Application.Current.Windows. Кроме того, поскольку это MainWindow, для него установлено значение null. Поскольку Application.Current.Windows теперь пусто, начинается отключение. Однако, поскольку не работает ни один диспетчерский цикл, пока ничего не сделано (установлен только внутренний флаг или аналог).
    • Если бы вы использовали app.Run(new DialogWindow()); app.Run(new MainWindow());, у вас было бы исключение при создании MainWindow, так как в этом случае цикл диспетчера работает правильно. Таким образом, он может фактически отключиться, поэтому, когда MainWindow создается, он генерирует исключение, поскольку цикл диспетчера уже отключен.
  5. Основное окно создано. Как и выше, он добавляет себя в Application.Current.Windows и устанавливает себя как Application.Current.MainWindow.
    • Однако условие для закрытия приложения уже достигнуто. Но пока приложение не имело возможности что-либо делать.
  6. Теперь вызывается Run (). Диспетчерская петля запускается снова и теперь имеет возможность завершить работу приложения. Поэтому он закрывает приложение и закрывает все открытые окна.

Опять же, без ошибок.

Так что один из способов решить эту проблему - перейти на OnExplicitShutdown. Затем на шаге 4 причина прекращения работы не достигается. Лучше (как в обычном приложении WPF) было бы иметь правильное ApplicationDefinition. Удалите StartupUri из App.xaml и вместо этого обработайте событие Startup:

private void OnStartup(object sender, StartupEventArgs e)
{
    this.ShutdownMode = ShutdownMode.OnExplicitShutdown;
    new DialogWindow().ShowDialog();

    var mainWindow = new MainWindow();
    this.ShutdownMode = ShutdownMode.OnLastWindowClose; // or OnMainWindowClose
    mainWindow.Show();
}

Так как при закрытии диалогового окна у нас есть OnExplicitShudown, у приложения нет причин начинать закрываться в этой точке. Затем, после создания MainWindow, у нас снова есть окно как главного окна и как одно из окон приложения. Тогда мы можем перейти в режим выключения, который нам действительно нужен, и показать главное окно.

7
задан Ron Srebro 20 June 2009 в 00:37
поделиться

6 ответов

Это ошибка компилятора.

Хотя вы не указали ее полностью, я ожидаю, что ваш код будет выглядеть так:

@interface Foo : NSObject {
    NSMutableArray *objects;
}
@property (readonly, copy) NSArray *objects;
@end

@implementation Foo
@synthesize objects;
@end

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

Вы можете обойти это, изменив свой код, чтобы четко отделить определение переменной экземпляра от определения свойства, например, поставив префикс имени переменной экземпляра:

@interface Foo : NSObject {
    NSMutableArray *_objects;
}
@property (readonly, copy) NSArray *objects;
@end

@implementation Foo
@synthesize objects = _objects;
@end

Таким образом, компилятор не Я не могу запутаться в сравнении свойства и переменной экземпляра в выражениях типа self.objects (чего в любом случае не должно быть, но, видимо, есть).

Просто чтобы предотвратить неизбежный ответ: Apple не резервирует префикс подчеркивания для переменных экземпляра. Он зарезервирован для методов. В любом случае, если вам не нравится подчеркивание, используйте другой префикс.

17
ответ дан 6 December 2019 в 06:37
поделиться

редактировать: Исходный ответ удален после того, как экспертная оценка обнаружила его отсутствие. Пожалуйста, прочтите комментарии Криса Хэнсона по этому поводу. Остальное я оставляю здесь, потому что думаю, что он все еще действителен.


Обратите внимание, что даже если вы объявите тип свойства как NSArray , возвращаемый объект все равно будет NSMutableArray , и для него определены изменяемые методы . Объявление свойства таким образом , а не предотвращает случайное изменение массива кем-либо.

Если вы хотите быть уверены, что возвращаемый массив неизменяем, вы можете объявить свойство, как в исходном примере, а затем сверните свой собственный аксессор:

- (NSArray *)myArray { return [NSArray arrayWithArray:myArray]; }

Обратите внимание, что это вернет несохраняемый NSArray .

5
ответ дан 6 December 2019 в 06:37
поделиться

Вы видите ошибки, потому что XCode теперь выдает предупреждения и ошибки для вещей, которых он раньше не делал ...

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

4
ответ дан 6 December 2019 в 06:37
поделиться

Это все еще Objective-C 2.0; компилятор, может быть, немного обновлен с учетом такого рода ошибок при изменении типа. Это в значительной степени должно быть ошибкой. По крайней мере, это должно предупредить вас, что вы, вероятно, не имеете в виду то, что написали. Затем вы можете использовать что-то, чтобы он не предупреждал вас, чего нельзя сделать с помощью оператора @synthesize .

Я просто вставил ваш код и оператор синтеза в свой контроллер, и я не получил ошибки или предупреждения об этом. Он построен отлично. Теперь я установил базовый SDK на «Simulator 3.0», а сборку на «Simulator 3.0 Debug». Этот проект начался с 2.2.1 SDK, и я только вчера установил 3.0 SDK; Xcode - это версия 3.1.3.

Обновление : О, я вижу, что на самом деле при попытке установить свойство вы получаете указанную вами ошибку.

1
ответ дан 6 December 2019 в 06:37
поделиться

Так что это действительно связано с вызовом @synthesize, который недоволен раскрытием NSMutableArray как NSArray - почему бы просто не реализовать getMethod.

На самом деле, если подумать, это должно быть метод set, который не устраивает - вы не сможете установить NSArray в NSMutableArray.

0
ответ дан 6 December 2019 в 06:37
поделиться

У вас были следующие вопросы:

Кто-нибудь знает об изменениях в objective-c между OS 2.2.1 и 3.0?

Есть ли какая-либо документация по этим изменениям?

Окончательные ответы таковы:

1) В спецификацию языка не было намеренных изменений, но изменились компилятор и другие инструменты разработчика. Крис и его коллеги являются экспертами по этим изменениям.

2) Вероятно, нет, потому что любые изменения были непреднамеренными или сделаны для лучшего соответствия поведения с документацией.

Вы не должны так быстро отклонять ответ Криса как "догадка." Крис работает над инструментами разработчика Apple.

0
ответ дан 6 December 2019 в 06:37
поделиться