NSThread number on iOS? [duplicate]
Asked Answered
T

2

11

When you print a NSThread's description, you get something like this:

<NSThread: 0x1e511b70>{name = (null), num = 1}

Is this "num" available somehow?

This is for debugging only, so it does not need to clear Apple's approval process.

Tyre answered 21/3, 2013 at 21:22 Comment(11)
Maybe it's in -[<thread> threadDictionary]? (You can store thread-local information in NSThread's threadDictionary...)Inflate
Great idea, but no dice: It's empty. :)Tyre
Isn't that just its pointer? May not be the same as the thread ID that NSLog prints.Moselle
No, NSLog prints something much prettier. I could stick with the pointer, of course, but I'd like to find the number. (For instance, the main thread is 1.)Tyre
So guess it's just the order in which the thread was created out of all threads. You could patch +[NSThread alloc] to keep track of the number of threads created... (then retrieve this information via a category on NSThread)Inflate
AFAIK, it can only be accessed from [thread description] and extract the number from itMethuselah
oh yeah--you could parse the result from [thread description]Inflate
Oh, for some reason I thought you were talking about the 0x1e511b70 that's highlighted in red (I wonder what language SO thought that line was in), not the num key in the dictionary. Sorry.Moselle
Ah, that's just Stack Overflow's syntax highlighting. I should have been more specific given it did that, though.Tyre
WARNING: I was parsing the description to get the number (for debugging purposes as well). My code broke in iOS 8 because the description format changed from... <NSThread: 0x79e7450>{num = 1, name = main} ...to... <NSThread: 0x79e7450>{number = 1, name = main}Crinoline
ANOTHER WARNING: It's worse than I originally thought... "num" changed to "number" and they also changed the order of number and name.Crinoline
F
15

That number is actually an ivar in NSThread's private implementation class. The class is _NSThreadInternal, and its name is "_private". Inside that object, the ivar is seqNum.

You can pull it directly if you're willing to rely on undocumented key paths. This'll do it (and good call neilsbot on using valueForKeyPath instead of runtime calls):

@implementation NSThread (GetSequenceNumber)

- (NSInteger)sequenceNumber
{
    return [[self valueForKeyPath:@"private.seqNum"] integerValue];
}

@end

I tested it by manually setting that ivar with runtime calls and then NSLogging the thread. Sure enough, the description reflected the change. This is obviously not documented, so...

...use at your own risk.

It's a fun exercise, but things are typically private for a reason. Shipped code should certainly avoid things like this unless all other routes have been thoroughly exhausted.

Fabricant answered 21/3, 2013 at 22:8 Comment(10)
I'm testing this now, but it looks perfect.Tyre
Yeah, this is perfect. Note that this needs to be done in file not using ARC. But since you can mix-and-match ARC and non, that's not a problem.Tyre
Good point Stephen, I whipped that out in an old project that happened to be open. I updated it to be ARC-friendly (and just nicer in general).Fabricant
Now I'm glad I asked, because I had no idea you could do that. Thanks! :)Tyre
No problem. It still remains to be seen if that's actually a useful value for anything. Typically things are private for a reason :)Fabricant
Matt, would you mind strengthening the "use at your own risk" here? I'd hate to think of future people trying to ship code that relies on direct instance variable access to private implementation classes. :)Tyre
Done. The general public has been appropriately warned.Fabricant
Does valueForKeyPath: work here?Inflate
It does, accounting for underscore prefixed ivar naming conventions. I like the idea. Updated.Fabricant
how to discover the ivar '_private' and 'seqNum'Escallop
I
7

I went ahead and wrote out @xlc's suggestion, just because:

@implementation NSThread (ThreadGetIndex)

-(NSInteger)getThreadNum
{
    NSString * description = [ self description ] ;
    NSArray * keyValuePairs = [ description componentsSeparatedByString:@"," ] ;
    for( NSString * keyValuePair in keyValuePairs )
    {
        NSArray * components = [ keyValuePair componentsSeparatedByString:@"=" ] ;
        NSString * key = components[0] ;
        key = [ key stringByTrimmingCharactersInSet:[ NSCharacterSet whitespaceCharacterSet ] ] ;
        if ( [ key isEqualToString:@"num" ] )
        {
            return [ components[1] integerValue ] ;
        }
    }
    @throw @"couldn't get thread num";
    return -1 ;
}

@end

This answers the question of getting "num" from the thread--although the question linked as a dupe might be helpful for the general question of uniquely identifying threads.

(The answer I like there is "generate a UUID and put in in the thread's thread dictionary.)

Inflate answered 21/3, 2013 at 21:54 Comment(2)
I like this better in this case, as the thread number is more readable than a UUID, and this matches the debugger. Thanks.Tyre
if you're doing this a lot, you can modify this code to cache the lookup using objc_setAssociatedObject()/objc_getAssociatedObject()Inflate

© 2022 - 2024 — McMap. All rights reserved.