This article explains the thread safety of OBJECTIVE-C code for iOS applications. First, a brief introduction to thread-safety basics, followed by a small example of non-thread-safe code, and finally a little bit about a tool that can be used to analyze thread-safety vulnerabilities.
1) Basic knowledge (Threading Basics)
When you start an application, iOS corresponds to creating a process and a piece of memory allocated to it. Simply put, the memory of an application process consists of three parts: (a more detailed description can be seen here):
Programmed memory (program memory) stores the execution code of an application, which is executed by an instruction pointer (instruction pointer, IP) to track the execution location of the program.
Heap (heap) stores objects that are created by [... alloc] init].
Stacks (stack) are used for function calls. A local variable that stores parameters and functions.
An application process defaults to a main thread. If there are multiple threads, all threads share program memory and heap, and each thread has its own IP and stack. This means that each thread has its own execution process, and when it calls a method, other threads are unable to access the calling parameters and the local variables of the method. objects created on the heap (heap) can be accessed and used by other threads.
2) Experiment (experiment)
Build a small program that uses the following code:
[CPP] View plain copy print? @interface FooClass {} @property (nonatomic, assign) nsuinteger value; - (void) doit; @end @implementation FooClass @synthesize value; - (void) doit { self.value = 0; for (int i = 0; i < 100000; ++i) { self.value = i; } nslog (@ "executed: %d (%@)", self.value, [nsthread currentthread]); } &nbsP @end
This class has an integer attribute value and is added 100,000 consecutive times in the doit method. After execution, it prints out its value and the thread information that calls the Doit method. Add a _startexperiment method to the Appdelegate and call it in the Application:didfinishlaunchingwithoptions: method:
[CPP]View Plain copy print? -(void) _startexperiment {fooclass *foo = [[Fooclass alloc] init]; [foo doIt]; [foo release]; }-(BOOL) Application: (UIApplication *) application didfinishlaunchingwithoptions: (Nsdictionary *) launchOptions { ... [Self _startexperiment]; return YES; }
Because there are multiple threads here, the result is a simple display value of 99999.
3 Thread Safety (threads Safety)
The following are multithreaded parallel execution doit ():
[CPP]View Plain copy print? -(void) _startexperiment {fooclass *foo = [[Fooclass alloc] init]; dispatch_queue_t queue = Dispatch_get_global_queue (Dispatch_queue_priority_default, 0); for (int i = 0; i < 4; ++i) {//Four threads Dispatch_async (queue, ^{[foo doIt]; }); } [foo release]; }
Again, your output may resemble the following: (actually it may not be the same):
After execution:19851 (nsthread:0x6b29bd0>{name = (null), num = 3}) after
execution:91396 (NSTHREAD:0X6B298F0&G T {name = (null), num = 4})
After execution:99999 (nsthread:0x6a288a0>{name = (null), num = 5}) after
execution:99999 (NSTHREAD:0X6B2A6F0&G T {name = (null), num = 6})
Not every thread's value is 99999. This is because the current code is not thread-safe.
The so-called thread safety is that the code runs in a multithreaded environment and runs in a single thread environment is the same.
What caused this behavior? As mentioned earlier, each thread has its own IP and stack, but it shares the heap (heap). The Fooclass in the example is created on the heap and can be used by all threads. The following illustration shows the conflicts in which two threads are executing the doit method:
Thread 1 and thread 2 are executing in different locations. DoIt () Does not protect multithreaded execution, and its implementation is not thread-safe.
One will doit ()