zoukankan      html  css  js  c++  java
  • 第6条:理解“属性”

    属性特质:
    •   原子性:iOS中有两个属性non-atomic和atomic,前者是非原子性的(线程不安全),后者是原子性的(线程安全),一般情况下不会去重写它们,但某些时候确实有重写的需求。
      摘要(原文:http://my.oschina.net/majiage/blog/267409
      atomic属性线程安全,会增加一定开销,但有些时候必须自定义atomic。这时候,我们就需要知道atomic的实现原理及方法了。这篇文章主要就是讲解自定义atomic的实现。

      atomic原子性与non-atomic非原子性

      iOS中有两个属性non-atomic和atomic,前者是非原子性的(线程不安全),后者是原子性的(线程安全),一般情况下不会去重写它们,但某些时候确实有重写的需求。

      那些int、float之类的类型,你重写想出错都很难。但是强引用类型(retain)就需要注意了。

      简单的说一下非原子性的nonatomic实现,方式如下:

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

      atomic实现:

      关于atomic的实现最开始的方式如下:

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

      具体讲就是retain的同步版本,本来以为没问题,但在用GCD重绘currentImage的过程中,有时候currentImage切换太频繁。在完成之前就把之前的currentImage释放了,程序仍然会崩溃。还需要在resize过程中增加retain和release操作,代码如下:

      - (UIImage *)resizedImage:(CGSize)newSize interpolationQuality:(CGInterpolationQuality)quality {
          // For multithreading
          [self retain];
                  
          BOOL drawTransposed;
          CGAffineTransform transform = CGAffineTransformIdentity;
                  
          // In iOS 5 the image is already correctly rotated. See Eran Sandler's
          // addition here: http://eran.sandler.co.il/2011/11/07/uiimage-in-ios-5-orientation-and-resize/
                  
          if([[[UIDevice currentDevice]systemVersion]floatValue] >= 5.0) {
              drawTransposed = NO;
          } else {
              switch(self.imageOrientation) {
                  case UIImageOrientationLeft:
                  case UIImageOrientationLeftMirrored:
                  case UIImageOrientationRight:
                  case UIImageOrientationRightMirrored:
                      drawTransposed = YES;
                      break;
                  default:
                      drawTransposed = NO;
              }
                      
              transform = [self transformForOrientation:newSize];
          }
          transform = [self transformForOrientation:newSize];
                  
          UIImage *image = [self resizedImage:newSize transform:transform drawTransposed:drawTransposed interpolationQuality:quality];
          [self release];
          return image;
      }
      
      

      原始版本的resize函数如下:

      - (UIImage *)resizedImage:(CGSize)newSize interpolationQuality:(CGInterpolationQuality)quality {
          BOOL drawTransposed;
          CGAffineTransform transform = CGAffineTransformIdentity;
                 
          // In iOS 5 the image is already correctly rotated. See Eran Sandler's
          // addition here: http://eran.sandler.co.il/2011/11/07/uiimage-in-ios-5-orientation-and-resize/
                  
          if([[[UIDevice currentDevice]systemVersion]floatValue] >= 5.0) {
              drawTransposed = NO;
          } else {
              switch(self.imageOrientation) {
                  case UIImageOrientationLeft:
                  case UIImageOrientationLeftMirrored:
                  case UIImageOrientationRight:
                  case UIImageOrientationRightMirrored:
                      drawTransposed = YES;
                      break;
                  default:
                      drawTransposed = NO;
              }
                      
              transform = [self transformForOrientation:newSize];
          }
          transform = [self transformForOrientation:newSize];
                  
          return [self resizedImage:newSize transform:transform drawTransposed:drawTransposed interpolationQuality:quality];
      }
      
      

      但是之前在没有重写getter之前,用atomic的getter程序不会崩溃。于是我就想现在的getter和atomic自己实现的getter肯定有区别。

      最后,答案出现:在getter的return之前retain,再autorelease一次就可以了。getter函数就变成了这样:

      - (UIImage *)currentImage
      {
          @synchronized(self) {
              UIImage *image = [_currentImage retain];
              return [image autorelease];
          }
      }
      
      

      这样可以确保currentImage在调用过程中不会因为currentImage被释放或者改变,使它的retainCount次数变为0,再在调用时让程序直接崩溃。

      Runtime方法

      Memory and thread-safe custom property methods这篇文章中还提到了一种Objective-C的runtime解决方案。

      Objective-C的runtime中实现了以下函数:

      id <strong>objc_getProperty</strong>(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic);
      void <strong>objc_setProperty</strong>(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy);
      void <strong>objc_copyStruct</strong>(void *dest, const void *src, ptrdiff_t size, BOOL atomic, BOOL hasStrong);
      
      

      这几个函数被实现了,但没有被声名。如果要使用他们,必须自己声名。它们比用@synchronized实现的要快。因为它的实现方式与一般情况不同,静态变量只在接收并发时才会锁住。

      声名方式:

      #define <strong>AtomicRetainedSetToFrom</strong>(dest, source) 
                  objc_setProperty(self, _cmd, (ptrdiff_t)(&dest) - (ptrdiff_t)(self), source, YES, NO)
      #define <strong>AtomicCopiedSetToFrom</strong>(dest, source) 
                  objc_setProperty(self, _cmd, (ptrdiff_t)(&dest) - (ptrdiff_t)(self), source, YES, YES)
      #define <strong>AtomicAutoreleasedGet</strong>(source) 
                  objc_getProperty(self, _cmd, (ptrdiff_t)(&source) - (ptrdiff_t)(self), YES)
      #define <strong>AtomicStructToFrom</strong>(dest, source) 
                  objc_copyStruct(&dest, &source, sizeof(__typeof__(source)), YES, NO)
      
      

      用这些宏定义,上面something的copy getter和setter方法将变成这样:

      - (NSString *)someString
      {
          return AtomicAutoreleasedGet(someString);
      }
      - (void)setSomeString:(NSString *)aString
      {
          AtomicCopiedSetToFrom(someString, aString);
      }
      
      

      someRect存取方法将变成这样:

      - (NSRect)someRect
      {
          NSRect result;
          AtomicStructToFrom(result, someRect);
          return result;
      }
      - (void)setSomeRect:(NSRect)aRect
      {
          AtomicStructToFrom(someRect, aRect);
      }
    • iOS - 属性关键字的使用
      •   ☐几张效果图:
        • Copy与mutableCopy

          一、深拷贝和浅拷贝

          • 深拷贝:对象拷贝 - 直接拷贝内容。
          • 浅拷贝:指针拷贝 - 将指针中的地址值拷贝一份。

          二、对于 Copy 与 mutableCopy 的实践
          • 思路:我用四个方案来验证 Copy 与 mutableCopy 的区别。
          • 方案:

            • 方案一:copy不可变的字符串

              NSString*str = @"aaa";
              NSString*copyStr = [str copy];
              NSLog(@"str = %p copyStr= %p",str,copyStr);
              NSLog(@"指针地址:str = %p copyStr= %p",&str,&copyStr);
              
              

              输出结果:str = 0x104d94068 copyStr= 0x104d94068
              指针地址:str = 0x7fff529e9aa8 copyStr= 0x7fff529e9aa0
              小结:对不可变的字符串的copy,我们对象的内存地址没有改变,只是指针的地址改变了,所以在这里我们默认进行了一次浅拷贝,只拷贝了指针。

            • 方案二:copy可变的字符串

              NSMutableString*str1 = [NSMutableString stringWithFormat:@"bbb"];
              NSString*copyStr1 = [str1 copy];
              NSLog(@"str1 = %p copyStr1 = %p",str1,copyStr1);
              NSLog(@"str1 = %p copyStr1= %p",&str1,&copyStr1);
              
              

              输出结果:str1 = 0x7fa522712cd0 copyStr1 = 0x7fa522717ba0
              指针地址:str1 = 0x7fff529e9a98 copyStr1= 0x7fff529e9a90
              小结:对可变字符串的copy,我们默认进行了一次深拷贝,直接拷贝了对象。

            • 方案三:mutableCopy不可变字符串的
              NSString*str2 = @"ccc";
              NSMutableString *copyStr2 = [str2 mutableCopy];
              NSLog(@"str2 = %p copyStr2 = %p",str2,copyStr2);
              
              
              输出结果:str2 = 0x10d216108 copyStr2 = 0x7fa522726290
              小结:对于不可变字符串的mutableCopy我们默认进行了深拷贝。
            • 方案四:mutableCopy可变字符串
              NSMutableString*str3 = [NSMutableString stringWithFormat:@"ddd"];
              NSMutableString*copyStr3 = [str3 mutableCopy];
              NSLog(@"str3 = %p copyStr3 = %p",str3,copyStr3);
              
              
              输出结果:str3 = 0x7fa5227153c0 copyStr3 = 0x7fa5227263f0
              小结:对于可变字符串的mutableCopy我们默认进行了深拷贝。

          三、结论

          • copy:因为copy默认返回的是不可变的,所以当我们对一个不可变的字符串进行copy的时候,我们只是拷贝了它的指针(浅拷贝)。当我们对一个可变的字符串进行拷贝的时候,因为类型转变了,我们需对其进行深拷贝
          • mutableCopy:默认返回的是一个可变的对象,适用于可变的对象,例如NSMutableString,NSMutableArray,NSMutableDictionary、etc。 无论对于可变的字符串还是不可变的字符串进行mutableCopy,系统都默认进行深拷贝,那么为什么对于相同类型的进行mutableCopy返回的仍然是新的对象呢,因为在这里系统要保证,旧的对象和新的对象都是可变的,切他们之前不会相互影响。
     
     
     
  • 相关阅读:
    【深入理解JAVA虚拟机】第一部分.走进Java
    【设计模式最终总结】桥接模式 VS 外观模式
    【设计模式最终总结】组合模式:树形结构的处理
    【设计模式最终总结】桥接模式(接口模式):处理多维度变化
    【设计模式最终总结】适配器模式
    深入编解码:ASCII,UNICODE,UTF8等
    好妈妈【第四章】培养良好的学习习惯
    【设计模式最终总结】建造者模式:复杂对象的组装与创建
    上火流鼻血
    Spring Data Jpa 学习笔记
  • 原文地址:https://www.cnblogs.com/xsyl/p/5788666.html
Copyright © 2011-2022 走看看