1.请说明并比较以下关键词:strong, weak, assign, copy, unsafe_unretained
-
strong
: 表示指向并强引用该对象,其修饰的对象引用计数会增加1。该对象只要引用计数不为0则不会被销毁。当然强行将其设为nil
可以销毁它 -
weak
: 表示弱引用对象,指向但不拥有该对象。其修饰的对象引用计数不会增加。当对象消失后指针自动指向nil,所以这里也就防止了野指针的存在。
// 测试 weak 和 strong
@property (nonatomic, strong) Person *strongPerson;
@property (nonatomic, weak) Person *weakPerson;
// 测试代码
self.strongPerson = [Person new];
self.weakPerson = self.strongPerson;
self.strongPerson = nil;
NSLog(@"strongStr = %@, weakStr = %@", self.strongPerson, self.weakPerson);
// 打印结果:说明weak修饰的属性并不会使引用计数增加
strongStr = (null), weakStr = (null)
稍微改一下代码:
self.strongPerson = [Person new];
self.weakPerson = self.strongPerson;
self.weakPerson = nil;
NSLog(@"strongStr = %@, weakStr = %@",self.strongPerson, self.weakPerson);
// 打印结果:说明weak修饰的属性只是对对象的弱引用,并不会真正的持有该对象。
strongStr = <Person: 0x600000007c40>, weakStr = (null)
再次修改代码验证:
Person *p = [Person new];
self.strongPerson = p;
self.weakPerson = self.strongPerson;
p = nil;
NSLog(@"strongStr = %@, weakStr = %@",self.strongPerson, self.weakPerson);
//打印结果:说明strong属性会强引用该对象并使该对象的引用计数+1,
//所以即使把p设置为nil,该对象也并没有释放,要想释放该对象,
//还得把strongStr设置为nil
strongStr = <Person: 0x600000200b40>, weakStr = <Person: 0x600000200b40>
self.strongPerson = nil;
// 打印结果:
strongStr = (null), weakStr = (null)
-
assign
: 主要用于修饰基本数据类型,如NSInteger
和CGFloat
,这些数值主要存在于栈上,统一由系统进行内存管理。 -
weak
和assign
对比:weak
一般用来修饰对象,assign
一般用来修饰基本数据类型。原因是assign
修饰的对象被释放后,指针的地址依然存在,造成野指针,在堆上容易造成崩溃。而栈上的内存系统会自动处理,不会造成野指针。 -
copy
与strong
对比:copy
与strong
类似,不同之处是strong
的复制是多个指针指向同一个地址,而copy
的复制每次会在内存中拷贝一份对象,指针指向不同地址。copy
一般用在修饰有可变对应类型的不可变对象上,如NSString
,NSArray
,NSDictionary
。 -
unsafe_unretained
:跟weak
类似,声明一个弱引用,但是当引用计数为 0 时,变量不会自动设置为nil
,不过现在基本不用,都用weak
了。 -
Objective-C
中,基本数据类型的默认关键字是atomic
,readwrite
,assign
;对象属性的默认关键字是atomic
,readwrite
,strong
。
再来看看copy关键字
// 在ViewController里面加个属性:
@property (nonatomic, copy) NSObject *c;
// 在.m中
NSObject *a = [[NSObject alloc]init];
self.c = a;
a = nil;
NSLog(@"%@",self.c);
毫无疑问,输出结果不为nil:
<NSObject: 0x600000010e80>
- 注意:在使用
NSMutableArray
,NSMutableDictionary
等可变集合对象的时候,千万不要用copy
进行修饰,这里用copy
很可能会出错,因为当你给该属性赋值时,它会自动调用对象的copy
方法,从而将可变集合转换成不可变集合,把一个不可变集合赋值给一个可变集合,再调用addObject
等方法就会报错,因为变成了不可变对象。
2.请说明并比较以下关键词:__weak
,__block
*__weak
与weak
基本相同。前者用于修饰变量(variable
),后者用于修饰属性(property
)。__weak
主要用于防止block
中的循环引用。
-
__block
也用于修饰变量。它是引用修饰,所以其修饰的值是动态变化的,即可以被重新赋值的。__block
用于修饰某些block
内部将要修改的外部变量。 -
__weak
和__block
的使用场景几乎与block
息息相关。而所谓block
,就是Objective-C
对于闭包的实现。闭包就是没有名字的函数,或者理解为指向函数的指针。
3. 请说明并比较以下关键词:atomatic
, nonatomic
-
atomic
修饰的对象会保证setter
和getter
的完整性,任何线程对其访问都可以得到一个完整的初始化后的对象。因为要保证操作完成,所以速度慢。它比nonatomic
安全,但也并不是绝对的线程安全,例如多个线程同时调用set
和get
就会导致获得的对象值不一样。绝对的线程安全就要用关键词synchronized
。 -
nonatomic
修饰的对象不保证setter
和getter
的完整性,所以多个线程对它进行访问,它可能会返回未初始化的对象。正因为如此,它比atomic
快,但也是线程不安全的。
4. 请说明并比较synthesize和@dynamic,分别有什么作用?
我们平时完成属性定义后,编译器自动编写访问这些属性所需的方法,用以访问给定类型中具有给定名称的变量,这个过程叫做“自动合成”( autosynthesis),简单讲就是:@property = getter + setter
@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end
上述代码与下面等效:
@interface Person : NSObject
- (NSString *)firstName;
- (void)setFirstName:(NSString *)firstName;
- (NSString *)lastName;
- (void)setLastName:(NSString *)lastName;
@end
上面的过程由编译器在编译期间帮我们完成,所以编辑器里看不到这些“合成方法”(synthesized method)的源代码。除了生成方法代码 getter
、setter
之外,编译器还要自动向类中添加对应类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字。在前例中,会生成两个实例变量,其名称分别为 _firstName
与_lastName
。当然我们也可以在类的实现代码里通过@synthesize
语法来指定实例变量的名字。
@implementation Person
@synthesize firstName = _myFirstName;
@synthesize lastName = myLastName;
@end
@property
有两个对应的词,一个是@synthesize
,一个是@dynamic
。如果两个都没写,那么默认就是@syntheszie var = _var
;
-
@synthesize
的语义是如果你没有手动实现setter
方法和getter
方法,那么编译器会自动为你加上这两个方法。 -
@
dynamic
告诉编译器:属性的setter
与getter
方法由用户自己实现,不自动生成(对于readonly
的属性只需提供getter
即可; 假如一个属性被声明为@dynamic var
,然后你没有提供@setter
方法和@getter
方法,编译的时候没问题,但是当程序运行到instance.var = someVar
,由于缺setter
方法会导致程序崩溃;或者当运行到someVar = var
时,由于缺getter
方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定
5. 什么是ARC
?
-
ARC
全称是Automatic Reference Counting
,是Objective-C
的内存管理机制。简单地来说,就是代码中自动加入了retain/release
,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。 -
ARC
的使用是为了解决对象retain
和release
匹配的问题。以前手动管理造成内存泄漏或者重复释放的问题将不复存在。 -
以前需要手动的通过
retain
去为对象获取内存,并用release
释放内存。所以以前的操作称为MRC (Manual Reference Counting)
。
网友评论