Как «подделать» ивары в категории Obj-C (iPhone) - PullRequest
6 голосов
/ 10 сентября 2009

Обновление:

iPhone OS 3.1 имеет связанные объекты. Тем не менее, iPhone симулятор не делает. Если вы хотите проверить код связанных объектов в симуляторе, вы должны сообщить об ошибке.

Смотрите мой ТАК вопрос здесь .

rdar: // 7477326


Снежный барс теперь имеет связанные объекты.

Есть ли способ сделать что-то подобное без связанных объектов? (Специально для iPhone.)

Я почти уверен, что видел нечто подобное некоторое время назад, но не могу вспомнить, где. Кое-что о превращении любого объекта в контейнер KVC.

Ответы [ 5 ]

13 голосов
/ 11 сентября 2009

objc_setAssociatedObject () и друзья были добавлены в iPhone OS 3.1, поэтому, если у вас есть возможность настроить таргетинг только на устройства 3.1+, вы можете сделать то же самое, что и на Snow Leopard ...

Если вы не можете, вы можете создать статический словарь ассоциаций и обезьяны исправьте метод NSObjects dealloc. По различным техническим причинам это решение не может быть корректно работать в присутствии GC (именно поэтому Apple добавила материал для ассоциации), но поскольку iPhone не поддерживает GC, это не проблема.

Если вы только начинаете работу над этим проектом, я настоятельно рекомендую использовать функции времени выполнения и нацелиться на версию 3.1 plus, но если это не вариант, вот пример того, как вы это делаете.

LGAssociativeStorage.h:

#import <pthread.h>
#import <Foundation/Foundation.h>

@interface NSObject (LGAssociativeStorage)
@property (retain) id associatedObject;
@end

LGAssociativeStorage.mm

#import <objc/runtime.h>
#import "LGAssociativeStorage.h"

/* We are using STL containers because:
   1) Using Objective C containers can cause deallocs which cause recursion issues
   2) STL containers are high perf containers that don't introduce external code dependencies
   Ideally one could include a thread safe map implementation, but I don't need one currently
*/

#include <map>

typedef std::map<id,id> idMap_t;
typedef std::pair<id,id> idPair_t;

static NSMutableDictionary * data = nil;
static pthread_mutex_t data_lock = PTHREAD_MUTEX_INITIALIZER;
static IMP gOriginalNSObjectDealloc = nil;
static idMap_t  associatedObjectMap;

static
void removeAssociatedObjectFromMap(id self) {
  idMap_t::iterator iter = associatedObjectMap.find(self);
    if( iter != associatedObjectMap.end() ) {
        [iter->second release];
        associatedObjectMap.erase(iter);
    }
}

static
id newNSObjectDealloc(id self, SEL deallocSelector, ...) {
    pthread_mutex_lock(&data_lock);
    removeAssociatedObjectFromMap(self);
    pthread_mutex_unlock(&data_lock);
    return gOriginalNSObjectDealloc(self, deallocSelector);
}

static void initIfNecessary(void) {
    if (!data) {
        data = [[NSMutableDictionary alloc] init];

        // The below line of code is abusive... in the future the Objective C runtime will use it as evidence
        // that I am an unfit software engineer and take custody of all my code
        gOriginalNSObjectDealloc = class_replaceMethod([NSObject class], @selector(dealloc), newNSObjectDealloc, "v@:");
    }
}



@implementation NSObject (LGAssociativeStorage)

- (id) associatedObject {
    id retval = nil;
    pthread_mutex_lock(&data_lock);
    initIfNecessary();
    idMap_t::iterator iter = associatedObjectMap.find(self);
    if( iter != associatedObjectMap.end() ) {
        retval = iter->second;
    }
    pthread_mutex_unlock(&data_lock);
    return retval;
}

- (void) setAssociatedObject:(id)object_ {
    pthread_mutex_lock(&data_lock);
    initIfNecessary();
    removeAssociatedObjectFromMap(self);
    [object_ retain];
    associatedObjectMap.insert(idPair_t(self, object_));
    pthread_mutex_unlock(&data_lock);   
}

@end
3 голосов
/ 10 сентября 2009

Вы всегда можете хранить их в синглтоне.

2 голосов
/ 11 сентября 2009

Нет хороших способов сделать это в общей категории.

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

Единственный общий способ решить эту проблему - использовать метод swizzling, чтобы заменить метод dealloc NSObject, чтобы сообщить об освобождении объекта и освободить связанные с ним данные. Я уверен, что кто-то сделал это, но такой отвратительный хак было бы очень трудно рекомендовать как действительный подход.

Теперь, если ваши объекты в вопросах имеют какой-то другой способ мониторинга жизненного цикла (т. Е. Какой-то хук освобождения, например какой-то метод делегата objectWillClose), вы можете подключиться к нему, чтобы освободить связанные с ним данные, и это сделать технику довольно простой и законной.

0 голосов
/ 19 ноября 2013

Несмотря на озабоченность проблемами параллелизма, почему бы просто не использовать глобальные переменные? Даже используя во время выполнения objc_set / get методы AssociatedObject (), вы не передаете адрес «глобальной» статической переменной, и в этом случае у вас все еще есть проблемы с параллелизмом, не так ли?

0 голосов
/ 11 сентября 2009

Я добавлю ответ.

Я нашел оригинальное сообщение в блоге , оно было от Стива Дегутиса.

Это в основном включает в себя замену методов NSObject на valueForUndefinedKey:, setValue:ForUndefinedKey: и dealloc. Затем используйте статический словарь для хранения любых неопределенных ключей.

Почти так же противно и весело, как решение Луи.

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