zoukankan      html  css  js  c++  java
  • @property详解,@property修饰符以及各个修饰符区别(上)

    相信很多参加过面试的人员很多都会被问到:weak与assign的区别,copy与strong的区别。如果你仅仅说一点点copy一般对NSString,weak对于控件的修饰,assign对于基本类型,那么面试官可以会对你深入问,block用过吗?修饰block用什么,又为什么用copy,这样一层层问下去,可能场面就很尴尬了,即使你进去,可能薪资也不能达到你所期望的。这篇我准备花几天完成,希望对大家有所帮助,阅读这篇问题大约需要20-30分钟……

     一.@property

    1.讲解

    Objective-C的属性(property)是通过用@property定义的公有或者私有的方法。属性(property)提供了一种安全、便捷的方式来与这些属性(attribute)交互,而不需要手动编写一系列的访问方法,如果需要的话可以自定义getter和setter方法来覆盖编译器自动生成的相关方法。

    尽量多的使用属性(property)而不是实例变量(attribute)因为属性(property)相比实例变量有很多的好处:

    (1)自动合成getter和setter方法。当声明一个属性(property)的时候编译器默认情况下会自动生成相关的getter和setter方法更好的声明一组方法。

    (2)因为访问方法的命名约定,可以很清晰的看出getter和setter的用处。

    2.用法

    @interface Person : NSObject
    {
        NSString *_name;
        NSUInteger _age;
    }
    
    - (void)setName:(NSString*)name;
    - (NSString*)name;
    - (void)setAge:(NSUInteger)age;
    - (NSUInteger)age;
    
    @end
    
    @implementation Person
    
    - (void)setName:(NSString*)name {
        _name = [name copy];
    }
    
    - (NSString*)name {
        return _name;
    }
    
    - (void)setAge:(NSUInteger)age {
        _age = age;
    }
    
    - (NSUInteger)age {
        return _age;
    }
    
    @end

    上述代码就是手动创建变量的gettersetter的实现,gettersetter本质就是符合一定命名规范的实例方法。

    具体使用

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Person *p = [[Person alloc] init];
            //函数调用name的setter
            [p setName:@"Jiaming Chen"];
            //函数调用age的setter
            [p setAge:22];
            //函数调用name和age的getter,输出 Jiaming Chen 22
            NSLog(@"%@ %ld", [p name], [p age]);
        }
        return 0;
    }
    
    
    //另一种写法
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Person *p = [[Person alloc] init];
            //使用点语法访问name的setter
            p.name = @"Jiaming Chen";
            //使用点语法访问age的setter
            p.age = 22;
            //使用点语法访问name和age的getter,输出 Jiaming Chen 22
            NSLog(@"%@ %ld", p.name, p.age);
        }
        return 0;
    }

    Objective-C允许使用点语法来访问gettersetter

    合成使用,使用@property方法

    @interface Person : NSObject
    
    @property (nonatomic, copy) NSString* name;
    @property (nonatomic, assign) NSUInteger age;
    
    @end
    
    @implementation Person
    
    //编译器会帮我们自动生成_name和_age这两个实例变量,下面代码就可以正常使用这两个变量了
    @synthesize name = _name;
    @synthesize age = _age;
    
    - (void)setName:(NSString*)name {
        //必须使用_name来赋值,使用self.name来设置值时编译器会自动转为调用该函数,会导致无限递归
        //使用_name则是直接访问底层的存储属性,不会调用该方法来赋值
        //这里使用copy是为了防止NSMutableString多态
        _name = [name copy];
    }
    
    - (NSString*)name {
        //必须使用_name来访问属性值,使用self.name来访问值时编译器会自动转为调用该函数,会造成无限递归
        return _name;
    }
    
    @end

     二.@property后面有哪些修饰符

    1.线程安全的-------atomic、nonatomic

    atomic:原子性:Object-C使用的是一种线程保护技术,从基本上讲,是防止在未完成的时候被另一个线程更改,造成数据错误。而这种机制是耗费系统资源,所以在iPhone这种小型设备上,如果没有使用多线程之间的通讯编程,那么nonatomic是一个非常好的选择。

    nonatomic:非原子性: 禁止多线程,变量保护,提高性能。

    原子性:默认

    这个属性是为了保证程序在多线程下,编译器会自动生成自旋锁代码,避免该变量的读写不同步问题,提供多线程线程,多线程只有一个线程对它访问。

    attention

    (1)atomic原子性指的是一个操作不可以被CPU中途叫停,然后再调度。即不能被中断,要么执行完,要么不执行。

    (2)atomic是自旋锁,当上一个线程没有执行完毕的时候(被锁住),下一个线程会一直等待,不会进入睡眠,当上一个线程执行完毕后,下一个线程会立即执行。它区别于互斥锁,互斥锁在等待的时候,会进入睡眠状态,当上一个线程执行完毕之后,会被唤醒,然后再执行。

    (3)atomic需要消耗大量的资源,执行效率低

    但是atomic并不保证线程安全,因为只会保持set内部安全,在外面并不能保证安全!例如在数组array set方法中atomic原子操作,但是外面使用array addobject并不会保证线程安全!

    二、访问权限

    readwrite 默认 拥有getter/setter方法,可读可写

    readonly 只读属性,只会生成getter方法,不会生成setter方法

    三、内存管理

    1.assign 默认

    适用于基本数据类型:NSInteger,CGFloat和C数据类型int,float等,另外还有id类型

    2.strong对应MRC中的retain

    强引用,只有OC对象才能使用该属性,它使对象的引用计数加1

    3.weak

    弱引用,只是单纯引用某个对象,但是并未拥有该对象

    即一个对象被持有无数个弱引用,只要没有强引用指向它,那么它就会被清楚释放。

    下面通过一些拓展一下:面试重点!!!

    (1)strong与retain

    相同点:strong和retain都是针对对象类型进行内存管理。如果去修饰基本数据类型,Xcode会直接报错,当给对象类型使用此修饰符时,setter方法先将旧的对象属性release掉,再将新的对象赋值给属性并对该对象进行一次retain操作,两者都会增加对象的引用计数。

    不同点:strong一般用于ARC,retain一般用于MRC环境。

    (2)assgin与weak

    相同点:assgin和weak不会牵扯到内存管理,不会增加引用计数

    不同点:assign可修饰基本数据类型,也可修饰OC对象,但如果修饰对象类型指向的是一个强指针,当它指向的这个指针释放后,他仍指向这块内存,必须手动给置为nil,否则就会产生野指针,如果还通过此指针操作那块内存,便会导致EXC_BAD_ACCESS错误,调用了已经释放的内存空间;而weak只能修饰OC对象,且相比assign比较安全,如果指向的对象消失了,那么他会自动置为nil,不会产生野指针。

    (3)strong与copy(重点重点)--这个可能比较难懂,多看两遍,可能有点乏味,不过很重要!!!

     1.copy(拓展)----深拷贝和浅拷贝的区别

    浅拷贝:指针拷贝,不产生新的对象,源对象的引用计数器加1;只是多了一个指向这块内存的指针,共用一块内存。

    深拷贝:对象拷贝,会产生新的对象,源对象的引用计数器不变;两块内存是完全不同的,也就是两个对象指针分别指向不同的内存,互不干涉。

    判断是浅拷贝和深拷贝就看一下两个变量的内存地址是否一样,一样就是浅拷贝,不一样就是深拷贝,也可以改变一个变量的其中一个属性值看两者的值都会发生变化;

    系统原生的对象深浅拷贝区别:

    NSObject类提供了copy和mutableCopy方法,通过这两个方法即可拷贝已有对象的副本,主要的系统原生对象有:NSString和NSMutableString、NSArray和NSMutableArray、NSDictionary和NSMutableDictionary、NSSet和NSMutableSet。 NSValue和NSNumber 只遵守的NSCopying协议。

    NSString-------copy/mutableCopy

    NSString *string = @"copyTest";  
    NSString *copyString = [string copy];  
    NSString *mutableCopyString = [string mutableCopy];  
    NSMutableString *copyMutableString = [string copy];  
    NSMutableString *mutableCopyMutableString = [string mutableCopy];  
    NSLog(@"
     string = %p 
     copystring = %p 
     mutablecopystring = %p "  
           "
     copyMutableString = %p 
     mutableCopyMutableString = %p 
    ",  
          string, copyString, mutableCopyString, copyMutableString, mutableCopyMutableString);  

    打印结果:

    2018-05-06 10:31:51.209346+0800 copy[829:67521] 
     string = 0x100001040 
     copystring = 0x100001040 
     mutablecopystring = 0x10058c8e0 
     copyMutableString = 0x100001040 
     mutableCopyMutableString = 0x10058cde0
    Program ended with exit code: 0

    小结论:在字符串是直接赋值的,是否生成新对象是和=右边相关的,如果=右边的是mutableCopy才会产生新的对象

     NSMutableString----copy/mutableCopy

    NSMutableString *string = [NSMutableString stringWithString:@"学习研究"];  
    NSString *copyString = [string copy];  
    NSString *mutableCopyString = [string mutableCopy];  
    NSMutableString *copyMutableString = [string copy];  
    NSMutableString *mutableCopyMutableString = [string mutableCopy];  
    NSLog(@"
     string = %p 
     copystring = %p 
     mutablecopystring = %p "  
           "
     copyMutableString = %p 
     mutableCopyMutableString = %p 
    ",  
          string, copyString, mutableCopyString, copyMutableString, mutableCopyMutableString);  

     打印结果:

    2018-05-06 10:48:44.755398+0800 copy[929:77373] 
     string = 0x100504600 
     copystring = 0x100554e10 
     mutablecopystring = 0x100555880 
     copyMutableString = 0x100506e40 
     mutableCopyMutableString = 0x1005558f0
    Program ended with exit code: 0

    小结论:只要=右边从创建到赋值,至少包含一个MSMutable便会重新创建生成一个对象。

    其他对象NSArray、NSMutableArray 、NSDictionary、NSMutableDictionary、NSSet、NSMutableSet一样适用。

    刚刚疏解完copy,下面看一下面试最喜欢问的strong和copy修饰的区别:

    下面看一组例子:

    以NSString为例说明下,首先定义以下属性。

    1 @property (nonatomic, strong) NSString *strongString;  
    2 @property (nonatomic, copy) NSString *copyedString;  
    3 @property (nonatomic, strong) NSMutableString *strongMutableString;  
    4 @property (nonatomic, copy) NSMutableString *copyedMutableString; 

    2.1 当外部赋给对应属性一个不可变(非mutable)的字符串 NSString

     1 - (void)testPropertyCopyOrStrong  
     2 {  
     3     NSString *string = [NSString stringWithFormat:@"abc"];  
     4     self.strongString = string;  
     5     self.strongMutableString = string;  
     6     self.copyedString = string;  
     7     self.copyedMutableString = string;  
     8     string = [string stringByReplacingOccurrencesOfString:@"c" withString:@"233"];  
     9   
    10     NSLog(@"
     origin        string: %p, %p  %@  %@", string, &string, string, NSStringFromClass([string class]));  
    11     NSLog(@"
     strong        string: %p, %p  %@  %@", _strongString, &_strongString, _strongS  tring, NSStringFromClass([_strongString class]));  
    12     NSLog(@"
     strongMutable string: %p, %p  %@  %@", _strongMutableString, &_strongMutableSt  ring, _strongMutableString, NSStringFromClass([_strongMutableString class]));  
    13     NSLog(@"
     copy          string: %p, %p  %@  %@", _copyedString, &_copyedString, _copyedS  tring, NSStringFromClass([_copyedString class]));  
    14     NSLog(@"
     copyMutable   string: %p, %p  %@  %@", _copyedMutableString, &_copyedMutableSt  ring, _copyedMutableString, NSStringFromClass([_copyedMutableString class]));  
    15   
    16 }  

    打印结果:

    1 origin        string: 0x103a74098, 0x7fff5c18ca88  ab233  __NSCFString  
    2 strong        string: 0xa000000006362613, 0x7f84c9f056d8  abc  NSTaggedPointerString  
    3 strongMutable string: 0xa000000006362613, 0x7f84c9f056e8  abc  NSTaggedPointerString  
    4 copy          string: 0xa000000006362613, 0x7f84c9f056e0  abc  NSTaggedPointerString  
    5 copyMutable   string: 0xa000000006362613, 0x7f84c9f056f0  abc  NSTaggedPointerString

    可能大家不是很看懂这个例子:我们换一个简单的操作:

    首先在类延展中声明两个属性变量:

    1 @property (nonatomic, strong)NSString * stringStrong;   //strong修饰的字符串对象  
    2 @property (nonatomic, copy)NSString * stringCopy;       //copy修饰的字符串对象

    接着创建两个不可变字符串(NSString)

    1 //新创建两个NSString对象  
    2 NSString * strong1 = @"I am Strong!";  
    3 NSString * copy1 = @"I am Copy!";

    将这两个属性进行赋值

    1 //初始化两个字符串  
    2 self.stringStrong = strong1;  
    3 self.stringCopy = copy1;

    分别打印四个变量的地址

    1   StrongOrCopy[5046:421886] strong1 = 0x10a0b3078  
    2   StrongOrCopy[5046:421886] stringStrong = 0x10a0b3078 
    //这是两个字符串

    3 StrongOrCopy[5046:421886] copy1 = 0x10a0b3098
    4 StrongOrCopy[5046:421886] stringCopy = 0x10a0b3098 

    结果发现:可以看出,无论是strong修饰的字符串还是copy修饰的字符串,都进行了浅拷贝(仅仅是多了个指向该内存的指针,地址不会发挥变化)

    如果创建两个不可变字符串对象(NSMutableString)呢

    1 //新创建两个NSMutableString对象  
    2 NSMutableString * mutableStrong = [NSMutableString stringWithString:@"StrongMutable"];  
    3 NSMutableString * mutableCopy = [NSMutableString stringWithString:@"CopyMutable"];

    分别对属性再次赋值

    self.stringStrong = mutableStrong;  
    self.stringCopy = mutableCopy;  

    打印结果:

    1 1 StrongOrCopy[5046:421886] mutableStrong = 0x7fccba425d60  
    2 2 StrongOrCopy[5046:421886] stringStrong = 0x7fccba425d60  
    3 3 StrongOrCopy[5046:421886] mutableCopy = 0x7fccba40d7c0 
    4 4 StrongOrCopy[5046:421886] stringCopy = 0x7fccba4149e0  

    结果发现:这时就发现了,用strong修饰的字符串依旧进行了浅Copy,而由copy修饰的字符串进行了深Copy,所以mutableStrong与stringStrong指向了同一块内存,而mutableCopy和stringCopy指向的是完全两块不同的内存。

    看了一些实例,有什么用呢,看如下:

     1 //新创建两个NSString对象  
     2 NSString * strong1 = @"I am Strong!";  
     3 NSString * copy1 = @"I am Copy!";  
     4   
     5 //初始化两个字符串  
     6 self.stringStrong = strong1;  
     7 self.stringCopy = copy1;  
     8   
     9 //两个NSString进行操作  
    10 [strong1 stringByAppendingString:@"11111"];  
    11 [copy1 stringByAppendingString:@"22222"]; 

    结果如下:

    1  StrongOrCopy[5146:439360] strong1 = I am Strong!  
    2  StrongOrCopy[5146:439360] stringStrong = I am Strong!  
    3  StrongOrCopy[5146:439360] copy1 = I am Copy!  
    4  StrongOrCopy[5146:439360] stringCopy = I am Copy! 

    分别对在字符串后面进行拼接,当然这个拼接对原字符串没有任何的影响,因为不可变自字符串调用的方法都是有返回值的,原来的值是不会发生变化的.打印如下,对结果没有任何的影响:(不可变的字符串)

    然后是对可变字符串进行操作:

     1 //新创建两个NSMutableString对象  
     2 NSMutableString * mutableStrong = [NSMutableString stringWithString:@"StrongMutable"];  
     3 NSMutableString * mutableCopy = [NSMutableString stringWithString:@"CopyMutable"];  
     4   
     5 //初始化两个字符串  
     6 self.stringStrong = mutableStrong;  
     7 self.stringCopy = mutableCopy;  
     8   
     9 //两个MutableString进行操作  
    10 [mutableStrong appendString:@"Strong!"];  
    11 [mutableCopy appendString:@"Copy!"];  

    打印结果如下:

    1  StrongOrCopy[5245:446189] stringStrong = StrongMutableStrong!  
    2  StrongOrCopy[5245:446189] mutableStrong = StrongMutableStrong!  
    3  StrongOrCopy[5245:446189] stringCopy = CopyMutable  
    4  StrongOrCopy[5245:446189] mutableCopy = CopyMutableCopy! 

    对mutableStrong进行的操作,由于用strong修饰的stringStrong没有进行深Copy,导致共用了一块内存,当mutableStrong对内存进行了操作的时候,实际上对stringStrong也进行了操作;   相反,用copy修饰的stringCopy进行了深Copy,也就是说stringCopy与mutableCopy用了两块完全不同的内存,所以不管mutableCopy进行了怎么样的变化,原来的stringCopy都不会发生变化.这就在日常中避免了出现一些不可预计的错误。

    总结:在不可变对象之间进行转换,strong与copy作用是一样的,但是如果在不可变与可变之间进行操作,那么楼主比较推荐copy,这也就是为什么很多地方用copy,而不是strong修饰NSString,NSArray等存在可变不可变之分的类对象了,避免出现意外的数据操作.

    >>>>>>>拓展

    修饰block为什么要用copy修饰?

    关于block的用法,前几篇博客有,请关注:下面直接说原因:

    (1)block内部没有调用外部局部变量时存放在全局区(ARC和MRC下均是)

    (2)block使用了外部局部变量,这种情况也正是我们平时所常用的方式。MRC:Block的内存地址显示在栈区,栈区的特点就是创建的对象随时可能被销毁,一旦被销毁后续再次调用空对象就可能会造成程序崩溃,在对block进行copy后,block存放在堆区.所以在使用Block属性时使用copy修饰。但是ARC中的Block都会在堆上的,系统会默认对Block进行copy操作

    (3)用copy,strong修饰block在ARC和MRC都是可以的,都是在堆区

    下面继续讲解

    4.指定方法名称: setter= getter=

    今天到此结束(如果太多,可能大家一时接受不了),下一步,我可能继续讲解block造成的循环引用,@property与ivar的区别等,看@property引出的那些问题

  • 相关阅读:
    使用excel2003中的solver解决最优化问题
    图的邻接表存储方式的建立
    LINUX下使用VI
    LINUX下基本命令
    应用程序各对象创建的顺序
    zookeeper常遇错误详解
    MapReduce_partition
    MapReduce_TopK
    MapReduce_MaxValue
    Hbase用java基础操作
  • 原文地址:https://www.cnblogs.com/guohai-stronger/p/8993093.html
Copyright © 2011-2022 走看看