Objective-c官方文档 封装数据属性

浏览:
字体:
发布时间:2013-12-09 23:24:02
来源:
 
很多对象需要跟踪信息为了执行他们的任务。一些对象设计模型一个或者多个值。例如NSNumber 类用来保存一个值或者自定义的类有一些属性。有一些对象不在一般的范围内。也许处理界面交互和一些信息展示这些对象用来跟踪界面元素或者相关的模型对象。
 
声明公有的属性公开数据:
Objective-c属性提供了一个定义类信息的方法目的是数据的封装
 
@interface XYZPerson : NSObject
 
@property NSString *firstName;
 
@property NSString *lastName;
 
@end
 
在这个例子中这个XYZPerson 类定义了String类型的属性用来存储人的名字
在面向对象思想中一个只要的原则是对象应该隐藏内部实现在公有接口的后面。我们访问对象的属性而不是直接访问内部的值。
我们用设置器和访问器方法来获取和设置属性的值
 
NSString *firstName = [somePerson firstName];
 
    [somePerson setFirstName:@"Johnny"];
 
默认情况下这些设置器和访问器方法是自动合成的由编译器,因此你不需要做任何事情除了声明属性。
合成的方法遵循特定的命名规范:
这个方法用来访问值(设置器方法)和属性有相同的名字。
例如:firstName 的访问器方法名字就叫做firstName
这个方法用来设置值(设置器方法)开始用set然后用上属性的名字第一个字母大写:
例如:setFirstName;
如果你不希望属性改变那么就用readonly只读的:
@property (readonly)NSString *fullName;
关键字显示对象怎么和属性交互,以及告诉编译器怎么合成相关的设置器和访问器方法。
例如这个编译器将要合成fullName访问器方法,但是没有setFullName方法。
和只读readOnly相反的是readwrite属性默认是可读写的。
如果你想给访问器方法设置不同的名字,你可以再给属性添加他的属性的时候指定名字。例如Boolean属性(这个属性有YES 或者NO值)给他自定义访问器方法以“IS“开头
@property (getter = isFinished)BOOL finished;
如果你有多个属性这样设置:
@property (readonly,getter = isFinished)BOOL finished;
在这个例子中编译器合成的是isFinished方法而不是setFinished方法。
 
点语法是调用的访问器方法
除了明确的访问器方法调用Objective-c还提供了点语法访问对象的属性。
点语法让你像这样访问属性:
NSString *firstName = somePerson.firstName;
 
点语法纯粹是一个包装器方法用来调用访问器方法的。当用逗号时候,属性仍然可以被访问和修改用getter和setter方法例如:
访问值用somePerson.firstName 和 [somePerson firstName]:是一样的。
设置值用somePerson.firstName = @"qiqi";和
[somePerson setFirstName:@“qiqi”];
这就意味着属性的访问用点语法也可以控制属性。如果属性是readonly编译器将要得到一个警告当你用逗号的时候。
大多数属性都有实例变量。
默认情况下,可读写的属性是有辅助实例变量的,再次由编译器自动合成。
 
实例变量是一个变量,在整个对象生命周期内都存在。当对象第一次被创建的时候,用于实例变量的内存分配,当对象销毁的时候就被释放了。除非有特别的指示否则合成的实例变量和属性有相同的名字,但是有一个下划线例如属性叫做firstName。合成的实例变量的名字叫做_firstName.
    虽然他的最佳实践是一个对象访问自己的属性用访问器或者点语法,他也有可能在实现文件里直接访问实例变量用实例方法。下划线加上实例变量表明你访问的是实例变量而不是局部变量例如:
 
- (void)someMethod {
 
    NSString *myString = @"An interesting string";
 
    _someString = myString;
 
}
这个例子很明显一个是局部变量和一个实例变量。
通常我们应该用访问器方法和点语法来进行属性的访问即你访问对象的属性用他们自己的实现,在这个例子中我们可以用self:
-(void)someMethod
{
  NSString *myString = @"qiqi";
self.someString = myString;
 //or
[self setSomeString:myString];
}
这个特殊的例外是当写初始化方法或者销毁方法或者自定义访问方法我们后来描述的。
 
你可以自定义合成实例变量的名字
@implementation YourClass
@synthesize propertyName = _instanceVariableName;
@end
 
例如:
@sythesize firstName = _my_firstName;
在这个例子中,属性将要被叫做firstName,并且可以访问通过firstName并且setFirstName访问器方法或者点语法,但是有辅助实例变量_my_firstName
重要注意:如果你用@synthesize没有指定实例变量的名字,就像:
@sytheSize firstName;
这个实例变量将要和你的属性的名字一样。
在这个例子中实例变量叫做firstName没有下划线。
 
你可以定义实例变量而不用属性
属性的最佳实践在对象上是任何时候你需要跟踪对象的值或者其他的对象。
 
如果你需要定义你自己的实例变量没有定义属性,你可以在头文件或者实现文件里用如下:
@interface SomeClass:NSObject
{
   NSString *_myInstanceVariable;
}
 
@implementation SomeClass
 
NSString *_anotherCustomInstanceVarible;
@end
 
你可以添加实例变量在延展里
访问实例变量直接从初始化方法里:
setter方法可以有额外的副作用。他们可以出发KVC通知,或者执行进一步的任务如果是你的自定义方法。
你应该直接访问实例变量从你的初始化方法里应为属性在这时候已经被设置了。其余的对象可能尚未初始化完成。即使你不提供自定义的访问器方法在未来的时候可能被其他的子类给覆盖。
一个典型的init方法。
 
- (id)init {
 
    self = [super init];
 
    if (self) {
 
        // initialize instance variables here
 
    }
 
    return self;
 
}
一个init方法应该分派self和父类初始化方法在做自己的初始化之前。由于父类可能初始化失败或者返回nil因此我们应该检查来确认self不是nil在我们本类初始化时候。
 
在调用父类初始化方法的时候会层层调用父类的初始化方法,当都初始化完毕的时候那么再初始化自己。
前面我们看的对象初始化除了调用init方法外,或者调用方法初始化特殊值。
- (id)initWithFirstName:(NSString *)aFirstName lastName:(NSString *)aLastName;
你可以实现这个方法像这样:
 
- (id)initWithFirstName:(NSString *)aFirstName lastName:(NSString *)aLastName {
 
    self = [super init];
 
    if (self) {
 
        _firstName = aFirstName;
 
        _lastName = aLastName;
 
    }
 
    return self;
 
}
指定初始化是主要的初始化方法:
如果一个对象一个或者多个初始化方法,你应该定义哪个方法是设计的初始化者,这个提供了很多可选的初始化(例如可以有很多的参数),并且可以为其他的方法调用提供便利,你应该也典型重载初始化方法来给你的初始化方法设定默认的值。
如果下面这个方法的类有生日的属性,我们设计初始化方法可能这样:
- (id)initWithFirstName:(NSString *)aFirstName lastName:(NSString *)aLastName dateOfBirth:(NSDate *)aDOB;
这个方法将要设置我们的实例变量,如果你将提供便利初始化第一个和第二个名字,你将要这样实现你的初始化方法:
- (id)initWithFirstName:(NSString *) aFrameworkName  lastName:(NSString *)aLastName
{
return [self initWithFirstName:aFirstName lastName:aLastName dateOfBirth:nil];
}
 
你可以实现标准的init方法来提供适当的默认值
- (id)init
{
 return [self initWithFirstName:@"qiqi" lastName@"deo" dateOfBirth:nil];
}
你需要写初始化方法当子类用多个初始化方法,你应该重写父类的初始化方法,来实现自己的初始化,或者添加自己的初始化方法,但是都得初始化父类在初始化自己之前。
 
你可以实现自己的访问器方法:
属性不能一直有自己的辅助实例变量。
举一个例子我们为了跟踪属性的值可以自定义打印出他的值例如:
 
- (NSString *)fullName {
 
    return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
 
}
 
如果你需要写自定义的访问器方法为属性并且有一个实例变量。你必须直接定义实例变量。例如:我们经常在属性里面延迟初始化实例变量懒加载设计模式:
 
- (XYZObject *)someImportantObject {
 
    if (!_someImportantObject) {
 
        _someImportantObject = [[XYZObject alloc] init];
 
    }
 
    return _someImportantObject;
 
}
再返回值之前需要检测返回值是不是nil,如果为空那么就初始化对象。
编译器会自动合成一个实例变量在所有的情况下,它也是合成只要一个访问器方法。如果你自己实现访问器和设置器方法,那么编译器会认为你自己控制属性的实现,而不会自动的生成实例变量。
 
如果你仍然需要实例变量的话,你将要需要请求一个合成。
@synthesize property = _property;
 
属性默认的是原子性的:
默认的Objective-c属性是原子性的:
 
@interface XYZObject : NSObject
 
@property NSObject *implicitAtomicObject;          // atomic by default
 
@property (atomic) NSObject *explicitAtomicObject; // explicitly marked atomic
 
@end
注意:属性的原子性的特性并不是意味着这个对象的线程是安全的。我们想象一下,一个对象有名字和姓在原子性的访问器被访问。如果一个线程同时访问他们的名和姓,这个原子性的访问方法不一定正确返回值。如果名字在访问之前改变,姓在访问之后改变,你会有一个不一致,不对应的结果,那么这不是你想要的结果。
 
通过对象的所有权和责任管理对象图
就像你已经看到的,Objective-c的对象是动态创建的在堆上,这意味着你需要用指针来跟踪对象地址,不像常量,不是一直能确定对象的生命周期的范围通过指针变量,相反的是一个对象必须长时间保存在内存中以便于其他的对象引用。
 
不要试图担心手动管理每个对象的生命周期,你应该试图考虑对象之间的关系。
 
>更多相关文章
24小时热门资讯
24小时回复排行
资讯 | QQ | 安全 | 编程 | 数据库 | 系统 | 网络 | 考试 | 站长 | 关于东联 | 安全雇佣 | 搞笑视频大全 | 微信学院 | 视频课程 |
关于我们 | 联系我们 | 广告服务 | 免责申明 | 作品发布 | 网站地图 | 官方微博 | 技术培训
Copyright © 2007 - 2024 Vm888.Com. All Rights Reserved
粤公网安备 44060402001498号 粤ICP备19097316号 请遵循相关法律法规
');})();