How can I make each message an object thread safe?

I am developing an Objective-C application, and what I want to do is something like this:

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

What I'm asking is, how can I create an object in objective-c that intercepts messages sent to another object to execute code before sending a message to that object.

Some things that I think will not work:

  • Categories (I need this to happen only for specific instances of the class)
  • Overwriting an object (I do not have access to the source of the object)
  • Swizzling method (again, this is only needed for specific instances of the class)
+4
source share
3 answers

So, I bit the bullet and decided to create my own proxy class. In a subclass, you simply redefine the message "forwardInvocation:" and you call any code you need before calling [super forwardInvocation:] . Please, this will not work with vardic methods, since NSInvocation does not work with vardic methods.

 #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; } 
+2
source

You must implement NSProxy, which forwards messages to your insecure object.

Here is a good post about forwarding messages in Objective-C and here is the Apple documentation .

To handle thread safety, it depends on what you need. If your unsafe object needs to run in a specific thread, you can use NSRunLoop in the specified thread to serialize messages to this object.

Here is an example of using NSInvocation in combination with NSRunLoop. In this example, they use performSelector:withObject:afterDelay: but using it with performSelector:onThread:withObject:waitUntilDone: will be very similar.

Otherwise, just use a single NSRecursiveLock in the proxy server.

+3
source

If you know exactly which instances the behavior that you are trying to achieve must be achieved, you can go with the swizzling method and call the base implementation if the instance is not the one you are looking for. You can have a global shared object that displays "interesting" instances and uses it in a swizzling implementation, whether you need to name the base or your own.

+2
source

Source: https://habr.com/ru/post/1389708/


All Articles