NSTask: как получить контекст по завершении, поскольку словарь userInfo не предоставлен?

#objective-c #cocoa #nstask

#objective-c #cocoa #nstask

Вопрос:

Я пытаюсь заархивировать файлы с помощью утилиты командной строки через NSTask.

псевдокод:

 controller:
  init:
    register_self_as_observer_of_nstask_notifications

  startZip(file):
    file = somefileobject 
    task = "zip" with file path as argument
    task.launch

  notification_listener(notification):
    task = notification.get_object
    file = task.??? 
  

Итак, как я могу узнать, к какому файловому объекту относится уведомление? Обычно я использую словарь userInfo для таких вещей, но у NSTask нет такой опции. От Apple Dev: Это уведомление не содержит словарь userInfo.

Спасибо!

Комментарии:

1. Один экземпляр контроллера порождает более одной задачи одновременно?

2. Да, это так. Вот почему мне нужен способ связать задачи с файлами.

3. Это одна из первых вещей, которые я попробовал, но NSDictionary пытается скопировать задачу, когда она используется в качестве ключа, а NSTask не реализует NSCopying.

Ответ №1:

Используйте связанный объектный API, чтобы прикрепить словарь user info к экземпляру задачи. Это был бы самый чистый подход, но его нельзя использовать до внедрения связанного объектного API в Mac OS X 10.6.

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

  • Вы не можете просто [taskInfoDict setObject:userInfo forKey:task] потому NSTask , что он не соответствует NSCopying , но NSDictionary полагается на копирование его ключей.
  • Использование идентификатора процесса, завернутого в NSNumber , в качестве прокси для объекта task в основном работает. Но идентификаторы процессов можно использовать повторно, и задача не получает PID до тех пор, пока она не будет запущена. Корень проблемы в том, что вы не управляете идентификатором процесса; это делает базовая ОС.

Использование адреса объекта task кажется лучшим решением:

 [taskInfoDict setObject:userInfo forKey:[NSValue valueWithPointer:task]]
  

Предполагая среду с подсчетом ссылок, адрес объекта задачи будет стабильным в течение всего срока службы, а его срок службы полностью контролируется вашим приложением. Копирующий сборщик мусора внес бы коррективы в это решение, но в этом случае вы могли бы использовать класс collection, который может обрабатывать указатель напрямую ( NSMapTable ).

Комментарии:

1. Это одна из первых вещей, которые я попробовал, но NSDictionary пытается скопировать задачу, когда она используется в качестве ключа, а NSTask не реализует NSCopying.

2. Не могли бы вы использовать идентификатор процесса задачи (завернутый в NSNumber ) в качестве ключа?

3. @Alexandre: Используйте NSValue перенос адреса задачи в качестве ключа вместо самого NSTask объекта. И связанный объектный подход не зависит от того, NSTask соответствует NSCopying или нет.

4. В итоге я согласился с предложением Wevah (см. Выше) использовать pid NSTask в качестве ключа словаря, и это прекрасно сработало. Вы видите какую-либо причину не делать этого?

5. Идентификаторы процессов можно использовать повторно, и задача не получает идентификатор процесса до его запуска. Адрес задачи полностью находится под контролем вашего приложения. (Хотя связанный объектный подход является самым чистым из всех.)

Ответ №2:

Рассмотрите возможность использования ассоциативных ссылок для привязки URL-адреса файла / пути к каждому экземпляру задачи. У каждого объекта может быть несколько связанных объектов, и у каждого связанного объекта есть соответствующий ключ, который используется для ссылки на связанный объект, когда это необходимо.

В вашем контроллере создайте static переменную, которая представляет URL-адрес файла / ключ пути:

 static char fileURLKey;
  

При создании NSTask экземпляра свяжите соответствующий URL-адрес файла с этим экземпляром:

 NSURL *fileURL = …;
NSTask *task = …;
objc_setAssociatedObject(task, amp;fileURLKey, fileURL, OBJC_ASSOCIATION_RETAIN);
  

Когда задача завершит выполнение, получите задачу из объекта notification, а затем получите URL-адрес файла из задачи:

 NSTask *task = [notification object];
NSURL *fileURL = (NSURL *)objc_getAssociatedObject(task, amp;fileURLKey);
  

Комментарии:

1. В итоге я согласился с предложением Wevah (см. Выше) использовать pid NSTask в качестве ключа словаря, и это прекрасно сработало. Вы видите какую-либо причину не делать этого?

2. @Alex Ну, я нахожу ассоциативные ссылки более простыми и общими. В итоге вы использовали словарь и управляли им вручную, тогда как ассоциативные ссылки выполняют эту работу за вас автоматически.

3. Хорошо, я попытаюсь заменить подход Dict вашим подходом. Спасибо!

4. О-о, только что заметил, что этот метод доступен только для версии 10.6. Все еще много пользователей Leopard. Метод Wevah кажется более совместимым.

5. Ответ Алекса Джереми — самый подходящий ответ, если вы ориентируетесь на Leopard.