Class methods are shared by threads
All methods who run a class method on a class name are using the same class and running through that one method. That class method acts as an entry point. You do this by looking at the address of self.
1 2 3 4 |
+(void)classMethod:(NSString*)threadName { NSLog(@"classMethod - thread %@, self address is: %p", threadName, self); } |
If you run couple of threads through this classMethod, you should something like this:
classMethod – thread B, self address is: 0x107159380
classMethod – thread A, self address is: 0x107159380
classMethod – thread C, self address is: 0x107159380
They all have the same address for ‘self’. Which means there exist only one blueprint of the class in which these class methods belong to.
static variables are shared by threads
1 2 3 4 5 6 |
+(void)classMethod:(NSString*)threadName { static int staticNumber; //local static variable NSLog(@"classMethod - thread %@, local static int number's address is: %p", threadName, &staticNumber); } |
you’ll see that in the output, they all display the static variable’s address, which is the same for all threads:
classMethod – thread B, local static int number’s address is: 0x1071594e8
classMethod – thread C, local static int number’s address is: 0x1071594e8
classMethod – thread A, local static int number’s address is: 0x1071594e8
local variables are not shared. They belong to their own thread stack
1 2 3 4 5 6 |
+(void)classMethod:(NSString*)threadName { int localNumber; NSLog(@"classMethod - thread %@, local int number's address is: %p", threadName, &localNumber); } |
you’ll see from output the the addresses of the local variable is different, which tells us that each thread have their own local variables:
classMethod – thread D, local int number’s address is: 0x11420fc44
classMethod – thread B, local int number’s address is: 0x114109c44
classMethod – thread C, local int number’s address is: 0x11418cc44
classMethod – thread F, local int number’s address is: 0x114315c44
classMethod – thread E, local int number’s address is: 0x114292c44
non-static variables are not allowed
If we were to declare an int number in our class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@interface SomeClass : NSObject { int number; } @property(nonatomic, assign) int number; @end @implementation @syntehsize number; ... ... @end |
and you try to use it in your static method, you’d get an error. Because only static variables are used in static methods. Its not intuitive or possible for a class method to keep track of instantiated variables. It simply does not work this way.
the variable ‘number’ is connected to an instantiation (object) of SomeClass.
No static class variables
You cannot declare ‘number’ in our previous example to be static because in objective c, there are no class static variables.
However, you CAN declare a static variable globally and use it in SomeClass’s static method. That is the basis of how a lot of singletons are made.
Example Singleton
.h
1 2 3 4 5 6 |
@interface VariableStore : NSObject { //variables } + (id)sharedManager; |
.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
static VariableStore * sharedVariableStore = nil; //global variable @implementation VariableStore //all threads come to this entry point because its a class method +(id)sharedManager { @synchronized(self) //sync for thread access { if(sharedVariableStore == nil){ [[self alloc] init]; } } return sharedVariableStore; } +(id)allocWithZone:(NSZone *)zone { @synchronized(self) { if(sharedVariableStore == nil) { sharedVariableStore = [super allocWithZone:zone]; return sharedVariableStore; } } return nil; } -(id)copyWithZone:(NSZone *)zone { return self; } -(id)retain { return self; } -(unsigned)retainCount { return UINT_MAX; //denotes an object that cannot be released } -(void)release { // never release } -(id)autorelease { return self; } @end |
As you notice we don’t let others copy or allocate our singleton by returning self. ….because there can be only 1 singleton.
Full Example
SomeClass header
1 2 3 4 5 6 7 8 9 10 11 |
@interface SomeClass : NSObject { int number; } @property(nonatomic, assign) int number; +(void)classMethod:(NSString*)threadName; +(void)classMethod2:(NSString*)threadName; @end |
SomeClass implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
@implementation SomeClass @synthesize number; +(void)classMethod:(NSString*)threadName { static int staticNumber; //local static variable int localNumber; // NSLog(@"thread %@, self address is: %p", threadName, self.number); //static method cannot use instance variable sleep(1); NSLog(@"classMethod - thread %@, self address is: %p", threadName, self); sleep(1); NSLog(@"classMethod - thread %@, local static int number's address is: %p", threadName, &staticNumber); sleep(1); NSLog(@"classMethod - thread %@, local int number's address is: %p", threadName, &localNumber); } +(void)classMethod2:(NSString*)threadName { static int staticNumber; //local static variable int localNumber; // NSLog(@"thread %@, self address is: %p", threadName, self.number); //static method cannot use instance variable sleep(1); NSLog(@"classMethod2 - thread %@, self address is: %p", threadName, self); sleep(1); NSLog(@"classMethod2 - thread %@, local static int number's address is: %p", threadName, &staticNumber); sleep(1); NSLog(@"classMethod2 - thread %@, local int number's address is: %p", threadName, &localNumber); } @end |
main – appdelegate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
@implementation AppDelegate -(void)accessClass:(NSString*)threadName { [SomeClass classMethod:threadName]; } -(void)accessClass2:(NSString*)threadName { [SomeClass classMethod2:threadName]; } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [NSThread detachNewThreadSelector:@selector(accessClass:) toTarget:self withObject:@"A"]; [NSThread detachNewThreadSelector:@selector(accessClass:) toTarget:self withObject:@"B"]; [NSThread detachNewThreadSelector:@selector(accessClass:) toTarget:self withObject:@"C"]; [NSThread detachNewThreadSelector:@selector(accessClass2:) toTarget:self withObject:@"D"]; [NSThread detachNewThreadSelector:@selector(accessClass2:) toTarget:self withObject:@"E"]; [NSThread detachNewThreadSelector:@selector(accessClass2:) toTarget:self withObject:@"F"]; } |
Result
classMethod2 – thread D, self address is: 0x107159380
classMethod – thread B, self address is: 0x107159380
classMethod – thread A, self address is: 0x107159380
classMethod – thread C, self address is: 0x107159380
classMethod2 – thread E, self address is: 0x107159380
classMethod2 – thread F, self address is: 0x107159380
classMethod2 – thread D, local static int number’s address is: 0x1071594ec
classMethod – thread B, local static int number’s address is: 0x1071594e8
classMethod – thread C, local static int number’s address is: 0x1071594e8
classMethod – thread A, local static int number’s address is: 0x1071594e8
classMethod2 – thread F, local static int number’s address is: 0x1071594ec
classMethod2 – thread E, local static int number’s address is: 0x1071594ec
classMethod – thread A, local int number’s address is: 0x114086c44
classMethod2 – thread D, local int number’s address is: 0x11420fc44
classMethod – thread B, local int number’s address is: 0x114109c44
classMethod – thread C, local int number’s address is: 0x11418cc44
classMethod2 – thread F, local int number’s address is: 0x114315c44
classMethod2 – thread E, local int number’s address is: 0x114292c44