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还是没有释放。

  • 相关阅读:
    windows 按时自动化任务
    Linux libusb 安装及简单使用
    Linux 交换eth0和eth1
    I.MX6 GPS JNI HAL register init hacking
    I.MX6 Android mmm convenient to use
    I.MX6 GPS Android HAL Framework 调试
    Android GPS GPSBasics project hacking
    Python windows serial
    【JAVA】别特注意,POI中getLastRowNum() 和getLastCellNum()的区别
    freemarker跳出循环
  • 原文地址:https://www.cnblogs.com/sunyanyan/p/5301908.html
Copyright © 2011-2022 走看看