zoukankan      html  css  js  c++  java
  • @property相关问题

    @property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的

    @property 的本质是什么?

    @property = ivar + getter + setter;

    下面解释下:

    “属性” (property)有两大概念:ivar(实例变量)、存取方法(access method = 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		
    

    ivar、getter、setter 是如何生成并添加到这个类中的?

    “自动合成”( autosynthesis)

    完成属性定义后,编译器会自动编写访问这些属性所需的方法,此过程叫做“自动合成”(autosynthesis)。需要强调的是,这个过程由编译 器在编译期执行,所以编辑器里看不到这些“合成方法”(synthesized method)的源代码。除了生成方法代码 getter、setter 之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字。在前例中,会生成两个实例变量,其名称分别为 _firstName 与 _lastName。

    也可以在类的实现代码里通过 @synthesize 语法来指定实例变量的名字.

    @implementation Person
    @synthesize firstName = _myFirstName;
    @synthesize lastName = _myLastName;
    @end	
    

    runtime 如何实现 weak 属性

    要实现 weak 属性,首先要搞清楚 weak 属性的特点:

    weak 此特质表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同 assign 类似, 然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。

    那么 runtime 如何实现 weak 变量的自动置nil?

    runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。


    @property中有哪些属性关键字?/ @property 后面可以有哪些修饰符?

    属性可以拥有的特质分为四类:

    1. 原子性--- nonatomic 特质

      在默认情况下,由编译器合成的方法会通过锁定机制确保其原子性(atomicity)。如果属性具备 nonatomic 特质,则不使用同步锁。请注意,尽管没有名为“atomic”的特质(如果某属性不具备 nonatomic 特质,那它就是“原子的” ( atomic) ),但是仍然可以在属性特质中写明这一点,编译器不会报错。若是自己定义存取方法,那么就应该遵从与属性特质相符的原子性。

    2. 读/写权限---readwrite(读写)、readonly (只读)

    3. 内存管理语义---assign、strong、 weak、unsafe_unretained、copy

    4. 方法名---getter= 、setter=

    getter=的样式:

      @property (nonatomic, getter=isOn) BOOL on;
    

    setter=这种不常用,也不推荐使用。故不在这里给出写法。)

    setter=一般用在特殊的情境下,比如:

    在数据反序列化、转模型的过程中,服务器返回的字段如果以 init 开头,所以你需要定义一个 init 开头的属性,但默认生成的 setter 与 getter 方法也会以 init 开头,而编译器会把所有以 init 开头的方法当成初始化方法,而初始化方法只能返回 self 类型,因此编译器会报错。

    这时你就可以使用下面的方式来避免编译器报错:

    @property(nonatomic, strong, getter=p_initBy, setter=setP_initBy:)NSString *initBy;


    @synthesize和@dynamic分别有什么作用?

    1. @property有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var;

    2. @synthesize 的语义是如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法。

    3. @dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。


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

    1. 对应基本数据类型默认关键字是

    atomic,readwrite,assign

    1. 对于普通的 Objective-C 对象

    atomic,readwrite,strong


    用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?

    1. 因为父类指针可以指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本.
      如果我们使用是 strong ,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性.

    2. copy 此特质所表达的所属关系与 strong 类似。然而设置方法并不保留新值,而是将其“拷贝” (copy)。 当属性类型为 NSString 时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个 NSMutableString 类的实例。这个类是 NSString 的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。

    例如:定义一个以 strong 修饰的 array:

    @property (nonatomic ,readwrite, strong) NSArray *array;
    

    然后进行下面的操作:

    NSMutableArray *mutableArray = [[NSMutableArray alloc] init];
    NSArray *array = @[ @1, @2, @3, @4 ];
    self.array = mutableArray;
    [mutableArray removeAllObjects];;
    NSLog(@"%@",self.array);
    
    [mutableArray addObjectsFromArray:array];
    self.array = [mutableArray copy];
    [mutableArray removeAllObjects];;
    NSLog(@"%@",self.array);
    

    打印结果如下所示:

    2015-09-27 19:10:32.523 CYLArrayCopyDmo[10681:713670] (
    )
    2015-09-27 19:10:32.524 CYLArrayCopyDmo[10681:713670] (
    1,
    2,
    3,
    4
    )
    

    在有了自动合成属性实例变量之后,@synthesize还有哪些使用场景?

    回答这个问题前,我们要搞清楚一个问题,什么情况下不会autosynthesis(自动合成)?

    1. 同时重写了 setter 和 getter 时

    2. 重写了只读属性的 getter 时

    3. 使用了 @dynamic 时

    4. 在 @protocol 中定义的所有属性

    5. 在 category 中定义的所有属性

    6. 重载的属性

      当你在子类中重载了父类中的属性,你必须 使用 @synthesize 来手动合成ivar。

    除了后三条,对其他几个我们可以总结出一个规律:当你想手动管理 @property 的所有内容时,你就会尝试通过实现 @property 的所有“存取方法”(the accessor methods)或者使用 @dynamic 来达到这个目的,这时编译器就会认为你打算手动管理 @property,于是编译器就禁用了 autosynthesis(自动合成)。

    readwrite,readonly,assign,retain,copy,nonatomic 属性的作用

    readwrite,readonly

    • 设置可供访问级别

    assign(默认)

    • setter方法直接赋值,不更改索引计数(即不进行retain操作)
    • 为了解决原类型与环循引用问题
    • 对基础数据类型 (NSInteger)和C数据类型(int, float, double, char, 等)

    retain

    • setter方法对参数进行release旧值再retain新值

    • 释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为1

    • 此属性只能用于Objective-C对象类型,而不能用于Core Foundation对象。(原因很明显,retain会增加对象的引用计数,而基本数据类型或者Core Foundation对象都没有引用计数)。

        注意: 把对象添加到数组中时,引用计数将增加对象的引用次数+1。
      

    assign与retain:

    • 假设你用malloc分配了一块内存,并且把它的地址赋值给了指针a,后来你希望指针b也共享这块内存,于是你又把a赋值给 (assign)了b。此时a和b指向同一块内存,请问当a不再需要这块内存,能否直接释放它?答案是否定的,因为a并不知道b是否还在使用这块内存,如 果a释放了,那么b在使用这块内存的时候会引起程序crash掉。
    • 了解到上面中assign的问题,那么如何解决?最简单的一个方法就是使用引用计数(reference counting),还是上面的那个例子,我们给那块内存设一个引用计数,当内存被分配并且赋值给a时,引用计数是1。当把a赋值给b时引用计数增加到 2。这时如果a不再使用这块内存,它只需要把引用计数减1,表明自己不再拥有这块内存。b不再使用这块内存时也把引用计数减1。当引用计数变为0的时候, 代表该内存不再被任何指针所引用,系统可以把它直接释放掉。
      总结:上面两点其实就是assign和retain的区别,assign就是直接赋值,从而可能引起1中的问题,当数据为int, float等原生类型时,可以使用assign。retain就如2中所述,使用了引用计数,retain引起引用计数加1, release引起引用计数减1,当引用计数为0时,dealloc函数被调用,内存被回收。

    copy

    • 对 NSString

         此属性只对那些实行了NSCopying协议的对象类型有效。 
      
      • 当属性类型为 NSString 时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个 NSMutableString 类的实例。这个类是 NSString 的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。
    • 建立一个索引计数为1的对象,然后释放旧对象

    • setter方法进行Copy操作,与retain处理流程一样,先旧值release,再 Copy出新的对象,retainCount为1。

    • 这是为了减少对上下文的依赖而引入的机制

    • copy与retain的区别:

      • copy是创建一个新对象,retain是创建一个指针,引用对象计数加1。
      • retain 是指针拷贝(指针相同,内容相同),copy 是内容拷贝(指针不同,内容相同)。

    nonatomic,

    • 非原子性访问,不加同步,多线程并发访问会提高性能。注意,如果不加此属性,则默认是两个访问方法都为原子型事务访问。锁被加到所属对象实例级

    atomic(默认)

    • 为setter方法加锁(默认就是atomic)而这种机制是耗费系统资源的,

    • 如果有多个线程同时调用setter的话,不会出现某一个线程执行setter全部语句之前,另一个线程开始执行setter情况,相当于函数头尾加了锁一样。

        @synchronized(self) { 
        	_age = age;
        }
      

    assign vs weak

    修饰对象

    • assign适用于基本数据类型,“纯量类型” (scalar type,例如 CGFloat 或 NSlnteger 等)
    • weak是适用于NSObject对象,并且是一个弱引用。

    指针地址改变

    • assign其实也可以用来修饰对象,那么我们为什么不用它呢?因为被assign修饰的对象在释放之后,指针的地址还是存在的,也就是说指针并没有被置为nil。如果在后续的内存分配中,刚好分到了这块地址,程序就会崩溃掉。
    • 而weak修饰的对象在释放之后,指针地址会被置为nil。所以现在一般弱引用就是用weak。
    • assigin 可以用非 OC 对象,而 weak 必须用于 OC 对象

    这个写法会出什么问题: @property (copy) NSMutableArray *array;

    两个问题:

    1. 添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃.因为 copy 就是复制一个不可变 NSArray 的对象;

    2. 使用了 atomic 属性会严重影响性能 ;

    __block vs __weak

    在block中修改变量

    • Blocks可以访问局部变量,但是不能修改,如果修改局部变量,需要加__block
    • _ _block:使用__block修饰的变量在block代码快中会被retain(ARC下,MRC下不会retain)
    • _ _weak:使用__weak修饰的变量不会在block代码块中被retain

    避免循环引用

    • 要避免block出现循环引用 __weak typedof(self)weakSelf = self;
    • 为什么不用__block 是因为通过引用来访问self的实例变量 ,self被retain,block也是一个强引用,引起循环引用,用__week是弱引用,当self释放时,weakSelf已经等于nil。

    使用atomic一定是线程安全的吗

    不是的

    • atomic仅限于getter,setter时的线程安全。
    • 在一个线程执行setter方法的时候,会涉及到字符串拷贝,另一个线程去读取,很可能读到一半的数据,也就是garbage数据。
    • 比如@property(atomic,strong)NSMutableArray *arr;如果一个线程循环读数据,一个线程循环写数据,肯定会产生内存问题。因为它和setter,getter没有关系。

    retain cycle例子

    block中的循环引用:一个viewController

    @property (nonatomic,strong)HttpRequestHandler * handler;
    @property (nonatomic,strong)NSData          *data;
    _handler = [httpRequestHandler sharedManager];
    [ downloadData:^(id responseData){
        _data = responseData;
    }];
    

    self 拥有_handler, _handler 拥有block, block拥有self(因为使用了self的_data属性,block会copy 一份self)

    vc异步的网络请求,成功后的block调用vc,如果此时,用户已经不用此vc了,vc还是没有释放。

  • 相关阅读:
    Django 报错RuntimeError: Model class apps.alarms.models.SendAlarmRecord doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.
    Go获取Windows下的窗口
    linux中得到当前目录指定文件或者目录的绝对路径
    关于在IDEA中使用maven的运行test目录下的main方法无法找到类的?
    数值变量互换的三种方式
    对于List和普通数组元素怎么去重的方法
    UE编辑器UltraEdit格式化XML数据
    java操作JSON字符串转换成对象的时候如何可以不建立实体类也能获取数据
    java设置RabbitMQ的消费处理出现:ConditionalRejectingErrorHandler : Execution of Rabbit message listener failed.
    org.springframework.http.converter.HttpMessageNotReadableException
  • 原文地址:https://www.cnblogs.com/sunyanyan/p/5301908.html
Copyright © 2011-2022 走看看