Синглтон снова, но с многопоточностью и Objective-C - PullRequest
1 голос
/ 12 апреля 2010

Я знаю, что модель Синглтона обсуждалась очень часто. Но поскольку я не до конца понимаю механизм управления памятью в Objective-C, поэтому, когда я объединил реализацию Singleton с многопоточностью, я получил большую ошибку и потратил целый день на ее устранение.

У меня был одноэлементный объект, давайте назовем его ObjectX. Я объявил объект внутри ObjectX, который отсоединит новый поток, давайте назовем этот объект objectWillSpawnNewThread, когда я вызвал

[[ObjectX sharedInstance].objectWillSpawnNewThread startNewThread];

Новый поток не может быть выполнен правильно, и, наконец, я обнаружил, что не должен объявлять объект objectWillSpawnNewThread внутри класса singleton.

Вот мои вопросы:

  1. Как Objective-C выделяет статический объект в памяти? Где Objective-C выделяет их (стек основного потока или где-то еще)?
  2. Почему это не удастся, если мы создадим новый поток внутри одноэлементного объекта?

Я искал документы языка Objective-C [ObjC.pdf] и Objective-C memory management, возможно, я что-то пропустил, но в настоящее время я не смог найти никакой полезной информации.

Ответы [ 2 ]

1 голос
/ 12 апреля 2010
  1. Изучите правила управления памятью. Они просты, и через неделю вы вернете время.

  2. Забудьте о синглетонах. Точнее, забыть о синглетах, обеспечиваемых кодом. Они излишне сложны, и 99% времени это просто плохой дизайн.

Чтение Синглтоны - это патологические лжецы от Miško Hevery, внимательно прочитайте его, прочитайте все посты в блоге и подумайте об этом. Большую часть времени в классе нет необходимости применять один экземпляр. Обычно вы можете обойти эту проблему, создав некоторый класс Factory, который создаст для вас большинство экземпляров и соединит их вместе:

@interface Factory {
    id classYouWantJustOneInstanceOf;
}

- (id) wireMainController;

@implementation Factory

- (id) init {
    [super init];
    // No “sharedFoo” stuff necessary. Foo is a plain
    // class without singleton boilerplate, easily testable.
    classYouWantJustOneInstanceOf = [[Foo alloc] init];
    return self;
}

- (id) wireMainController {
    id controller = [[SomeClass alloc] init];
    // Dependencies set explicitly, good.
    [controller setFoo:classYouWantJustOneInstanceOf];
    return [controller autorelease];
}

@implementation UIApplicationDelegate

- (void) applicationDidFinishLauching: (UIApplication) app {
    // Now all your “singletons” will get created,
    // no funny static stuff.
    factory = [[Factory alloc] init];
    controller = [[factory wireMainController] retain];
    [window addSubview:controller.view];
    // up and running
}

- (void) dealloc {
   [controller release];
   // Now all your “singletons” will get released.
   [factory release];
   [super dealloc];
}

Я знаю, что это немного неудобно по сравнению с «простым» [Foo sharedFoo], но оно того стоит, поверьте мне.


Преимущества этого решения, если они не очевидны:

  1. В ваших классах нет единого шаблона. Нет статических общих экземпляров, нет синхронизации потоков, ничего.

  2. Явное управление зависимостями. Если для работы класса A необходим экземпляр B, это можно увидеть из открытого сеттера или параметра конструктора A. Внутри файла реализации нет неожиданных зависимостей. И если вам нужно подключить А по-другому, скажем, для целей тестирования, легче, когда зависимости явные (в отличие от вызова [B sharedB]).

  3. Очистить жизненный цикл объекта. Нет статических переменных, нет статической инициализации и ленивой инициализации, если вы действительно этого не хотите. Вы знаете, когда объекты создаются, и вы можете освободить все, что создаете.

Обратите внимание, что это упрощенный случай, так что разделение между делегатом приложения и Factory кажется немного педантичным, но в реальном случае это имеет смысл (см. Принцип одиночной ответственности ).

0 голосов
/ 12 апреля 2010

Трудно сказать, что вы сделали неправильно, если не напишите некоторый код, но в одноэлементных объектах нет никакой магии.Такого статического объекта не существует.

Вопрос 1:

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

// Header
@interface ObjectX : NSObject
{
    SpawnObject* objectWillSpawnNewThread;
}
+(ObjectX*) sharedInstance;

// Implementation
@implementation ObjectX

-(id) init
{
    self = [super init];
    if (self != nil)
    {
        objectWillSpawnNewThread = [[SpawnObject alloc] init];
    }
    return self;
}

+(ObjectX*) sharedInstance
{
    static ObjectX* sharedInstance;
    @synchronized([ObjectX class])
    {
        if (sharedInstance == nil)
        {
            sharedInstance = [[ObjectX alloc] init];
        }
    }
    return sharedInstance;
}
@end

Вопрос 2:

Не могу ответить вам, если вы не отправите свой код и сообщение об ошибке.Тем не менее, одна из распространенных проблем - забыть о создании пула автоматического выпуска в новом потоке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...