• 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/

  • 相关阅读:
    月半小夜曲下的畅想--DOCTYPE模式
    css模块化思想(一)--------命名是个技术活
    聊聊css盒子模型
    【随笔】借鉴 & KPI式设计
    【转载】社交的蒸发冷却效应
    【随笔】写在闪电孵化器分享会之后
    【随笔】微信删除加载动画
    【随笔】微信支付有感 续
    【转载】如何把产品做简单
    【随笔】写在2014年的第一天
  • 原文地址:https://www.cnblogs.com/feng9exe/p/13799968.html
Copyright © 2020-2023  润新知