【iOS Sharing】【7】属性相关

本文最后更新于:2021年12月22日 中午

【iOS笔记】系列目录


目录

1、atomic关键字内部使用的是什么锁?

2、@property(copy)NSMutableArray *array这样声明属性会出现什么问题?

3、什么情况使用 weak 关键字,相比 assign 有什么不同?

4、weak属性需要在dealloc中置nil么?

5、ARC下,不显式指定任何属性关键字时,默认的关键字都有哪些?


1、atomic关键字内部使用的是什么锁?

答:

首先了解一些基本概念:

  • 临界区:指的是一块对公共资源进行访问的代码,并非一种机制或是算法。

  • 自旋锁:是用于多线程同步的一种锁,线程反复检查锁变量是否可用。由于线程在这一过程中保持执行,因此是一种忙等待。一旦获取了自旋锁,线程会一直保持该锁,直至显式释放自旋锁。 自旋锁避免了进程上下文的调度开销,因此对于线程只会阻塞很短时间的场合是有效的。

  • 互斥锁(Mutex):是一种用于多线程编程中,防止两条线程同时对同一公共资源(比如全局变量)进行读写的机制。该目的通过将代码切片成一个一个的临界区而达成。

  • 读写锁:是计算机程序的并发控制的一种同步机制,也称“共享-互斥锁”、多读者-单写者锁) 用于解决多线程对公共资源读写问题。读操作可并发重入,写操作是互斥的。 读写锁通常用互斥锁、条件变量、信号量实现。

  • 信号量(semaphore):是一种更高级的同步机制,互斥锁可以说是semaphore在仅取值0/1时的特例。信号量可以有更多的取值空间,用来实现更加复杂的同步,而不单单是线程间互斥。

  • 条件锁:就是条件变量,当进程的某些资源要求不满足时就进入休眠,也就是锁住了。当资源被分配到了,条件锁打开,进程继续运行。

  • 死锁:指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去,这些永远在互相等待的进程称为死锁进程。

  • 轮询(Polling):一种CPU决策如何提供周边设备服务的方式,又称“程控输出入”。轮询法的概念是,由CPU定时发出询问,依序询问每一个周边设备是否需要其服务,有即给予服务,服务结束后再问下一个周边,接着不断周而复始。

锁的类型:

  • 互斥锁

    • NSLock
    • pthread_mutex
    • pthread_mutex(recursive)递归锁
    • @synchronized
  • 自旋锁

    • OSSpinLock
    • os_unfair_lock
  • 读写锁

    • pthread_rwlock
  • 递归锁

    • NSRecursiveLock

    • pthread_mutex(recursive)(见上)

  • 条件锁

    • NSCondition
    • NSConditionLock
  • 信号量

    • dispatch_semaphore

time

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//10000000
OSSpinLock: 112.38 ms
dispatch_semaphore: 160.37 ms
os_unfair_lock: 208.87 ms
pthread_mutex: 302.07 ms
NSCondition: 320.11 ms
NSLock: 331.80 ms
pthread_rwlock: 360.81 ms
pthread_mutex(recursive): 512.17 ms
NSRecursiveLock: 667.55 ms
NSConditionLock: 999.91 ms
@synchronized: 1654.92 ms

//1000
OSSpinLock: 0.02 ms
dispatch_semaphore: 0.03 ms
os_unfair_lock: 0.04 ms
pthread_mutex: 0.06 ms
NSLock: 0.06 ms
pthread_rwlock: 0.07 ms
NSCondition: 0.07 ms
pthread_mutex(recursive): 0.09 ms
NSRecursiveLock: 0.12 ms
NSConditionLock: 0.18 ms
@synchronized: 0.33 ms

atomic

atomic使用的是自旋锁,主要用于赋值操作等轻量操作(散列表,引用计数,弱引用指针赋值),而互斥锁一般都是锁线程,比如单例。


OC method简单介绍

1
2
3
4
5
6
typedef struct objc_method *Method;
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE; //方法名
char *method_types OBJC2_UNAVAILABLE; //方法类型
IMP method_imp OBJC2_UNAVAILABLE; //方法实现
}

objc_method 存储了方法名,方法类型和方法实现。

SEL 方法名类型

方法类型 method_types 是个 char 指针,存储方法的参数类型和返回值类型
method_imp 指向了方法的实现,本质是一个函数指针
Ivar
Ivar 是表示成员变量的类型。

1
2
3
4
5
6
7
8
9
typedef struct objc_ivar *Ivar;
struct objc_ivar {
char *ivar_name OBJC2_UNAVAILABLE;
char *ivar_type OBJC2_UNAVAILABLE;
int ivar_offset OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
}

其中 ivar_offset 是基地址偏移字节

IMP

IMP在objc.h中的定义是:

1
typedef id (*IMP)(id, SEL, ...);

它就是一个函数指针,这是由编译器生成的。当你发起一个 ObjC 消息之后,最终它会执行的那段代码,就是由这个函数指针指定的,而 IMP 这个函数指针就指向了这个方法的实现。


2、@property(copy)NSMutableArray *array这样声明属性会出现什么问题?

答:
  • NSMutableArray经过copy修饰后是NSArray(不可变数组)。如果对经copy修饰后的可变数组进行增删改的操作,实际上是在操作不可变数组,从而会引起程序异常,引起Crash。
  • 不写原子性修饰词默认使用atomic,而atomic性能比nonatomic差很多。

扩展:

浅拷贝:指针拷贝,不生成新对象

  • 不可变对象的不可变拷贝
1
2
3
4
5
6
7
8
NSArray *array = [NSArray array];

//相同地址
NSLog(@"%p",array);
NSLog(@"%p",[array copy]);

// __NSArray0 (不可变数组)
NSLog(@"%@",NSStringFromClass([[array copy] class]));

输出:
在这里插入图片描述


深拷贝:生成一个新对象,对象内容相同( 除浅拷贝那种情况,其他都是深拷贝)

  • 可变对象的可变拷贝
1
2
3
4
5
6
7
8
NSMutableArray *mutArray = [NSMutableArray array];

//输出不同地址
NSLog(@"%p",mutArray);
NSLog(@"%p",[mutArray mutableCopy]);

// __NSArrayM (可变数组)
NSLog(@"%@",NSStringFromClass([[mutArray mutableCopy] class]));

输出:
在这里插入图片描述

  • 可变对象的不可变拷贝

    1
    2
    3
    4
    5
    6
    7
    NSMutableArray *mutArray = [NSMutableArray array];
    //不同地址
    NSLog(@"%p",mutArray);
    NSLog(@"%p",[mutArray copy]);

    // __NSArray0
    NSLog(@"%@",NSStringFromClass([[mutArray copy] class]));

    输出:
    在这里插入图片描述

  • 不可变对象的可变拷贝

1
2
3
4
5
6
7
NSArray *array = [NSArray array];

//不同对象
NSLog(@"%p",array);
NSLog(@"%p",[array mutableCopy]);
// __NSArrayM
NSLog(@"%@",NSStringFromClass([[array mutableCopy] class]));

输出:
在这里插入图片描述


总结:

除不可变对象的不可变拷贝为浅拷贝,其余都是深拷贝
在这里插入图片描述


3、什么情况使用 weak 关键字,相比 assign 有什么不同?

(1)、什么情况下使用weak:

  • 在ARC中修饰代理
  • 使用@IBOutlet连接控件
  • 当block会造成循环引用

(2)、与assign的不同

assign可以用于非对象类型,而weak必须用于对象类型


进阶

区别

  • 修饰变量类型的区别

    weak 只可以修饰对象。如果修饰基本数据类型,编译器会报错-“Property with ‘weak’ attribute must be of object type”。
    assign 可修饰对象,和基本数据类型。当需要修饰对象类型时,MRC时代使用unsafe_unretained。当然,unsafe_unretained也可能产生野指针,所以它名字是”unsafe_”。
  • 是否产生野指针的区别

    weak 不会产生野指针问题。因为weak修饰的对象释放后(引用计数器值为0),指针会自动被置nil,之后再向该对象发消息也不会崩溃。 weak是安全的。
    assign 如果修饰对象,会产生野指针问题;如果修饰基本数据类型则是安全的。修饰的对象释放后,指针不会自动被置空,此时向对象发消息会崩溃。

相似

都可以修饰对象类型,但是assign修饰对象会存在问题。

总结

assign 适用于基本数据类型如int,float,struct等值类型,不适用于引用类型。因为值类型会被放入栈中,遵循先进后出原则,由系统负责管理栈内存。而引用类型会被放入堆中,需要我们自己手动管理内存或通过ARC管理。

weak 适用于delegate和block等引用类型,不会导致野指针问题,也不会循环引用,比较安全。

4、weak属性需要在dealloc中置nil么?

在ARC中,无论是strong修饰的对象还是weak修饰的对象,都不再需要在dealloc中将对象置为nil,编译器会自动帮我们处理,即使编译器不帮助我们处理,在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。

5、ARC下,不显式指定任何属性关键字时,默认的关键字都有哪些?

  • 原子性 – atomic
  • 读写权限 – readwrite
  • 内存管理 – 基础数据类型(assign),对象(strong)

联系方式

邮箱: xiebangyao_1994@163.com

相关账号: