【IOS 开发学习总结-OC-12】★objective-c面向对象之——合成存取方法与点语法

合成存取方法

前面我们介绍了为成员变量自己实现 setter 方法和 getter 方法,但如果一个了类中有很多成员变量时,会非常的不便。
objective-c从 OC 2.0版本开始,自动合成了setter 方法和 getter 方法。而且,如果开发者需要自己控制某个setter 方法和 getter 方法的实现时,可以自己提供 setter 方法和 getter 方法,开发者提供的setter 方法和 getter 方法会覆盖系统自动合成的相应方法。

使系统自动合成 setter 和 getter 的方法的步骤

  1. 在类接口部分使用@property 指令定义属性。——@property 指令放在属性定义的前面,直接放在@interface,@end 之间定义。
  2. 在类实现的部分使用@synthesize 指令声明该属性。
    当采用上面的2步后,会合成成对的 setter 和 getter 方法。还会自动在类的实现部分定义一个与 getter 方法同名的成员变量。这部分可以参见之前的一篇博文《【IOS 开发学习总结-OC-11】★objective-c面向对象之——封装和访问控制符 》
    如果为某个类定义了一个成员变量,并提供了相应的setter 和 getter 方法,那么可以称为定义了一个属性(property)。
    Xcode4.0后的编码规范推荐将成员变量名定义为以下划线开头,比如在 Xcode 的模板代码中有这样的代码:@synthesize window=_window; 该代码的作用是:告诉系统合成的property对应的成员变量为_window, 而不是 window。
    @synthesize的语法格式:@synthesize property名 =成员变量名; 或者是@synthesize property名
    @synthesize 后如果没有有指定成员变量名,成员变量默认与合成的 getter 方法重名。
    示范合成存取方法的用法。示例代码:
    FKUser.h文件:
#import <Foundation/Foundation.h>
@interface FKUser : NSObject
// 使用@property定义3个property
@property (nonatomic) NSString* name;
@property NSString* pass;
@property NSDate* birth;
@end

FKUser.m文件:

#import "FKUser.h"

@implementation FKUser
// 为3个property合成setter和getter方法,
// 指定name property底层对应的成员变量名为_name
@synthesize name = _name;
@synthesize pass;
@synthesize birth;
// 实现自定义的setName:方法,添加自己的控制逻辑
- (void) setName:(NSString*) name
{
    self->_name = [NSString stringWithFormat:@"+++%@"
         , name];
}
@end

FKUserTest.m文件:

#import "FKUser.h"

int main(int argc , char * argv[])
{
    @autoreleasepool{
        // 创建FKUser对象
        FKUser* user = [[FKUser alloc] init];
        // 调用setter方法修改user成员变量的值
        [user setName:@"admin"];
        [user setPass:@"1234"];
        [user setBirth:[NSDate date]];
        // 访问user成员变量的值
        NSLog(@"管理员账号为:%@,密码为:%@,生日为:%@"
            , [user name] , [user pass] , [user birth]);
    }
}

程序通过@property,@synthesize 合成 setter,getter 方法后,程序就可以在后面通过setter,getter 方法存取成员变量的值。

定义@property时用到的特殊指示符

assign

assign:指定对属性只是简单赋值,不更改对所赋的值的引用计数。主要适用于:NSInteger 等基本类型,以及 short,float,结构体等各种 C 数据类型。那么问题来了,

为什么NSInteger 等基本类型,以及 short,float,结构体等各种 C 数据类型不存在回收的问题呢?

iOS中的垃圾处理机制是根据一个对象的索引数(引用计数)来处理的,为0的时候表示没有地方使用该对象,则该对象将被清除,而基本数据类型不属于对象,它的创建和使用都是在内存中,超出对应方法体即被清除,所以不需要使用垃圾处理机制,无需记录索引值(计数),不存在回收的问题。所以使用assgin。

atomic与nonatomic

atomic(nonatomic):指定合成的存取方法是否为原子操作。默认为atomic。
如果用atomic,那么合成的存取方法都是线程安全的——当一个线程进入存,取方法的方法体后,其他线程无法进入该存,取方法,这样可以避免多线程破坏对象的数据完整性。

copy

copy:使用 copy 指示符时,当调用 setter 方法对成员变量赋值时,会将被赋值的对象复制一个副本,再将副本赋值给成员变量。copy 会将原成员变量所引用对象的引用计数减1。

何时考虑使用 copy?

当成员变量的类型是可变类型或其子类是可变类型时,被赋值的对象可能在赋值之后被修改,如果程序不需要这种修改影响 setter 方法设置的成员变量的值,此时考虑用 copy 指示符。
示例程序:
FKBook.h

#import <Foundation/Foundation.h>

@interface FKBook : NSObject
// 使用@property定义1个property
@property (nonatomic) NSString* name;
@end

FKBook.m

#import "FKBook.h"

@implementation FKBook
@synthesize name;
@end

FKBookTest.m

#import "FKBook.h"

int main(int argc , char * argv[])
{
    @autoreleasepool{
        FKBook* book = [[FKBook alloc] init];
        //NSMutableString是NSString的子类
        NSMutableString* str = [NSMutableString 
            stringWithString:@"iOS讲义"];
        // 对book的name属性赋值
        [book setName:str];
        // 输出book的name属性
        NSLog(@"book的name为:%@" , [book name]);
        // 修改str字符串
        [str appendString:@"是一本很好的iOS学习图书"];
        // 下面代码将会看到book的name属性也会被修改
        NSLog(@"book的name为:%@" , [book name]);
    }
}

编译运行该程序后,输出内容为:
book的name为:iOS讲义
book的name为:iOS讲义是一本很好的iOS学习图书

如果我们不想 FKBook 的 name 属性不会随着NSMutableString对象的改变而改变,我们可以在定义 name 属性的一行增加copy 指示符就可以了, @property (nonatomic,copy) NSString* name;

setter&getter

用于为合成的setter,getter方法指定自定义的方法名。
举个栗子:getter=abc 指定 getter 方法名为 abc,setter=xyz:,则指定 setter 方法名为 xyz。注意:——setter方法不要忘了带冒号,因为它要带参数。
示例程序:
FKItem.h

#import <Foundation/Foundation.h>

@interface FKItem : NSObject
// 使用@property定义1个property,并指定自定义的getter、setter方法名
@property (assign , nonatomic , getter=wawa , setter=nana:) int price;
@end

FKItem.m

#import "FKItem.h"

@implementation FKItem
@synthesize price;
@end

FKItemTest.m

#import "FKItem.h"

int main(int argc , char * argv[])
{
    @autoreleasepool{
        FKItem* item = [[FKItem alloc] init];
        // 设置price属性
        [item nana:30];
        // 访问price属性
        NSLog(@"item的price为:%d" , [item wawa]);
    }
}

这里的的 setPrice: 方法和 获取的price 方法已经不存在,取而代之的是 nana 和 wawa。

readonly&readwrite

readonly指示系统只合成 getter 方法,不再合成 setter 方法。readwrite是默认值,2个方法都合成。

retain

使用retain指示符定义属性时,当把某个对象赋值给该属性时,该属性原来所引用的对象的引用计数-1,被赋值对象的引用计数+1.
说明:在启动 ARC 的情况下,一般少用 retain。但在未启用 ARC 的情况下,retain是很有用的。
示例程序:(下面的程序关闭了 ARC)
FKWin.h

#import <Foundation/Foundation.h>

@interface FKWin : NSObject
// 使用@property定义1个property
@property (nonatomic , retain) NSDate* date;
@end

FKWin.m

#import "FKWin.h"

@implementation FKWin
@synthesize date;
@end

FKWinTest.m

#import "FKWin.h"

int main(int argc , char * argv[])
{
    FKWin* win = [[FKWin alloc] init];
    NSDate* date = [[NSDate alloc] init];
    // 第一次赋值时,date的引用计数为1
    NSLog(@"date的引用计数为:%ld" , date.retainCount);
    // 由于使用了retain指示符,赋值时导致date的引用计数+1
    [win setDate:date];
    // 下面输出的引用计数为2
    NSLog(@"[win date]的引用计数为:%ld" , [win date].retainCount);
    // 释放date的引用计数,date的引用计数-1
    [date release];
    // 下面输出的引用计数为1
    NSLog(@"[win date]的引用计数为:%ld" , [win date].retainCount);
}

点语法

objective-c 允许使用简化的点语法访问属性和对属性赋值。其本质仍是调用 getter,setter 方法。
示例程序:
FKCard.h

#import <Foundation/Foundation.h>

@interface FKCard : NSObject
// 使用@property定义2个property
@property (nonatomic , copy) NSString* flower;
@property (nonatomic , copy) NSString* value;
@end

FKCard.m

#import "FKCard.h"

@implementation FKCard
@synthesize flower;
@synthesize value;
@end

FKCardTest.m

#import "FKCard.h"

int main(int argc , char * argv[])
{
    @autoreleasepool{
        FKCard* card = [[FKCard alloc] init];
        // 通过点语法对属性赋值
        card.flower = @"♠";
        card.value = @"A";
        // 通过点语法来访问属性值
        NSLog(@"我的扑克牌为:%@%@", card.flower, card.value);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值