OC语言之属性关键字

属性关键字

读写权限

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

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

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,144评论 1 32
  • 307、setValue:forKey和setObject:forKey的区别是什么? 答:1, setObjec...
    AlanGe阅读 1,587评论 0 1
  • 1.设计模式是什么? 你知道哪些设计模式,并简要叙述?设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型...
    龍飝阅读 2,194评论 0 12
  • 已经回到湛江 最重要的事情还没完成 计划 目标 今天还是没能, 静下心来, 去做一些事。 工作上不要急 做好提前量...
    魔山乐水阅读 33评论 0 0
  • 以下提示为了大家便于查找日课,如果有更好的建议希望大家分享。谢谢! 快速找到今日日课 快速浏览日课 快速查询日课递...
    周洋_图乐园阅读 187评论 0 0