Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to determine the NSRunLoop/NSThread that is associated with an open NSStream?

I am using (and am required to use) a third-party framework to which I do not have source. The third-party framework handles creating an authenticated client/server connection and hands back a pair of open NSStreams.

The stream creation process, per Apple's docs, is: alloc/init, set delegate, schedule in run loop, and open. Apple's docs go further to say: "You should never attempt to access a scheduled stream from a thread different than the one owning the stream’s run loop." https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html#//apple_ref/doc/uid/20002273-1001844

The stream disposal process is: close, unschedule, release.

If one creates a stream themselves, it's clear where the stream was scheduled. If a third-party framework creates the stream, one may not know where the stream was scheduled.

Looking at the documentation that I found, I did not see a way to programmatically determine the NSRunLoop and NSThread with which an open NSStream is associated. Is there a way to determine this information at runtime?

like image 968
xyzzycoder Avatar asked May 24 '12 15:05

xyzzycoder


People also ask

What is NSRunLoop?

Overview. A NSRunLoop object processes input for sources, such as mouse and keyboard events from the window system and NSPort objects. A NSRunLoop object also processes NSTimer events. Your application neither creates nor explicitly manages NSRunLoop objects.

What is a run loop in Swift?

A RunLoop is a programmatic interface to objects that manage input sources, such as touches for an application. A RunLoop is created and managed by the system, who's also responsible for creating a RunLoop object for each thread object.


1 Answers

I am going to give a code, that will probably work and should be used with care.

We define the following class category:

@interface TheSpecificNSStreamClass (ProposedCategory)

@property (nonatomic, strong, readonly) NSArray* associatedRunLoops;

- (void)myScheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
- (void)myRemoveFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;

@end

and implementation:

@implementation TheSpecificNSStreamClass (ProposedCategory)

- (NSArray*)associatedRunLoops
{
    return [NSArray arrayWithArray:objc_getAssociatedObject(self, @"___associatedRunloops")];
}

- (void)myScheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode
{
    NSMutableArray* runloops = objc_getAssociatedObject(self, @"___associatedRunloops");

    if(runloops == nil)
    {
        runloops = [NSMutableArray array];
        objc_setAssociatedObject(obj, @"___associatedRunloops", runloops, OBJC_ASSOCIATION_RETAIN);
    }

    [runloops addObject:aRunLoop];

    [self myScheduleInRunLoop:aRunLoop forMode:mode];
}

- (void)myRemoveFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode
{
    NSMutableArray* runloops = objc_getAssociatedObject(self, @"___associatedRunloops");

    [runloops removeObject:aRunLoop];

    [self myRemoveFromRunLoop:aRunLoop forMode:mode];
}

@end

Now, in some place in your application delegate, we use method swizzling to exchange the two original methods with our implementation:

Method origMethod = class_getInstanceMethod([TheSpecificNSStreamClass class], @selector(scheduleInRunLoop:forMode:));
Method altMethod = class_getInstanceMethod([TheSpecificNSStreamClass class], @selector(myScheduleInRunLoop:forMode:));
if ((origMethod != nil) && (altMethod != nil))
{
    method_exchangeImplementations(origMethod, altMethod);
}

origMethod = class_getInstanceMethod([TheSpecificNSStreamClass class], @selector(removeFromRunLoop:forMode:));
altMethod = class_getInstanceMethod([TheSpecificNSStreamClass class], @selector(myRemoveFromRunLoop:forMode:));
if ((origMethod != nil) && (altMethod != nil))
{
    method_exchangeImplementations(origMethod, altMethod);
}

The resulting array will have all the associated NSRunLoops.

like image 178
Léo Natan Avatar answered Oct 19 '22 07:10

Léo Natan