【问题标题】:Is it possible to determine the NSRunLoop/NSThread that is associated with an open NSStream?是否可以确定与打开的 NSStream 关​​联的 NSRunLoop/NSThread?
【发布时间】:2012-05-24 15:04:31
【问题描述】:

我正在使用(并且必须使用)我没有来源的第三方框架。第三方框架处理创建经过身份验证的客户端/服务器连接并返回一对打开的 NSStream。

根据 Apple 的文档,流创建过程是:alloc/init、设置委托、在运行循环中调度和打开。 Apple 的文档进一步说:“您永远不应该尝试从与拥有流的运行循环的线程不同的线程访问调度的流。” https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html#//apple_ref/doc/uid/20002273-1001844

流处理过程为:关闭、取消调度、释放。

如果有人自己创建流,则很清楚该流的调度位置。如果第三方框架创建了流,则可能不知道流的调度位置。

查看我找到的文档,我没有看到以编程方式确定与打开的 NSStream 关​​联的 NSRunLoop 和 NSThread 的方法。有没有办法在运行时确定这些信息?

【问题讨论】:

  • 一位编辑删除了 Objective-C 运行时标签,但没有提供问题的答案。我相信,如果有答案,它很可能涉及询问运行时。所以,请保持运行时标签不变。
  • 认为答案可能位于特定域中并不意味着应该对问题进行如此标记。 ObjC 运行时比线程、流和运行循环更低级别(或至少不同)——这些都是框架概念。
  • 如果 3rdparty 框架返回的流是在不可知的线程上创建的,这听起来像是一个严重损坏的 API。可公开访问的流应始终在以下三个位置之一创建:创建包装器的运行循环、框架提供的显式可公开访问的运行循环或主运行循环。几乎总是正确的答案是主运行循环。我肯定会首先联系供应商以确定他们的框架是否真的那么坏。你有证据吗?
  • 请记住,一个流可以在多个 runloop 上调度,因此无法访问它所属的单个 runloop。我必须更深入地考虑如何确定流的来源。如果你有这个,你可以询问运行循环它是否包含给定的源。您当然也可以将流安排到您当前的运行循环中,以便您知道它将在该运行循环(以及其他运行循环)上。不过,关于该配置的清晰文档并不多。
  • @xyzzycoder 仅仅因为答案涉及运行时骇客,并不意味着您的问题与运行时有关。这些标签有助于将问题过滤到某人的兴趣或知识领域。用“objective-c-runtime”标记这个问题会假装这个问题是关于运行时的问题,而这绝对不是。

标签: ios macos nsthread nsstream


【解决方案1】:

我将给出一个代码,它可能可以工作,应该小心使用。

我们定义以下类类别:

@interface TheSpecificNSStreamClass (ProposedCategory)

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

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

@end

和实施:

@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

现在,在您的应用程序委托中的某个地方,我们使用方法调配将两个原始方法与我们的实现交换:

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);
}

生成的数组将包含所有关联的NSRunLoops。

【讨论】:

  • 这是一个聪明的主意,当然值得一试!
  • @Leo Natan 谢谢。创建 NSStreams 时,有一个特定的初始化序列(根据 Apple 的文档),包括在特定的运行循环(以及关联的线程)中调度 NSStream - scheduleInRunLoop:forMode:。 Apple 的文档还说:“您永远不应该尝试从与拥有流的运行循环的线程不同的线程访问预定的流。”所以,我试图解决的挑战是是否有一种机制可以询问 NSStream(或线程/运行循环)以了解 NSStream 的调度位置?
  • 我已经编辑了我的答案,请看一下。抱歉,拖了这么久。
  • @LeoNatan 我没想过要走那条路。聪明的。我担心部署一些涉及方法调配的东西。 :-)
  • @LeoNatan 你认为这是唯一的方法吗?我有点惊讶没有某种官方 API 可以提供这种信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-10
  • 1970-01-01
  • 1970-01-01
  • 2010-10-08
  • 1970-01-01
  • 1970-01-01
  • 2019-12-24
相关资源
最近更新 更多