第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 - 属性关键字的使用
    • 原文:http://www.jianshu.com/p/9e16d8ba0345

      ?几张效果图:

      • 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返回的仍然是新的对象呢,因为在这里系统要保证,旧的对象和新的对象都是可变的,切他们之前不会相互影响。
时间: 2024-10-29 19:09:56

第6条:理解“属性”的相关文章

第6条:理解“属性”这一概念(中)

属性特质: 使用属性时还有一个问题要注意,就是其各种特质(attribute)设定也会影响编译器所生成的存取方法.比如下面这个属性就指定了三项特质: 1 @property (nonatomic, readwrite, copy) NSString *testFirstName; 属性可以拥有的特质分为四类: 原子性 在默认情况下,由编译器所合成的方法会通过锁定机制确保其原子性(atomicity)(在并发编程中,如果某操作具备整体性,也就是说,系统其他部分无法观察到其中间步骤所生成的临时结果,

Effective Objective-C 2.0 — 第二章 对象、消息、运行期 - 第六条:理解“属性”这一概念

开发者通过对象来 存储并传递数据. 在对象之间传递数据并执行任务的过程就叫做“消息传递”. 这两条特性的工作原理? Objective-C运行期环境(Objective-C runtime) ,提供了使得对象之间能够传递消息的重要函数,并且包含创建类实例所用的全部逻辑. 第六条:理解“属性”这一概念 property:

IL来理解属性

IL来理解属性 阅读目录 概述: C#中如何定义一个属性 Student类 属性Name Main方法 实现get,set方法 性能 访问权限 回到最开始提出的问题 参考资料 .Net底层剖析目录章节 1.[深入浅出.Net IL]1.一个For循环引发的IL 2.[.Net底层剖析]2.stfld指令-给对象的字段赋值 3.[.Net底层剖析]3.用IL来理解属性 未完待续...... 回到顶部 概述: 我们经常在code中用到属性,但是我们真的知道属性和字段的区别吗?为什么会有属性这个用法?

(第2章-对象、消息、运行期)第6条:理解“属性”这一概念

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

第6条: 理解“属性”这一概念

通过对象来存储并传递数据, 在对象之间传递数据并执行任务的过程称为“消息传递”. OC对象会把其所需要的数据保存为实例变量并通过“存取方法”来访问.这称为属性. @dynamic 告诉编译器:不要自动创建实现属性所用的实例变量,也不要为其创建存取方法.这样在编译时虽然发现没有定义存取方法也不会报错,相信在运行期能找到. 例:CoreData 框架中的NSManagedObject类的子类时,用了这个关键字,说明数据是在运行时创建存取方法的. 属性特质:4类 原子性:nonatomic,非原子的,

Android自学笔记之ProgressBar进度条的属性、常用方法及使用

1,属性: android:progress="0"  ----设置第一层进度条的初始值 android:max="100"  ---设置进度条的最大值 android:secondaryProgress="10"  --设置第二层进度条的初始值 2.进度条的常用方法: int getMax():返回这个进度条的最大值 int getProgress():返回进度条当前进度 int getSecondProgress():返回当前次要进度 voi

6、理解“属性”这一概念

要理解好"属性"这一概念,我觉得需要把以下几个问题弄明白: 1.实例变量与public.protected.private 2.实例变量的继承性 3.属性摆放的位置,比如声明文件.实现文件.匿名分类声明文件等 4.属性与实例变量的关系 5.使用属性还是使用实例变量 6.属性与setter.getter方法的关系,与点语法的关系 7.属性的4个属性特质:原子性.读/写权限.内存管理语义.方法名 笔者结合工作经验.书本信息.网络资源等,希望能够将这些内容尽量梳理清楚. (未完-待续,工作任

react--滑动输入条默认属性问题

滑动输入条页面刷新时只会读取第一次传入的属性值,在没有用传入id获取到正确的默认值时,页面已经被渲染了. 目前想到的解决方法: 设置一个当前状态,默认值为false,把状态给checked={this.state.istop?true:false} 试着在页面第一次render之前 didmount里面,调用方法之后,传入回调设置this.state.istop; ========================== 或者只用用this.state.istop:this.props.curIst

编程的有效方法--深刻理解“属性这一概念”

1.简介 1>“属性”(property)是Objecive-C的一项特性,用于封装对象中的数据. 2>可以把属性当做一种简称,其意思是说:编译器会自动写出一套存取方法,用以访问给定类型中具有给定名称的变量,若不想令编译器自动合成存取方法,则可以自己实现.如果你只实现了其中一个存取方法,那么另外一个还是会由编译器来合成. 3> 还有一种办法能阻止编译器自动合成存取方法,就是使用@dynamic关键字,它会告诉编译器:不要自动创建实现属性所用的实例变量,也不要为其创建存取方法. 2.内存管