IOS: Application Thread Security

Source: Internet
Author: User
This article describes the thread security of the iOS app's objective-C code. First, we will briefly introduce the basic knowledge of thread security. Then, we will use a small example to observe non-thread security code. Finally, we will briefly introduce a tool that can be used to analyze thread security risks.

1) threading Basics)

When an application is started, IOS will create a process and a memory allocated for it. To put it simply, the memory of an application process consists of three parts: (for more details, see here ):
Program memory (Program memory) Stores the Execution Code of an application. During execution, an instruction pointer (IP) is used to track the execution position of the program. Heap Storage [...
Alloc] init.

Stack is used for function calls. Stores the local variables of parameters and functions.

An application process has a main thread by default. If multiple threads exist, all threads share the program.
Memory and heap,
Each thread has its own IP address and stack. That is to say, each thread has its own execution flow. When it calls a method, other threads cannot access the call parameters and local variables of the method. The objects created on the heap can be accessed and used by other threads.

2) Experiment (experiment)

Create a small program using the following code:

@ 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 (@ "% d (% @)", self. value, [nsthread currentthread]);} @ end

This class has an integer attribute value and will be continuously increased by 100000 times in the doit method. After execution, output its value and the thread information that calls the doit method. Add a _ startexperiment method to appdelegate and call it in application: didfinishlaunchingwithoptions: method:

 - (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, the result is very simple and the value is 99999.

3) thread safety (thread safety)

Execute doit () in parallel with multiple threads as follows ():

-(Void) _ startexperiment {fooclass * Foo = [[fooclass alloc] init]; dispatch_queue_t queue = dispatch_get_global_queue (queue, 0); For (INT I = 0; I <4; ++ I) {// four threads dispatch_async (queue, ^ {[Foo doit] ;}) ;} [Foo release];}

Then, your output may be similar to the following results: (the actual results may be different ):

 after execution: 19851 (NSThread: 0x6b29bd0>{name = (null), num = 3})   after execution: 91396 (NSThread: 0x6b298f0>{name = (null), num = 4})   after execution: 99999 (NSThread: 0x6a288a0>{name = (null), num = 5})   after execution: 99999 (NSThread: 0x6b2a6f0>{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 security means that the code runs in a multi-threaded environment and runs in a single-threaded environment.

What causes this behavior? As mentioned above, each thread has its own IP address and stack, but shares heap ). In this example, fooclass is created on the stack and can be used by all threads. The conflict between the two threads when executing the doit method: thread 1 and thread 2 are being executed at different locations. Doit () does not protect the execution of multiple threads. Its implementation is non-thread-safe. One way to change doit () to thread-safe is to use the following direve VE in the body of its function: @ synchronized the new Code is as follows:

- (void)doIt {     @synchronized(self) {       self.value = 0;        for (int i = 0; i < 100000; ++i) {         self.value = i;       }       NSLog(@"after execution: %d (%@)", self.value, [NSThread currentThread]);          }   } 

Use the @ synchronized indicator,
Each thread uses self in the doit () mutex. However, in the current Code, @ synchronized wraps the entire function body and cannot achieve parallel execution.
Another synchronous access mechanism is to use GCD: Grand
Central Dispatch (GCD ).

4) how to identify non-thread-safe code (how to identify not thread safe code)

The above example is too simple. In reality, the code that took time to write often encounters deadlocks, crashes, or problems that cannot be reproduced. In short, it is different from the expected behavior.

The main cause of thread problems is shared or global state data. Multiple objects access a global variable, share a common object in the heap, or write data to a common bucket. In the previous example, the shared status is self, and the corresponding access is self. value. The example shows much simpler than the actual situation. In fact, the shared or global status (share
Or Global State) is not easy.

The solution is to write a tool that is identified by a function called by multiple threads. The following are the core concepts of this tool.
The tool mainly contains four types: multithreadinganalysis instances are used to record a thread's call to a method, threadingtrace and methodexecution classes are used to output the analysis results sorted by multithreadinganalysis, the multithreadinganalysishook class is used to hook an object and trace all the methods it calls.

The multithreadinganalysis class provides two methods:

  • Recordcalltomethod: ofclass: onthread: records that a method is called on a thread.
  • Threadingtraceoflastapplicationrun must be called after the analysis is complete.
 @interface MultiThreadingAnalysis : NSObject           - (void)recordCallToMethod:(NSString*)methodName                  ofClass:(NSString*)className                 onThread:(NSString*)threadID;                    - (ThreadingTrace*) threadingTraceOfLastApplicationRun;               @end  

The analysis result is processed by threadingtrace.
It contains a group of methodexecution instances, each representing a thread's call to a method:

 /*    * An instance of this class captures    * which methods of which classes have been    * called on which threads.    */   @interface ThreadingTrace : NSObject        /*         * Set of MethodExecution         */        @property (nonatomic, readonly) NSSet *methodExecutions;        - (void)addMethodExecution:(MethodExecution*)methodExec;   @end      /*    * An instance of this class represents a call    * to a method of a specific class on a thread    * with a specific threadID.    */   @interface MethodExecution : NSObject        @property (nonatomic, retain) NSString *methodName;        @property (nonatomic, retain) NSString *className;        @property (nonatomic, retain) NSString *threadID;   @end  

To record method calls as much as possible, I used nsproxy to hook calls to all methods of an object. The multithreadinganalysishook class inherits from nsproxy and calls the target object in forwardinvocation: Method parsing.
Before relocating to the target object, a multithreadinganalysis instance is used to record this call.

 
@interface MultiThreadingAnalysisHook : NSProxy        @property (nonatomic, retain) id target;        @property (nonatomic, retain) MultiThreadingAnalysis *analysis;   @end      @implementation MultiThreadingAnalysisHook      -(void)forwardInvocation:(NSInvocation*)anInvocation {          [self.analysis recordCallToMethod:NSStringFromSelector([anInvocation selector])                      ofClass:NSStringFromClass([self.target class])                  onThread:[NSString stringWithFormat:@"%d", [NSThread currentThread]]];          [anInvocation invokeWithTarget:self.target];   }   @end

Now you can use it. Create a private method _ withthreadinganalysis in the class you want to analyze. This method creates a multithreadinganalysishook instance and points the target to self. Call _ withthreadinganalysis in the self-designated initialization function and return the result (hook action ). In this way, the self of the original object is encapsulated using the multithreadinganalysishook instance, and the calls of all external objects can be recorded.

@implementation YourClass      - (id)init {        //... do init stuff here        return [self _withThreadingAnalysis];   }      - (id)_withThreadingAnalysis {     MultiThreadingAnalysisHook *hook =        [[MultiThreadingAnalysisHook alloc] init];     hook.target = self;     return hook;   }   @end


Then you can call multithreadinganalysis.
The threadingtraceoflastapplicationrun method of to obtain the analysis result. The result is as follows:
Begin threading Analysis for class fooclassMethod doit (_ multithreadaccess _)Method Init (_ singlethreadaccess _)
If a method is called by multiple threads (marked as _ multithreadaccess _), you can see more details.

Address: http://sodecon.blogspot.com/2012/08/ios-multithreading-thread-safety-in-ios.html

Reprinted please indicate the source: http://blog.csdn.net/horkychen

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.