《Effective Objective-C 2.0》—(第6-10条)—对象、属性、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很少这么做。该写法的变量内存布局在编译期已经固定了。如图所示:

详细见《深度探索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);

下图是对象关联类型

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

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

时间: 2024-10-11 12:59:56

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

Effective-OC 10.在既有类中使用关联对象存储自定义数据

EOC中介绍与案例 有时候需要在对象中存放相关的信息 这时候我们通常会从对象所属的类中继承一个子类,然后改用这个子类对象.然而并非所有的情况都能这么做.有的时候 类的实例可能是由某种机制创建的,而开发者无法令这种机制创建出自己写的子类的实例,OC中有一强大的特性可以解决这个问题 就是"关联对象" 可以给某对象关联许多其他的对象 这些对象通过"键"来区分.存储对象值得实惠 可以指明"存储策略",用以维护相对应的"内存管理语义".

iOS开发——技术精华Swift篇&amp;Swift 2.0和Objective-C2.0混编之第三方框架的使用

Swift 2.0和Objective-C2.0混编之第三方框架的使用 swift 语言是苹果公司在2014年的WWDC大会上发布的全新的编程语言.Swift语言继承了C语言以及Objective-C的特性,且克服了C语言的兼容性问题.Swift语言采用安全编程模式,且引入了多种新功能,使得编程工作更加简便,灵活! 2015年6月9日苹果又一次给所有开发之者带来了一个惊喜,那就是今年年底swift讲开源,者队iOS开发着来说无疑是一个值得兴奋的消息,可是就在这短短的几个月里面swift吸引了越来

升级_开阔视野之Oracle图形化升级(dbca建库后升级)—10.2.0.1.0升为10.2.0.5.0

***********************************************声明***********************************************************************  原创作品,出自 "深蓝的blog" 博客,欢迎转载,转载时请务必注明出处,否则追究版权法律责任. 表述有错误之处,请您留言或邮件([email protected])指明,不胜感激. 本文转载必须保留此处:http://blog.csdn.net

Raid 0 1 5 10的原理、特点、性能区别

1.1 RAID介绍 RAID是磁盘冗余阵列(redundant array of independent disks)简称磁盘阵列. RAID是一种把多块独立的物理磁盘按不同的raid级别组合起形成一个磁盘组,在逻辑上看是一块大的磁盘,可以提供比单个物理磁盘更大的存储容量或更高的存储性能,同时又能提供不同级别数据冗余备份的一种技术. 用RAID最直接的好处是: 提升数据安全性 提升数据读写性能 提供更大的单一逻辑磁盘数据容量存储 1.2.实现模式 软件磁盘阵列(software RAID),主

sudo: effective uid is not 0, is sudo installed setuid root

当普通用户需要临时使用root权限的时候需要执行sudo命令,但是在执行sudo命令的时候需要使用root的权限去执行/usr/bin/sudo二进制文件. 如果报错出现sudo: effective uid is not 0, is sudo installed setuid root 证明/usr/bin/sudo文件没有设置s权限(用户在执行文件的时候,临时拥有文件所有者的权限.) 解决方法: chmod u+s /usr/bin/sudo 加上权限之后再查看文件 ll  /usr/bin

Oracle升级_oracle 10g版本由 10.2.0.4.0升级为10.2.0.4.4(即PSU升级)

***************************************************************************************************** 续借上篇:Oracle升级_oracle 10g版本由 10.2.0.1.0升级为10.2.0.4.0(即CPU升级) ***********************************************声明***************************************

升级_开阔视野之Oracle图形化升级(升级后dbca建库)—10.2.0.1.0升为10.2.0.5.0

***********************************************声明**********************************************  原创作品,出自 "深蓝的blog" 博客,欢迎转载,转载时请务必注明出处,否则追究版权法律责任. 表述有错误之处,请您留言或邮件([email protected])指明,不胜感激. 本文转载必须保留此处:http://blog.csdn.net/huangyanlong/article/det

VRay 2.0 SP1 2.10.01 for 3ds max 9/2008/2009/2010/2011/2012 32/64位 顶渲简体中文版+英文版[中国室内设计论坛-室内人]

VRay 2.0 SP1 2.10.01 for 3ds max 9/2008/2009/2010/2011/2012 32/64位 顶渲简体中文版+英文版[中国室内设计论坛-室内人] 对最新版本的V-Ray2.0 SP1的所有版本,重新进行了一次彻底的汉化,继以前版本的彻底.稳定之处特点外,还对所发生的Bug进行了彻底排查,能正常支持V-Ray RT.分布渲染.材质烘焙.渲染元素等V-Ray全部功能.顶渲简体中文版,还剔除了原程序中用于二次开的SDK包,正常用户不需要此开发包,这使程序更加紧凑

问题:sudo su: results -effective uid is not 0 is sudo installed setuid root

前段时间,新装的Centos运行sudo命令时出现的问题: sudo su: results -effective uid is not 0 is sudo installed setuid root 这是我在国外论坛上找到的解决办法 This another common problem for the new users for Linux. Anonymous change of permission of root or while experimenting we do run com