我怎样才能使每个消息对象接收线程安全?

我正在开发一个Objective-C应用程序,我想要做什么,如下所示:

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

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

有些事情我认为是行不通的:

  • 类别(我需要这个只发生在某个类的某些实例上)
  • 重写对象(我没有访问对象的来源)
  • 方法调整(再一次,这种需要只发生在某个类的某些实例)

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

下面是 Objective-C中消息转发的一个很好的写法 , 这里是苹果公司的文档 。

为了处理线程安全,这取决于你需要什么。 如果你的非线程安全的对象必须运行在一个特定的线程上,那么你可以在这个线程上使用一个NSRunLoop来序列化这个对象的消息。

这里是 NSInvocation与NSRunLoop结合使用的一个例子 。 在这个例子中,他们使用performSelector:withObject:afterDelay:但是用它与performSelector:onThread:withObject:waitUntilDone:会非常相似。

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

如果您确切地知道哪些实例应该具有您正在尝试实现的行为,那么可以使用方法调整,并在实例不是您正在寻找的实例的情况下调用基础实现。 您可以拥有一个全局共享对象,列出“有趣”的实例,并在debugging实现中使用它,无论您需要调用基本实例还是自定义实例。

所以,我咬紧牙关,决定做自己的代理class。 要inheritance子类,只需重写“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; }