Atomic Properties

atomic的作用只是给getter和setter加了个锁,atomic只能保证代码进入getter或者setter函数内部时是安全的,一旦出了getter和setter,线程就不再是安全的了 这时候处理线程安全 就得靠自己加锁了

http://liuduo.me/2018/02/08/objective-c-atomic/

Ever wondered how Apple is handling atomic setting/getting of properties? By now you have likely heard about spinlocks, semaphores, locks, @synchronized - so what’s Apple using? Thankfully, the Objective-C runtime is public, so we can take a look behind the curtain.

A nonatomic property setter might look like this:

- (void)setUserName:(NSString *)userName {
      if (userName != _userName) {
          [userName retain];
          [_userName release];
          _userName = userName;
      }
}

This is the variant with manual retain/release; however, the ARC-generated code looks similar. When we look at this code it’s obvious why this means trouble when setUserName: is called concurrently. We could end up releasing _userNametwice, which can corrupt memory and lead to hard-to-find bugs.

What’s happening internally for any property that’s not manually implemented 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 call parameters would look like this: 

objc_setProperty_non_gc(self, _cmd, 
  (ptrdiff_t)(&_userName) - (ptrdiff_t)(self), userName, NO, NO);`

The ptrdiff_t might look weird to you, but in the end it’s simple pointer arithmetic, since an Objective-C class is just another C struct.

objc_setProperty calls down to following method:

static inline void reallySetProperty(id self, SEL _cmd, id newValue, 
  ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) 
{
    id oldValue;
    id *slot = (id*) ((char*)self + offset);

    if (copy) {
        newValue = [newValue copyWithZone:NULL];
    } else if (mutableCopy) {
        newValue = [newValue mutableCopyWithZone: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);
}

Aside from the rather funny name, this method is actually fairly straightforward and uses one of the 128 available spinlocks in PropertyLocks. This is a pragmatic and fast approach – the worst case scenario is that a setter might have to wait for an unrelated setter to finish because of a hash collision. 

While those methods aren’t declared in any public header, it is possible to call them manually. I’m not saying this is a good idea, but it’s interesting to know and could be quite useful if you want atomic properties and to implement the setter at the same time.

// Manually declare runtime methods.
extern void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, 
  id newValue, BOOL atomic, BOOL shouldCopy);
extern id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, 
  BOOL atomic);

#define PSTAtomicRetainedSet(dest, src) objc_setProperty(self, _cmd, 
  (ptrdiff_t)(&dest) - (ptrdiff_t)(self), src, YES, NO) 
#define PSTAtomicAutoreleasedGet(src) objc_getProperty(self, _cmd, 
  (ptrdiff_t)(&src) - (ptrdiff_t)(self), YES)

Refer to this gist for the full snippet including code to handle structs. But keep in mind that we don’t recommend using this.

https://www.objc.io/issues/2-concurrency/thread-safe-class-design/#the-deallocation-problem

atomic (Default)

As the word explains itself, a single thread will be able to access the property at a given time. To explain more, only one thread will be able to access getter/setter of a property. Other threads have to wait until first thread releases get/set method. Let’s say we have a property firstName which is atomic.

- Thread A => obj.firstName=@"A"
- Thread B => obj.firstName=@"B"
- Thread C => NSLog("First Name: %@",obj.firstName);

It will ensure that the value of property remains consistent throughout the lifecycle. By default all properties are atomic.

Example atomic property:

@property (strong,atomic) NSString *firstName;
OR
@property (strong) NSString *firstName;

Sample getter setter: This is how the methods of a property will look for an atomic property after

@synthesize firstName = _firstName;
-(NSString *)firstName{
    @synchronized (self) {
        return _firstName;
    }
}
-(void)setFirstName:(NSString *)firstName{
    @synchronized (self) {
        if(_firstName != firstName){
            [_firstName release];
            _firstName = [firstName retain];
        }
    }
}

Note: the Objective-C 2.0 specification, mentions that locks are used internally, but it doesn’t specify exactly how. What you see above, is roughly what an atomic getter/setter would look like, but it might not be accurate.

Pros: Ensures that user gets a valid value and not some garbage

Cons: Slow, as it has code to ensure read write safety

https://nabeelarif.github.io/post/atomic-vs-nonatomic/

原文地址:https://www.cnblogs.com/feng9exe/p/13799968.html