Designing a thread-safe class

Source: Internet
Author: User
Tags uikit

Turn from: http://ansonzhao.com/blog/2013/11/28/thread-safe-class-design/

Translation from Thread-safe Class design thread-safe Apple Framework

First let's look at the Apple framework. In general, most classes are not thread-safe by default unless you declare them in advance. Some are what we expect, but others are quite interesting.

Even experienced IOS/MAC developers often make the mistake of accessing part of Uikit/appkit in a background thread. The easiest mistake is to assign values to the property in a background thread, such as a picture, because their content is retrieved from the network in the background. Apple's code is optimized for performance, and if you change the property from a different thread, it won't warn you.

For example, a common problem with a picture in this case is that your changes will cause delays. But if two threads set up the picture at the same time, it is likely that your program will crash directly because the currently set picture may be released two times. Because this is related to timing, crashes usually occur when customers are using them, not during the development process.

There are no official tools to find such errors, but there are some tricks to avoid them. The Uikit Main thread guard is a small piece of code that can patch any setneedslayout and setneedsdisplay that invoke UIView, and check whether execution is performed on the main thread before sending the call. Because these two methods are called by many Uikit setters methods (including pictures), this will catch many thread-related errors. While this does not use proprietary APIs, we do not recommend using them in the product program, but preferably in the development process.

Uikit Security is an intentional design decision by Apple. There's not much benefit from the performance aspect of thread safety, it actually slows down a lot of things. In fact, Uikit and the main thread bundle make it easy to write concurrent programs and use Uikit. All you have to do is make sure that Uikit is always invoked on the main thread. Why Uikit is not thread-safe.

Ensuring thread safety on a large framework such as Uikit is a major task that can bring huge costs. Changing a non-atomic property to an atomic property is just a small part of what needs to be changed. Usually you want to change multiple property at once before you can see the result of the change. For this, Apple has to expose a method, like CoreData's performblock: And the Synchronized method performblockandwait:. If you consider most call Uikit classes are about configuration (configuration), make them thread-safe more meaningless.

However, even if the call is not about configuration (configuration) to share internal state, they are not thread-safe. If you have written back to the Dark ages iOS3.2 and previous applications, you must have experienced the Drawinrect:withfont of using nsstring when preparing a background image: Crash at any time. Thankfully, with the advent of iOS4, Apple provides most of the methods and classes for drawing, such as the use of Uicolor and uifont in background threads.

Unfortunately, Apple's documentation currently lacks thread-safe topics. They recommend that only the main thread access, or even the painting method, they do not guarantee thread safety. So reading the version of iOS is always a good idea.

In most cases, the Uikit class should only be used in the main thread of the program. Whether it's a class derived from Uiresponder, or a user interface that involves manipulating your application in any way. deallocate Issue

Another risk for using the Uikit object in the background is "deallocate issues." Apple summed up the problem in TN2109 and proposed a variety of solutions. The problem is that the UI object should be freed in the main thread because some of the objects might change the view hierarchy in Dealloc. As we know, this invocation of Uikit needs to occur on the main thread.

Because it is common to the secondary thread, the operation or block retains the caller, which can be easily faulted and difficult to find and fix. This is also a long-standing bug in afnetworking, just because not many people know the problem, as usual, it is obviously very rare, and it is difficult to reproduce the collapse. It is helpful to consistently use __weak and not access Ivars in asynchronous block operations. Collection Class

Apple has a good overview document, the most common base class for outgoing thread security on iOS and Mac. In general, immutable classes, like Nsarray, are thread-safe, while their mutable variants, like Nsmutablearray, are not. In fact, when you serialize access in a queue, you can use them in different threads. Keep in mind that methods may return variable variants of a collection object, even if they live their return type is immutable. The good thing is to write something like return [array copy] to make sure that the object returned is actually immutable.

Unlike the Java language, the Foundation framework does not provide a thread-safe collection class outside the framework. In fact, this is very reasonable, because in most cases, you want to use your locks at the higher level to avoid too many lock operations. A notable exception is the cache, where a mutable dictionary may hold unchanged data-where Apple adds Nscache to iOS4, which not only locks access, but also clears its contents in low memory.

This means that in your program, this may be a valid case, where a thread-safe variable dictionary can be very lightweight. This is thanks to the cluster (class cluster) solution, which can be easily written. Atomic attributes (properties)

Have you ever wondered how Apple handles atomic settings/gets properties. Now you've probably heard of Spinlocks, semaphores, locks, @synchronized – what Apple uses. Luckily, the objective-c run is public, so we can see what's going on behind the scenes.

A setter method for a non-atomic property might look like this:

1
2
3
4
5
6 7
-(void) Setusername: (NSString *) UserName {
      if (userName!= _username) {
          [userName retain];
          [_username release];
          _username = UserName;
      }

This is a manual retain/release variable, but the code generated by arc looks similar. Let's take a look at this code, obviously when Setusername: being called at the same time is in trouble. We may end up releasing _username two times, which can damage memory and cause bugs that are hard to find.

What happens inside any of the non-manual implementations is that the compiler generates a call to OBJC_SETPROPERTY_NON_GC (ID self, SEL _cmd, ptrdiff_t offset, id newvalue, BOOL Atomic, signed Char shouldcopy). In our example, the invocation argument is like this:

1
2
OBJC_SETPROPERTY_NON_GC (self, _cmd,
  (ptrdiff_t) (&_username)-(ptrdiff_t) (self), userName, No, no);

ptrdiff_t You might look weird, but ultimately it's a simple pointer algorithm, because a objective-c class is just another C structure.

Objc_setproperty calls the following method:

1
2
3
4
5
6
7 8 9 (
28) (a)
static inline void Reallysetproperty (id self, SEL _cmd, id newvalue,
  ptrdiff_t offset, BOOL atomic, BOOL copy, BOOL M Utablecopy)
{
    id oldValue;
    ID *slot = (id*) ((char*) self + offset);
    if (copy) {
        newvalue = [NewValue copywithzone:null];
    } else if (mutablecopy) {
        newvalue = [NewValue mutable Copywithzone:null];
    } else {
        if (*slot = = newvalue) return;
        NewValue = Objc_retain (NewValue);
    }
    if (!atomic) {
        oldValue = *slot;
        *slot = newvalue;
    } else {
        spin_lock_t *slotlock = &propertylocks[goodhash (slot)];
        _spin_lock (slotlock);
        OldValue = *slot;
        *slot = newvalue;
        _spin_unlock (Slotlock);
    }
    Objc_release (OldValue);
}

In addition to pretty interesting names, this approach is actually fairly simple and uses 128 of the spinlocks available in Propertylocks. This is a pragmatic and fast solution – at worst, because of a hash conflict, a setter has to wait for an unrelated setter to end.

Although these methods are not declared in any common header file, they can be invoked manually. I'm not saying that's a good idea, but if you want atomic properties and want to implement the setter at the same time, it's interesting to know and it can be quite useful.

1
2
3
4
5
6 7 8 9 10
Manually declare runtime methods.

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.