程序员社区

属性标识符

1、@property、@synthesize、@dynamic

@synthesize:作用于在@implementation内部,用@synthesize声明的属性在编译的时候会自动为该属性按照固有规则生成相应的getter setter方法。如果有手动生成getter setter方法也不会报错。

//array是声明的属性名称,_array是编译器自动生成的成员变量。
@synthesize array = _array;

@dynamic:作用于在@implementation内部,与@synthesize不同,使用@dynamic声明时相当于告诉编译器getter setter方法由用户自己生成。如果声明为@dynamic而没有手动生成getter setter方法编译的时候不报错,但是在运行时如果使用.语法去调用该属性时会崩溃。之所以在运行时才会发生崩溃是因为OC具有动态绑定特性。只有在运行时才会去确认具体的调用方法。

@interface TestObject()
{
   NSMutableDictionary *_dic;
}
@property (nonatomic, strong) NSMutableDictionary *dic;
@end
//不同于@ synthesize, @dynamic只需要声明属性名称即可,不需要dic = _dic把值赋值过去。但是需要自己在@interface中声明成员变量。
@dynamic dic;

@property:相对于@dynamic 和 @synthesize ,@property声明的作用区域在@interface内部。 它会告诉编译器自动生成getter setter方法。也允许用户手动生成getter setter中的一个方法,用@property声明的属性不能手动同时写getter setter方法,否则编译器会报错。@property更好的声明属性变量。因为访问方法的命名约定,可以很清晰的看出getter和setter的用处,会传递一些额外信息,后面会跟相应的各种信息例如:@property (nonatomic, strong,onlyread) NSString *name;大多数时候都用的@property声明

@interface TestObject()
{
   NSMutableDictionary *_dic;
}
//因为使用了@property声明,编译器会自动生成相应getter setter方法。
//使用@property不能手动同时生成getter  setter方法,编译器会报错
//nonatomic表示属性是非原子操作,strong表示强引用,
//readonly表示该属性权限为仅读,那么编译器只会生成getter方法,不会生成setter方法
@property (nonatomic, strong,readonly) NSString *name;

@property (nonatomic, strong) NSMutableArray *array;

@property (nonatomic, strong) NSMutableDictionary *dic;

@end
@implementation TestObject

//如果上面的array使用的@property声明,而用户又要手动同时生成getter  setter方法
//可以使用@synthesize 告诉编译器 该属性getter setter方法如果没有手动声明就自动创建,有就不自动生成。
@synthesize array = _array;

//如果dic用@property声明过了,会自动生成getter  setter方法。但是又不希望它自动生成getter setter方法。
//可以用@dynamic 声明。告诉编译器 该属性的getter  setter方法不自动生成
//但如果要自己生成getter setter必须在@ interface内部声明对应的成员变量
@dynamic dic;


- (void)setArray:(NSMutableArray *)array
{
    _array = array;
}

- (NSMutableArray *)array
{
    if (!_array) {
        _array = [NSMutableArray new];
    }
    return _array;
}
- (void)setDictionary:(NSMutableDictionary *)dic
{
    _dic = dic;
}

- (NSMutableDictionary *)dic
{
    if (!_dic) {
        _dic = [NSMutableDictionary new];
    }
    return _dic;
}

@end

2、nonatomic、atomic

nonatomic(非原子性):在调用用nonatomic声明的对象属性时是非线程安全性的。最为直观的就是NSMutableArray的使用。当同时在子线程去增删数组元素,在主线程中去遍历数组元素就会出现数组越界或者数组没有遍历完。因为采用的nonatomic,不同操作可以同时执行,而不需要等前面的操作完成后在进行下一步操作。所以称之为非线程安全。非原子性的执行效率更高不会阻塞线程

atomic(原子性):相反与非原子性,atomic是具有线程安全性的。当属性声明为atomic时,调用该属性的getter/setter方法,会加上同步锁,保证同一时刻只能有一个线程调用属性的getter/setter方法。但是atomic只能保证线程的读和写过程是可靠的。但并不能保证数据一定是可靠的。例如:有线程A和线程B的情况下,从调度顺序来说,线程A先调用了setter方法修改了属性值,然后线程B也调用了setter方法再次修改了属性值,最后线程A调用getter方法获取属性值时,获取到的结果值可能是线程A修改的值,也可能是线程B修改的值,因为A、B是两个线程异步的,最后A调用getter获取的值,取决于B线程是已经设置成功还是B线程还没有执行setter方法。再比如说:当一个线程正在get或set时,又有另一个线程同时在进行release操作,可能会直接crash。

如何实现属性的atomic?

- (void)setCurrentImage:(UIImage *)currentImage
{
    @synchronized(self) {
        if (_currentImage != currentImage) {
            [_currentImage release];
            _currentImage = [currentImage retain];
            // do something
        }
    }
}

- (UIImage *)currentImage
{
    @synchronized(self) {
        return _currentImage;
    }
}

3、strong、weak、retain、assgin、copy、unsafe_unretained

retain:释放旧对象,提高输入对象的引用计数+1,将旧对象的赋予输入对象,只能用户声明OC对象

@property (nonatomic, retain) Room *room;
- (void)setRoom:(Room *)room // room = r
{
    // 只有房间不同才需用release和retain
    if (_room != room) {  
        // 将以前的房间释放掉 -1,将旧对象释放
        [_room release];

        // MRC中需要手动对房间的引用计数器+1
        [room retain];

        _room = room;
    }
}

strong:强引用,它是ARC特有。在MRC时代没有,相当于retain。由于MRC时代是靠引用计数器来管理对象什么时候被销毁所以用retain,而ARC时代管理对象的销毁是有系统自动判断,判断的依据就是该对象是否有强引用对象。如果对象没有被任何地方强引用就会被销毁。所以在ARC时代基本都用的strong来声明代替了retain。用于声明OC对象(ARC特有)
苹果官网对strong的解释代码:

Precondition:object is a valid pointer to a __strong object which is adequately aligned for a pointer. value is null or a pointer to a valid object.

Performs the complete sequence for assigning to a __strong object of non-block type [[*]]. Equivalent to the following code:

void objc_storeStrong(id *object, id value) {
  id oldValue = *object;
  value = [value retain];
  *object = value;
  [oldValue release];
}

assgin:简单的赋值操作,不会更改引用计数,用于基本的数据类型声明。

weak:弱引用,表示该属性是一种“非拥有关系”。为这种属性设置新值时既不会保留新值也不会释放旧值,类似于assgin。 然而在对象被摧毁时,属性也会被清空(nil out)。这样可以有效的防止崩溃(因为OC中给没有对象地址的指针发送消息不会崩溃,而给有内存地址但地址中是空对象的指针发消息会崩溃,野指针),该声明必须作用于OC对象。
对于 weak 对象会放入一个 hash 表中, 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会调用 dealloc, 使用key在weak 表中搜索,将找到的所有对象设置为 nil。
(ARC特有),strong 和 weak的指针,根本区别在于,strong执行了retain操作,而weak没有。

runtime 如何实现 weak 变量的自动置 nil?(weak底层原理)
runtime 对注册的类会进行内存布局,Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,key是所指对象的地址,value是weak指针的地址(这个地址的值是所指对象的地址)数组。
1.初始化时:runtime 会调用 objc_initWeak 函数,初始化一个新的 weak 指针指向对象的地址。
2.添加引用时:objc_initWeak 函数会调用 objc_storeWeak() 函数, objc_storeWeak()的作用是更新指针指向,创建对应的弱引用表。
3.释放时,调用 clearDeallocating 函数。clearDeallocating 函数首先根据对象地址获取所有weak 指针地址的数组,然后遍历这个数组把其中的数据设为 nil,最后把这个 entry 从 weak 表中删除,最后清理对象的记录。

copy:不同于其他声明,copy会根据声明的属性是否是可变类型而进行不同操作。如果对象是一个不可变对象,例如NSArray NSString 等,那么copy等同于retain、strong。如果对象是一个可变对象,例如:NSMutableArray,NSMutableString等,它会在内存中重新开辟了一个新的内存空间,用来 存储新的对象,和原来的对象是两个不同的地址,引用计数分别为1. 这就是所谓的深拷贝浅拷贝,浅拷贝只是copy了对象的内存地址,而深拷贝是重新在内存中开辟新空间,新空间对象值与拷贝对象的值一样。但是是完全不同的2个内存地址。 例如copy修饰的类型为 NSString不可变对象时,copy可以保护其封装性,当赋值对象是一个 NSMutableString 类时(NSMutableString是 NSString 的子类,表示一种可修改其值的字符串),此时若是不用copy修饰拷贝字符串,那么赋值该对象之后,赋值对象字符串的值就可能会在其他地方被修改,修改后赋值后对象也会改变,造成值不对。所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。

1.当不可变对象进行copy的时候,本质上只是创建了一个新的指针,指向了copy对象的内存空间。所以新指针与原指针所指向的地址相同
2.当可变对象进行copy操作的时候,由于copy方法返回的对象都是不可变对象,所以对可变对象进行copy操作相当于单层深拷贝,创建了一个新指针,并重新再内存中分配了一个新的对象,但是这个对象是不可变对象,而非原对象的可变对象。所以这是一种单层的深拷贝。
3.不可变对象进行mutableCopy的时候,本质上是进行了深拷贝,创建了新指针,并深拷贝重新开辟了一个新的内存空间放置拷贝的对象。所以指针和指针所指向的内存地址与原对象都不一样。并且新的内存中实际对象是一个可变对象。
4.可变对象进行mutableCopy与不可变对象进行mutableCopy本质是一样的。
5.copy一个可变对象并赋值给一个不可变对象,由于copy方法返回的是不可变对象,因此会对原可变对象进行深拷贝,新的内存地址,新的指针。并且copy的结果是一个不可变对象
6.copy一个不可变对象并复制给一个可变对象,同理与上面5的解答,虽然最终赋值给一个可变的指针,但是内存地址中实际是一个不可变对象。
7.mutableCopy一个不可变对象,并赋值给一个可变对象。对原对象进行深拷贝,由于mutableCopy方法返回的是一个可变对。所以实际内存中是一个可变对象

unsafe_unretained:和weak 差不多,唯一的区别便是,对象即使被销毁,指针也不会自动置空, 对象被销毁后指针指向的是一个无用的内存地址(野地址)。如果对象销毁后后还使用此指针,程序会抛出 BAD_ACCESS 的异常。 所以一般不使用unsafe_unretained。(ARC特有)

4、readOnly、readWrite、getter=、setter=

readOnly表示属性仅能读不能设置其值。告诉编译器只生成getter方法不生成setter方法。

readWrite默认值,表示属性可读可写。编译器会自动生成getter setter方法

getter=指定属性gettter方法的方法名

@property (nonatomic, strong, getter=getMyDic) NSMutableDictionary *dic;

setter=指定属性setter方法的方法名

@property (nonatomic, strong,setter=myArray:) NSMutableArray *arr;

5、__unsafe_unretained、__weak、__strong、__unsafe_unretained

NSMutableArray __unsafe_unretained *array = [[NSMutableArray alloc]init];
[array addObject:@"123"];

__unsafe_unretained:使用__unsafe_unretained修饰符的变量与使用__weak修饰符的变量一样,因为自己生成并持有的对象不能继续为自己持有,所以生成的对象会立即被释放。也就是说在执行完init方法以后,对象指针所指向的内存就已经释放掉了,但因为用的__unsafe_unretained修饰指针并不像__weak的指针那样,将指针自动置为nil,它依然指向原来的地址,可是这块地址的内存已经被系统回收了,再访问就是非法的,也就是野指针,再执行后面的addObject方法自然会出错了。

__weak:主要用于解决循环引用,用__weak修饰的变量 当对象释放后,指针自动设置为nil,当后面继续使用该指针变量的时候不会造成crash,更不会造成强引用使该释放的对象无法释放,造成内存泄露。

__weak typeof(self) weakSelf = self;

__strong:相反于__weak,主要用于当使用某个对象时,希望它没有提前被释放。强引用该对象使其无法释放。例如在block内部,希望block调用时该对象不会被提前释放造成错误。可以使用强引用。

TestAlertView *alertView = [TestAlertView new];
alertView = ^()
{
  //当block内部需要使用本身这个局部对象时,需要用强引用方式,让alertView在传递完block后不会被释放依然可以执行setTitle操作
   __strong typeof(alertView) strongAlertView = alertView;
  [strongAlertView setTitle:@"1234"];

}
[alertView show];
赞(0) 打赏
未经允许不得转载:IDEA激活码 » 属性标识符

一个分享Java & Python知识的社区