代码之家  ›  专栏  ›  技术社区  ›  Corey Floyd

如何在obj-c类别中“伪造”ivars(iPhone)

  •  6
  • Corey Floyd  · 技术社区  · 16 年前

    更新:

    iPhone OS 3.1有关联的对象。然而,iPhone模拟器却没有。如果您想在模拟器中测试相关的对象代码,您应该提交一个bug。

    看我的问题 here .

    RDAR:/ / 7477326


    雪豹现在有了关联的对象。

    有没有一种方法可以在没有关联对象的情况下完成类似的事情?(专门针对iPhone。)

    我很确定不久前我看到了这样的东西,但我记不起在哪里了。关于将任何对象转换为kvc容器的内容。

    5 回复  |  直到 12 年前
        1
  •  13
  •   Louis Gerbarg    16 年前

    objc ou setassociatedobject()和朋友被添加到iPhone OS 3.1中,所以如果你可以选择只瞄准3.1+个设备,实际上你可以像在雪豹身上那样做。

    如果你不能,你可以创建一个静态的关联字典和monkey补丁nsobjects-dealloc方法。由于各种技术原因,这个解决方案不能在gc存在的情况下正常工作(这就是苹果添加关联功能的原因),但由于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
    
        2
  •  3
  •   Jeff Kelley    16 年前

    你可以把它们放在一个单独的地方。

        3
  •  2
  •   Peter N Lewis    16 年前

    在一般类别中没有好的方法可以做到这一点。

    通过使用全局nsmutabledictionary,可以轻松地为对象添加数据,该dictionary可以从任意nsobject映射到所需的任何数据。问题是无法知道对象何时被释放,因此您无法(通常)知道数据何时过时。

    解决这个问题的唯一通用方法是使用Swizing方法替换nsObject DealLoc方法来报告对象的释放位置并释放关联的数据。我肯定有人做过这件事,但这是一个可怕的黑客,很难推荐一个有效的认可。

    现在,如果有问题的对象有其他方法来监视生命周期(例如,一些释放挂钩,比如委托对象将关闭某种类型的方法),那么您可以使用它来释放关联的数据,这将使该技术非常直接和合法。

        4
  •  0
  •   Corey Floyd    14 年前

    我再加一个答案。

    我找到了原版 blog post 是史蒂夫·德古蒂斯写的。

    它基本上包括替换nsObject的方法 valueForUndefinedKey: , setValue:ForUndefinedKey: dealloc . 然后使用静态字典存储任何未定义的键。

    就像路易斯的解决方案一样恶心有趣。

        5
  •  0
  •   slimbikr    12 年前

    尽管担心并发性问题,为什么不使用全局变量呢?即使使用运行时objc_set/get associatedobject()方法,也不会传递一个“全局”静态变量地址,在这种情况下,仍然存在并发问题,不是吗?