美文网首页iOS面试总结
OC语言之属性关键字

OC语言之属性关键字

作者: Jimmy_L_Wang | 来源:发表于2019-06-14 22:17 被阅读0次

    属性关键字

    读写权限

    • readonly
    • readwrite(默认)

    原子性

    OC中的属性可以修饰成nonatomic和atomic,即原子和非原子属性。atomic属性设计的出发点是保证多线程下使用属性的安全性,

    • atomic(默认)

    atomic修饰的属性可以保证赋值和获取值(对成员属性的直接获取和赋值,并不代表操作和访问),线程安全。

    假如一个atomic修饰的的数组,对数组进行赋值和获取可以保证线程安全,如果对数组进行操作,比如给数组添加对象或移除对象元素,则不在atomic的负责范围内,没办法保证数组的线程安全。

    使用atomic修饰的属性并不会线程安全

    源码objc4-750.1

    void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset)
    {
        reallySetProperty(self, _cmd, newValue, offset, true, false, false);
    }
    
    static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
    {
        if (offset == 0) {
            object_setClass(self, newValue);
            return;
        }
    
        id oldValue;
        id *slot = (id*) ((char*)self + offset);
    
        if (copy) {
            newValue = [newValue copyWithZone:nil];
        } else if (mutableCopy) {
            newValue = [newValue mutableCopyWithZone:nil];
        } else {
            if (*slot == newValue) return;
            newValue = objc_retain(newValue);
        }
    
        if (!atomic) {
            oldValue = *slot;
            *slot = newValue;
        } else {
            spinlock_t& slotlock = PropertyLocks[slot]; //自旋锁
            slotlock.lock();
            oldValue = *slot;
            *slot = newValue;        
            slotlock.unlock();
        }
    
        objc_release(oldValue);
    }
    

    从使用PropertyLocks数组中的一个给写操作上锁,在赋完值之后进行解锁操作。

    也就是说,atomic只是在 setter方法 中加锁,当多个线程同时写操作时,要进行排队。A线程对属性进行写操作,B线程不可以对该属性进行写操作,只有等A线程访问结束,B线程才能操作。问题在于,当A线程再想对属性进行读操作,读取的是B线程写的数据,这就破坏了线程安全,。如果再有C线程在A线程读操作前Release这个属性,那么程序就会Crash.
    综上,atomic 操作是原子性的,它只保证了属性的setter和getter时的线程安全,并不能保证属性线程安全,atomic的使用只是更好的降低了线程出错的可能性。

    id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
        if (offset == 0) {
            return object_getClass(self);
        }
    
        // Retain release world
        id *slot = (id*) ((char*)self + offset);
        if (!atomic) return *slot;
            
        // Atomic retain release world
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        id value = objc_retain(*slot);
        slotlock.unlock();
        
        // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
        return objc_autoreleaseReturnValue(value);
    }
    
    • nonatomic

    atomic 与 nonatomic 的区别如下:

    1. atomicnonatomic的本质区别其实也就是在setter方法上的操作不同,atomic保证了gettersetter存取方法的线程安全,两者都不能保证整个对象是线程安全的。
    2. nonatomic的速度要比atomic的快。

    鉴于以上两点,大部分使用的是nonatomic这个属性。

    那么问题来了,Apple为什么只给setter和getter方法加锁而不直接使用@synchronized加锁呢?

    看看 synchronized 操作的源码, 简单的一个注解,其实做了许多事情,每个@synchronized()代码块,使用了 objc_sync_enterobjc_sync_exitid2data三个加解锁序列,而这种作法相比使用一个atomic来修饰,只给settergetter方法加锁的方式 在性能上要慢很多。读写操作一个属性时,通常需要很快来完成,atomic的方式要适合这项工作。

    在必要时,可以使用@synchronized,但是在保证多线程操作在发生错误的时候,不会发生dead lock。

    引用计数

    retain/strong(修饰对象的)

    assign/unsafe_unretained(基本数据类型或对象)

    assign与weak的区别有哪些?

    • assign修饰基本数据类型,如int,BOOL

    • assign修饰对象类型时,不改变其引用计数。

    • 会产生悬垂指针。

      assign修饰的对象在被释放之后,其assign指针仍然指向原对象内存地址,这个时候如果通过assign指针继续访问对象,可能会由于悬垂指针的原因导致内存泄漏,或程序异常。

    • weak不改变被修饰对象的引用计数

    • 所指对象在被释放之后会自动置为nil

    • weak之修饰对象

    copy

    copy关键字.png
    • 可变对象的copymutableCopy都是深拷贝。
    • 不可变对象的copy是浅拷贝,mutableCopy是深拷贝。
    • copy方法返回的都是不可变对象;

    深拷贝与浅拷贝

    浅拷贝就是对内存地址的复制,让目标对象指针和源对象指向同一片内存空间。

    1. 会增加对象的引用计数
    2. 没有发生新的内存分配。

    深拷贝让目标对象指针和源对象指针指向两片内容相同的内存空间。

    1. 不会增加对象的引用计数
    2. 产生了新的内存空间分配
    @property(copy) NSMutableArray *array;
    

    如果像上面那样声明一个成员属性会导致什么问题?

    1. 如果赋值过来的是NSMutableArray,copy之后是NSArray

    2. 如果赋值过来的是NSArray,copy之后是NSArray

      所以无论被赋值过来的是NSMutableArray,还是NSArray copy的结果都是不可变对象,由于原来的属性被声明为

      NSMutablArray就不可避免的有调用方调用其进行元素的添加与移除,此时由于copy的结果是NSArray,调用其子类方法的添加与移除会造成程序的崩溃。

    OC语言笔试题

    MRC下如何重写retain修饰变量的setter方法?

    @property(nonatomic, retain) id obj;
    
    - (void)setObj:(id)obj
    {
      if(_obj != obj) { //防止提早释放了对象
        [_obj release];
        _obj = [obj retain];
      }
    }
    

    请简述分类实现原理?

    KVO的实现原理是怎样的?

    能否为分类添加成员变量?

    相关文章

      网友评论

        本文标题:OC语言之属性关键字

        本文链接:https://www.haomeiwen.com/subject/ogvmfctx.html