【问题标题】:How can I make every message an object receives thread-safe?如何使对象接收到的每条消息都是线程安全的?
【发布时间】:2012-01-06 19:03:46
【问题描述】:

我正在开发一个 Objective-C 应用程序,我想做的事情类似于以下内容:

+-----------------+              +---------------+
|   Some Object   | <----------  |  Synchronize  |
|(Not Thread Safe)|              |     Proxy     |
+-----------------+            / +---------------+
                              / 
                             /  Intercepts [someobject getCount]
                            /   @synchronize (someObject)
                           /               
    [someObject getCount] /
                +----------------------+
                |  Some Calling Object |
                +----------------------+

我要问的是,如何在objective-c 中创建一个对象,拦截发送到另一个对象的消息,以便在消息发送到该对象之前执行代码。

一些我认为行不通的事情:

  • 类别(我只需要对某个类的某些实例进行此操作)
  • 重写对象(我无权访问对象的源)
  • 方法调配(同样,这只需要在类的某些实例中发生)

【问题讨论】:

  • 所以,我知道您既无法控制“某些对象”也无法控制“某些调用对象”?
  • @Barley,没错。可惜你没有那么多权限访问 iOS 上的基础库。
  • 一般来说,这种不顾一切的“所有线程可以随时调用任何方法”的并发模式会导致异常糟糕的性能。

标签: objective-c ios multithreading thread-safety


【解决方案1】:

您将实现一个 NSProxy,将消息转发到您的非线程安全对象。

Objective-C 中消息转发的Here is a nice writeup,和here is Apple's documentation

要处理线程安全,这取决于您需要什么。如果您的非线程安全对象必须在特定线程上运行,那么您可以在所述线程上使用 NSRunLoop 将消息序列化到该对象。

Here is an example 将 NSInvocation 与 NSRunLoop 结合使用。在该示例中,他们使用的是performSelector:withObject:afterDelay:,但与performSelector:onThread:withObject:waitUntilDone: 一起使用会非常相似。

否则,只需在您的代理中使用单个 NSRecursiveLock

【讨论】:

    【解决方案2】:

    如果您确切知道哪些实例应该具有您想要实现的行为,那么您可以使用方法调配并在该实例不是您要查找的实例时调用基本实现。 您可以拥有一个列出“有趣”实例的全局共享对象,并在灵活的实现中使用它,无论您必须调用基本实例还是自定义实例。

    【讨论】:

    • 有趣...我现在正在尝试类似的方法,我会在接受之前看看它是如何工作的..
    • 我试过这种方法,但是因为它是一个带有任意选择器的任意对象,我遇到了参数问题。 (无法知道 vardic 函数有多少个参数)。
    【解决方案3】:

    所以,我咬紧牙关,决定创建自己的代理类。要子类化,您只需覆盖“forwardInvocation:”消息,然后在调用[super forwardInvocation:] 之前调用您需要的任何代码。请注意,这不适用于 vardic 方法,因为 NSInvocation 不适用于 vardic 方法。

    #import <Foundation/Foundation.h>
    #import <objc/runtime.h>
    #import <objc/objc.h>
    #import <objc/message.h>
    
    @interface RJProxy : NSObject {
        @private
        NSObject *target;
    }
    
    @property(readwrite, retain) NSObject *target;
    
    -(NSObject *) getTarget;
    
    @end
    
    @implementation RJProxy
    
    @synthesize target;
    
    -(NSMethodSignature *) methodSignatureForSelector:(SEL)aSelector
    { 
        if (objc_getAssociatedObject(self, "isProxy"))
        {
            IMP NSObjectImp = [NSObject instanceMethodForSelector:@selector(methodSignatureForSelector:)];
    
            NSMethodSignature *methodSignature = (NSMethodSignature *) NSObjectImp(self, @selector(methodSignatureForSelector:), aSelector);
    
            if (methodSignature)
                return methodSignature;
    
            return [target methodSignatureForSelector:aSelector];
        }
        else
        {
            Class subClass = self->isa;
            @try {
                self->isa = objc_getAssociatedObject(self, "realSuperclass");
                return [super methodSignatureForSelector:aSelector];
            }
            @finally {
                self->isa = subClass;
            }
        }
    }
    
    -(void) forwardInvocation:(NSInvocation *)anInvocation
    {
        if (objc_getAssociatedObject(self, "isProxy"))
        {
            Class subClass = target->isa;
            target->isa = objc_getAssociatedObject(self, "realSuperclass");
            [anInvocation invokeWithTarget:target];
            target->isa = subClass;
        }
        else
        {
            Class realSuperclass = objc_getAssociatedObject(self, "realSuperclass");
            Class subclass = self->isa;
    
            self->isa = realSuperclass;
    
            if ([self respondsToSelector:[anInvocation selector]])
            {
                [anInvocation invokeWithTarget:self];
            }
            else
            {
                [self doesNotRecognizeSelector:[anInvocation selector]];
            }
    
            self->isa = subclass;
        }
    }
    
    -(NSObject *) getTarget
    {
        if (objc_getAssociatedObject(self, "isProxy"))
        {
            return target;
        }
    
        return self;
    }
    
    @end
    
    BOOL object_setProxy(NSObject *object, RJProxy *proxy);
    BOOL object_setProxy(NSObject *object, RJProxy *proxy)
    {
        proxy.target = object;
    
        Class objectClass = object_getClass(object);
    
        Class objectSub = objc_allocateClassPair(objectClass, [[NSString stringWithFormat:@"%s_sub%i", class_getName(objectClass), objc_getAssociatedObject(objectClass, "subclassTimes")] UTF8String], 0);
        objc_setAssociatedObject(objectClass, "subclassTimes", (id) ((int) objc_getAssociatedObject(objectClass, "subclassTimes") + 1), OBJC_ASSOCIATION_ASSIGN);
        objc_registerClassPair(objectSub);
    
        Class proxyClass = object_getClass(proxy);
    
        Class proxySub = objc_allocateClassPair(proxyClass, [[NSString stringWithFormat:@"%s_sub%i", class_getName(proxyClass), objc_getAssociatedObject(proxyClass, "subclassTimes")] UTF8String], 0);
        objc_setAssociatedObject(proxyClass, "subclassTimes", (id) ((int) objc_getAssociatedObject(proxyClass, "subclassTimes") + 1), OBJC_ASSOCIATION_ASSIGN);
        objc_registerClassPair(proxySub);
    
        object_setClass(object, proxySub);
        object_setClass(proxy,  proxySub);
    
        objc_setAssociatedObject(object, "isProxy", (id) NO, OBJC_ASSOCIATION_ASSIGN);
        objc_setAssociatedObject(proxy,  "isProxy", (id) YES, OBJC_ASSOCIATION_ASSIGN);
    
        objc_setAssociatedObject(object, "realSuperclass", objectClass, OBJC_ASSOCIATION_ASSIGN);
        objc_setAssociatedObject(proxy,  "realSuperclass", proxyClass, OBJC_ASSOCIATION_ASSIGN);
    
        return NO;
    }
    
    @interface SynchronizeProxy : RJProxy
    
    @end
    
    @implementation SynchronizeProxy
    
    -(void) forwardInvocation:(NSInvocation *)anInvocation {
        @synchronized ([self getTarget])
        {
            [super forwardInvocation:anInvocation];
        }
    }
    
    @end
    
    int main (int argc, const char * argv[])
    {
        @autoreleasepool { 
            NSArray *arrayToSynchronize = [NSArray arrayWithObjects:@"This, is, a, test!", nil];
            SynchronizeProxy *myProxy = [SynchronizeProxy new];
    
            object_setProxy(arrayToSynchronize, myProxy);
    
            // now all calls will be synchronized!
            NSLog(@"Array at address 0x%X with count of %lu, and Objects %@ ", (unsigned) arrayToSynchronize, [arrayToSynchronize count], arrayToSynchronize);
    
            [myProxy release];
            [arrayToSynchronize release];
        }
    
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2016-08-19
      • 2013-12-30
      • 1970-01-01
      • 2019-12-21
      • 2016-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多