码迷,mamicode.com
首页 > 其他好文 > 详细

《Effective Objective-C 2.0》—(第6-10条)—对象、属性、equalToString、关联对象

时间:2014-08-10 15:45:40      阅读:322      评论:0      收藏:0      [点我收藏+]

标签:对象属性equaltostring

    用OC等面向对象语言编程时,“对象”(object)就是“基本构造单元”(building block),开发者可以通过对象来存储并传递数据。在对象之间传递数据并执行任务的过程就叫做“消息传递”(Messaing)。

    当应用程序运行起来以后,为其提供相关支持的代码叫做“Objective-C运行期环境”(Objevtive-C runtime),它提供了一些使得对象之间能够传递消息的重要函数,并且包含创建类实例所用的全部逻辑。在理解了运行期环境各个部分协同工作的原理之后,你的开发水平会进一步提升。

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

    “属性”(property)是OC的一项特性,用于封装对象中的数据。OC对象通常会把其所需要的数据保存为各种实例变量。属性还有一些访问方法setter和getter。这一概念已经定型,开发者可以令编译器自动编写与属性相关的访问方法。

    在描述个人信息的类中,也许会存放人名、生日、地址等内容。可以在类接口的public区段中声明一些实例变量:

@interface EOCPerson : NSObject{
@public
    NSString* _firstName;
    NSString* _lastName;
@private
    NSString* _someInternalData;
}
@end;

    原来用过Java和C++的人比较熟悉这种写法,可以定义变量的作用域。OC很少这么做。该写法的变量内存布局在编译期已经固定了。如图所示:

bubuko.com,布布扣

详细见《深度探索C++对象模型》http://blog.csdn.net/hherima/article/details/8888539

    如果EOCPerson类中添加了一个属性NSString* _dateOfBirth,那么代码需要重新编译,否则就会出错。例如,某个代码库中的代码使用了一份旧的类定义。如果和其相链接的代码使用了新的类定义,那么运行时就会出现不兼容现象。各种编程语言都有应对此问题的方法。Objetive-C的做法是,把实例变量当做一种存储偏移量所有的“特殊变量”(special variable),交由“类对象”保管。偏移量会在运行期查找,如果类的定义变了,那么存储的偏移量也就变了,这样的话,无论何时访问实例变量,总能使用正确的偏移量。甚至可以在运行期向类中新增实例变量,这就是稳固的“应用程序二进制接口”(Application Binary Interface,ABI)

    @property语法,能够访问封装在对象里的数据。因此,也可以把属性当做一种简称,其意思是说:编译器会自动生成一套访问方法,用以访问给定类型中具有给定名称的变量。例如:

@interface EOCPerson : NSObject
@proterty NSString* firstName;
@proterty NSString* lastName;
@end

对于类的使用者来说,上述代码写出来的类与下面这种写法等效:

@interface EOCPerson : NSObject
-(NSString*)firstName;
-(void)setFirstName:(NSString*)firstName;
-(NSString*)lastName;
-(void)setLastName:(NSString*)lastName;
@end

访问属性使用点语法,属性另外一个好处是可以使用自动合成@synthesize,另外@dynamic关键字可以告诉编译器不要自动生成属性访问方法,这里不再累赘。

属性特质

属性有四种特质:原子性、读/写权限、内存管理语义和方法名:

1. 原子性 默认情况下是atomic特质(尽管没有atomic这个特质),nonatmic告诉编译器不使用同步锁。

2. 读/写权限  具备read/write(读写)特质的属性拥有访问方法。若属性由@synthesize实现,则编译器会自动生成这两个方法。具备readonly(只读)特质的属性仅拥有getter方法。

3. 内存管理语义

● assign 只会针对纯类型简单赋值操作

● strong 拥有关系,为这种属性设置新值的时候,保留新值,释放旧值。

● weak 同assign,但是对象销毁的时候,属性值也会清空

● unsafe_unretained 同assign,它适用于“对象类型”这也是跟assign的唯一区别

● copy 跟strong的特质类似。然而设置setter不会保留新值,而是将其“拷贝”(copy)。当属性类型是NSMutableString*时,需要copy特质,目的是为了防止自己的属性被无意修改。参考《Effective Objective-C 2.0》—(三)—、接口与API设计、深拷贝、浅拷贝 》最后部分http://blog.csdn.net/hherima/article/details/38403277

4. 方法名

● getter=<name>指定获取方法名

● setter=<name>指定“设置方法”名

本节要点

● 使用@property 语法来定义对象中封装的数据

● 通过“特质”来指定存储数据所需的正确语义。

● 在设置属性对应的实例变量时,一定要遵从该属性声明的语义

● 开发iOS程序时候应该使用nonatomic特质,因为atomic会严重印象性能。

第7条:在对象内部尽量直接访问实例变量

    在对象之外访问实例变量时,总是应该通过属性来做,那么在对象内部?笔者强烈建议大家在读取实例变量的时候采用直接访问的形式,而在设置实例变量的时候通过属性来做。之所以要通过“设置方法”来写入实例变量,首要原因在于,这样能够确保相关属性的“内存管理语义”得以贯彻。

第8条:理解“对象等同性”这一概念

    NSObject协议中有两个判断等同性的关键方法:

-(BOOL) isEqual:(id)object;
-(NSUInteger)hash;

    NSObject类对这两个方法的默认实现是:当且仅当“指针值”(pointer value,可理解为内存地址)完全相等时,这两个对象才相等。如果"isEqual:"方法判定两个对象相等,那么其hash方法必须返回同一个值。但是,如果两个对象的hash方法返回同一个值,那么“isEqual:”方法未必会认为两者相等。

先看看"isEqual:"接口,如果一个类中有A,B,C三个字段,那么isEqual就要对三个地段逐个对比,然后都相同的时候就返回ture。

再看看hash方法,一般的做法是,将A,B,C三个字段分别hash,最后再异或一下。

-(NSUInteger)hash{
NSUInteger firstNameHash = [_firstName hash];
NSUInteger lastNameHash = [_lastName hash];
NSUInteger ageHash = _age;
return firstNameHash ^ lastNameHash ^ ageHash
}

这种做法既能保持高效率,又能使生成的哈希码至少位于一定范围之内,而不会过于频繁的重复。

特定类型所具有的等同性判定方法

    除了刚才提到的NSString之外,NSArray与NSDictionary类也有判定方法,前者是“isEqualToArray”后者是“isEqualToDictionary”。

除了刚才

等同性判定的执行深度

    创建等同性判断方法时,需要决定是根据整个对象来判断,还是仅根据其中几个字段来判断。NSArray的检测方式为先看两个数组所含有对象个数是否相等,若相同,则在每个对应位置的两个对象身上调用其“isEqual”方法,如果对应位置上的对象均相等,那么这两个数组就相等,这叫做“深度等同判定”

容器中可变类的等同性

直接举例NSSet就好。

第一步:在NSSet中添加一个可变数组A[@1,@2];那么set中就有了一个对象,{[@1,@2]}

第二步:再向NSSet中添加一个可变数组B[@1,@2],由于数组A和B是一样的,所以set中仍是一个对象,这也负责set的特性。

第三步:向NSSet中添加一个可变数组C[@1],那么set中就有了两个对象,{[@1],[@1,@2]}.

第四步:我们改变数组C的值为[@1,@2],那么set居然包含了两个相同的对象,{[@1,@2],[@1,@2]}.这不符合set的特性。

第五步:复制一份set。新的set的内容又变成{[@1,@2]}了。看上去set好像是由一个空set开始,通过逐个向其中添加新对象而创建出来的。

这是因为像set这样的collect,把某个对象放入collect之后,就不应该改变其哈希码了。

本节要点:

● 若想检测对象的等同性,请提供“isEqual:”与hash方法。

● 相同的对象必须具有相同的哈希码,但是两个哈希码相同的对象未必相同。

● 不要盲目的逐个检测每个属性,而是应该依照具体需求来定制检测方案

● 编写hash方法时,应该使用计算速度快而且哈希码碰撞几率低的算法。

第9条:以“类族模式”隐藏实现细节

    “类族”(class cluster)模式是一种很有用的模式(pattern),可以隐藏“抽象基类”(abstract base class)背后的实现细节。Objective-C的系统框架中普遍使用此模式。比如,iOS的用户界面框架UIKit中就有一个名为UIButton类。

第10条:在既有类中使用关联对象存放自定义数据

    直接列举开发中常见的例子吧:一个UIAlertView的使用,通常的做法是这样的:

-(void) askUserAQuestion{
    UIAlterView *alert = [[UIAlterView alloc] initWithTitle:@"Question" message:@"what do you want to do?" delegate:self cancelButtonTitle:@"cancel" otherButtonTitle:@"cancel",nil];
    [alert show];
}
//UIAlertViewDelegate protocol method
-(void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    if(buttonIndex == 0){
        [self doCancel];
    } else {
        [self doContinue];
    }
}

alertView的创建和处理,不在一个地方。开发过程中,经常来回滑动代码查看。那么“关联对象”就起到作用了,将操作跟UIAlertView关联起来。例如:

static void* EOCMyAlertKey = "EOCMyAlertViewKey";
-(void) askUserAQuestion{
    UIAlterView *alert = [[UIAlterView alloc] initWithTitle:@"Question" message:@"what do you want to do?" delegate:self cancelButtonTitle:@"cancel" otherButtonTitle:@"cancel",nil];
    void (^block)(NSInteger) = ^(NSInteger buttonIndex){    
        if(buttonIndex == 0){
            [self doCancel];
        } else {
            [self doContinue];
        }
        };
    objc_setAssociatedObject(alert,EOCMyAlertViewKey,block,OBJC_ASSOCIATION_COPY);
    [alert show];
}
//UIAlertViewDelegate protocol method
-(void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    if(buttonIndex == 0){
        [self doCancel];
    } else {
        [self doContinue];
    }
}
-(void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    void (^block)(NSInteger) = objc_getAssociatedObject(alertView,EOCMyAlertViewkey);
    block(buttonIndex);
}

介绍一下改进代码中的技术

● void objc_setAssociatedObject(id object, void*key ,id value,objc_AssociatedPolicy policy) 

此方法以给定的键和策略为某对象设置关联对象值

● id objc_getAssociatedObject(id object,void* key)

此方法根据给定的键从某对象中获取对应的关联对象值

● void objc_removeAssociatedObjects(id object);

下图是对象关联类型

bubuko.com,布布扣

但是,这个方法应该慎用,在使用block的时候小心循环引用(参考bloc的介绍)。所以一般的做法还是:从中继承子类,把block保存为子类中的属性。


《Effective Objective-C 2.0》—(第6-10条)—对象、属性、equalToString、关联对象,布布扣,bubuko.com

《Effective Objective-C 2.0》—(第6-10条)—对象、属性、equalToString、关联对象

标签:对象属性equaltostring

原文地址:http://blog.csdn.net/hherima/article/details/38413573

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!