iOS面试总结

OC语言之属性关键字

2019-06-14  本文已影响0人  Jimmy_L_Wang

属性关键字

读写权限

原子性

OC中的属性可以修饰成nonatomic和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);
}

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的区别有哪些?

copy

copy关键字.png

深拷贝与浅拷贝

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

  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的实现原理是怎样的?

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

上一篇下一篇

猜你喜欢

热点阅读