我正在开发一个Objective-C应用程序,我想做的事情如下:
+-----------------+ +---------------+
| Some Object | <---------- | Synchronize |
|(Not Thread Safe)| | Proxy |
+-----------------+ / +---------------+
/
/ Intercepts [someobject getCount]
/ @synchronize (someObject)
/
[someObject getCount] /
+----------------------+
| Some Calling Object |
+----------------------+
我要问的是,如何在objective-c中创建一个拦截发送到另一个对象的消息的对象,以便在将消息发送到该对象之前执行代码。
我认为有些事情不起作用:
答案 0 :(得分:3)
您将实现一个将消息转发到非线程安全对象的NSProxy。
Objective-C中的消息转发的Here is a nice writeup和here is Apple's documentation。
为了处理线程安全,它取决于您的需求。如果您的非线程安全对象必须在特定线程上运行,那么您可以在所述线程上使用NSRunLoop来序列化该对象的消息。
将NSInvocation与NSRunLoop结合使用的 Here is an example。在该示例中,他们使用的是performSelector:withObject:afterDelay:
,但与performSelector:onThread:withObject:waitUntilDone:
一起使用会非常相似。
否则,只需在代理中使用一个NSRecursiveLock
。
答案 1 :(得分:2)
如果您确切知道哪些实例应该具有您尝试实现的行为,则可以使用方法调配并在实例不是您要查找的实例时调用基本实现。 您可以拥有一个列出“有趣”实例的全局共享对象,并在调配实现中使用它,无论您是否需要调用基本实例或自定义实例。
答案 2 :(得分:2)
所以,我咬紧牙关,决定自己制作代理课程。要进行子类化,只需覆盖'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;
}