• iOS之copy、strong使用,block特性


    身边一同事,我印象在过去三个月,有两次因为使用“copy”修饰UIKit控件的属性,导致程序崩溃。他还一本正经的说我以前一直使用copy。

         好了,到这里我们就不得不说说什么时候使用copy。我的印象中,只有两处使用了copy,即修饰NSString类型与block,其他的都是使用strong关键字修饰。

        说到这里,我们先来说说NSString类型,我在创建的NSString类型的属性中,也曾也使用过strong修饰的,因为我几乎没有使用过NSMutableString类型转换,我不用去考虑是用copy还是strong更好,当然为了代码的健壮使用copy更好,以下我就作具体分析缘由。

        在MRC中,使用retain,copy进行拷贝,会使retainCount结果+1.但是如果是深拷贝,便会改变指针,retainCount = 1;下面我直接在ARC下调试,我只关心内存指针,不关心retainCount。

       

     NSString *str0 = @"a";
    
        NSLog(@"str0内存地址: %p",str0);    //0x107fcb088   在64位系统上得到的内存地址较短,说明存放在常量区(代码,常量,全局,堆,栈)
    
        NSString *string0  = [str0 copy];
    
        NSLog(@"string0的内存地址: %p",string0);   //0x107fcb088  浅拷贝
    
       
    
        NSString *str = [NSString stringWithFormat:@"%@",@"a"];
    
        NSLog(@"str内存地址: %p",str);   //0xa000000000000611  (栈区)
    
        NSString *string  = [str copy];
    
        NSLog(@"string的内存地址: %p",string);   //0xa000000000000611  浅拷贝
    
        
    
        NSMutableString *str1 = [NSMutableString stringWithFormat:@"a"];
    
        NSLog(@"str1的内存地址:%p",str1);   //0x60000007c5c0
    
        NSMutableString *string1 = [str1 copy];
    
        NSLog(@"string1的内存地址: %p",string1);   //0xa000000000000611   内存地址发生了改变,进行了深拷贝,而且跟上面的地址一样

    总结:对于NSString类型只是引用了内存,浅拷贝;NSMutableString作为NSString的子类进行copy才是深拷贝。

         刚刚上面的深拷贝,出现跟浅拷贝一样的地址,不由得我们需要多做两个测试,如下:

       

     NSMutableString *strEx = [str mutableCopy];
    
        NSLog(@"strEx的内存地址:%p",strEx);    //0x608000263040 深拷贝
    
        NSMutableString *strExCopy = [str mutableCopy];
    
        NSLog(@"strExCopy的内存地址:%p",strExCopy); //0x60000026a440    str两次mutableCopy的地址不一样
    
         NSMutableString *stringEx = [strEx copy];
    
        NSLog(@"stringEx的内存地址: %p",stringEx);   //0xa000000000000611 与上面地址一样
    
       NSMutableString *stringEx1 = [strExCopy copy];
    
        NSLog(@"stringEx1的内存地址: %p",stringEx1); //这个也是0xa000000000000611,说明两次copy都指向同一个地址
    
        NSMutableString *strExEx = [strEx mutableCopy];
    
        NSLog(@"strExEx的内存地址:%p",strExEx);     //0x600000073d00 深拷贝

    结论:可以看成,str1所谓的“深拷贝”,其实不是“深拷贝”,它还是拷贝了之前的地址。这样,我得出,当进行mutable创建,其实是系统首先创建了一份NSString的地址,然后再深拷贝,相当于[NSMutableString stringWithFormat:@"a"];来自于 [str mutableCopy];。

    在字符串类型NSString中使用strong还是copy,到底哪个更好,苹果自己的API中告诉了我,copy更好,那么我们就进一步进行验证。首先我们创建两个字符串对象分别为strong与copy修饰的,然后再进行赋值比较,如下。

    @property (strong,nonatomic)NSString *testStr;
    
    @property (copy,nonatomic)NSString *testStrCopy;
    
    - (void)viewDidLoad {
    
        [super viewDidLoad];
    
       NSString *testStr = [NSString stringWithFormat:@"%@",@"a"]; 
    
        NSLog(@"testStr内存地址: %p",testStr); //0xa000000000000611
    
        self.testStr = testStr;
    
        NSLog(@"self.testStr内存地址: %p",self.testStr);//0xa000000000000611   浅拷贝
    
        self.testStrCopy = testStr;
    
        NSLog(@"self.testStrCopy内存地址: %p",self.testStrCopy);//0xa000000000000611  浅拷贝
    
        NSMutableString *testStr1 = [NSMutableString stringWithFormat:@"%@",@"a"];
    
        NSLog(@"testStr1内存地址: %p",testStr);  //0x608000078a00  与上面的str1 0x60000007c5c0也不一样,MutableCopy是重新创建了地址
    
        self.testStr = testStr1;
    
        NSLog(@"self.testStr内存地址: %p",self.testStr);//0x608000078a00 strong指向同一个地址
    
        self.testStrCopy = testStr1;
    
        NSLog(@"self.testStrCopy内存地址: %p",self.testStrCopy);//0xa000000000000611 虽然地址变了,但还是指向原来的地址
    
    } 

    总结:对于NSString类型,使用copy修饰,不会改变它原有的类型,strong会指向引用的对象,有可能改变其类型状态,所以copy能增强NSString的健壮性----------------用下面一张图表示

    block特性

    另一个使用copy的地方就是修饰block:

    @property (nonatomic,copy)void(^demoBolck)();
    @property (nonatomic,strong)void(^demoBolck1)();
    
    int b=8;
    
    void (^demoBolck)() = ^{
    
            NSLog(@"demoBolck");
    
        };
    
        NSLog(@"demoBolck %@",demoBolck);    //<__NSGlobalBlock__: 0x1085af0e0>  无论ARC还是MRC下,因不访问外部局部(包括无外部变量或者只有全局变量),NSGlobalBlock表示在全局区
    
    void (^demoBolck4)() = ^{
    
            NSLog(@"demoBolck4  %d",b);
    
        };
    
       NSLog(@"demoBolck4 %@",demoBolck4);    //<__NSGlobalBlock__: 0x10150b120>  全局区
        __block int a = 6;   //block内部引用a,并修改其值,需要用block修饰,不然可以不用。不过是引用行属性,需要
    
        void (^demoBolck2)() = ^{
            NSLog(@"demoBolck2 %d",a++);
        };
    
        demoBolck2();
    
        NSLog(@"demoBolck2 %@,%d",demoBolck2,a);   //<__NSMallocBlock__: 0x600000056c50> ARC下堆区  <__NSStackBlock__: 0x7fff5d0ada28>MRC下在栈区
    
        NSLog(@"demoBolck2.Copy %@",[demoBolck2 copy]);    //<__NSMallocBlock__: 0x600000056c50>copy操作不管MRC或者ARC都在堆区,只是在MRC下进行copy会改变地址
    
        self.demoBolck = demoBolck2;
    
        NSLog(@"self.demoBolck %@",self.demoBolck);
        self.demoBolck1 = demoBolck2;
        self.demoBolck1();     //demoBolck2  7   能执行无问题
    
        NSLog(@"self.demoBolck1 %@",self.demoBolck1);     //<__NSMallocBlock__: 0x600000056c50>  strong修饰并没有问题

    __weak 本身是可以避免循环引用的问题的,但是其会导致外部对象释放了之后,block 内部也访问不到这个对象的问题,我们可以通过在 block 内部声明一个 __strong 的变量来指向 weakObj,使外部对象既能在 block 内部保持住,又能避免循环引用的问题。

    __block 本身无法避免循环引用的问题(__block NSObj *a = (NSObj*)b;),但是我们可以通过在 block 内部手动把 blockObj 赋值为 nil 的方式来避免循环引用的问题。另外一点就是 __block 修饰的变量在 block 内外都是唯一的,要注意这个特性可能带来的隐患。但是__block有一点:这只是限制在ARC环境下。在非arc下,__block是可以避免引用循环的

     

         总结:在 Objective-C 语言中,一共有 3 种类型的 block:

    1. _NSConcreteGlobalBlock 全局的静态 block,不会访问外部局部变量。
    2. _NSConcreteStackBlock 保存在栈中的 block,当函数返回时会被销毁。
    3. _NSConcreteMallocBlock 保存在堆中的 block,当引用计数为 0 时会被销毁。

     关于block的知识,参考http://blog.devtang.com/2013/07/28/a-look-inside-blocks/

    提高技能如同提升自信心。
  • 相关阅读:
    IIS7报错:如果要使用托管的处理程序,请安装 ASP.NET
    mysql 存储过程 循环
    Mysql 遇到过的自带函数使用
    Mysql:is not allowed to connect to this MySQL server
    mysql 删除或更新时出现 you are usering safe update model 的解决方案
    将一个服务器的表数据插入到另一个服务器的表中
    C#操作Excel无法删除worksheet解决方案
    安装MongoDB数据库的注意事项
    Python爬取小猪短租,用的是lxml解析器
    使用Python爬取腾讯房产的新闻,用的Python库:requests 、re、time、BeautifulSoup ​​​​
  • 原文地址:https://www.cnblogs.com/chims-liu-touch/p/6702203.html
Copyright © 2020-2023  润新知