美文网首页
读EffectiveObjective-C2.0(第六条、第七条

读EffectiveObjective-C2.0(第六条、第七条

作者: LazyLoad | 来源:发表于2020-10-28 10:48 被阅读0次

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

  • ”属性“是Objective-C的一项特性,用于封装对象中的数据。Objective-C对象通常会把其所需要的数据保存为各种实例变量。实例变量通过存取方法来访问,getter方法用来读取变量值,setter 方法用于写入变量值。

  • 开发者可以令编译器自动编写与属性相关的存取方法,可以使用点语法来访问其中的数据。编译器会把点语法转换为对存取方法的调用,使用点语法的效果与直接调用存取方法相同。

EOCPerson *aPerson = [EOCPerson new];
aPerson.firstName = @"Bob"; // same as
[aPerson setFirstName:@"bob"];

NSString *lastName = aPerson.lastName;  // same as 
NSString *lastName = [aPerson lastName];
  • 使用了属性,编译器就会自动编写访问这些属性所需的方法,编译器还会自动向类中添加适当类型的实例变量,并且在属性名前加下划线,以作为实例变量的名字。上面的代码中,会生成两个实例变量,名称分别为_firstName_lastName

  • 可以使用@synthesize语法指定实例变量的名字,建议使用编译器生成的即可。

@implementation EOCPerson
@synthesize firstName = myFirstName;
@synthesize lastName = myLastName;
@end
  • 如果不想让编译器自动生成存取方法,可以自己实现。如果你只实现了其中一个存取方法,那么另外一个还是会由编译器自动生成。
  • 还可以使用@dynamic关键字阻止编译器自动生成存取方法,它会告诉编译器:不要自动创建实现属性所用的实例变量,也不要创建存取方法。在编译时,即使编译器发现没有存取方法,也不会报错,编译器相信,这些方法运行期一定能找到。
@implementation EOCPerson
@dynamic firstName, lastName;
@end
  • 如果没有自己实现存取方法,运行期编译器就找不到setter和getter方法而导致程序崩溃。

属性特质:属性可以拥有的特质分为四类

  • 原子性:定义属性的时候,默认就是atomic的。

  • 读/写权限

    • readwrite(读写):属性拥有setter方法和getter方法
    • readonly(只读):属性仅拥有getter方法
  • 内存管理语义:属性用于封装数据,而数据要有具体的所有权语义。编译器在合成存取方法的时候,要根据此特质决定生成的代码。

    • assignsetter方法只会执行针对纯量类型(CGFloat、NSInteger等)的简单赋值。
    • strong:此特质表明该属性定义了一种拥有关系,这种属性在设置新值时,setter方法会先保留新值,并释放旧值,然后将新值设置上去。
    • weak:此特质表明该属性定义了一种非拥有关系,这种属性在设置新值时,setter方法既不保留新值,也不释放旧值。此特质通assign类似,然而在属性所指的对象销毁时,属性值也会被清空,会指向nil
    • unsafe_unretain:此特质的语义和assign类似,但是它适用于对象类型,该特质表达非拥有关系,当目标对象销毁时,属性不会自动清空,不会指向nil
    • copy:此特质所表达的所属关系与strong类似。然而setter方法并不保留新值,而是将其拷贝。当属性类型为NSString*时,经常用此特质来保护其封装性。
    @interface ViewController ()
    @property (nonatomic, copy) NSString *str; // 使用 copy 修饰
    @property (nonatomic, strong) NSString *str; // 使用 strong 修饰
    @end
    
    // 首先,父类指针可以指向子类即NSString* 可以指向 NSMutableString*类型
    // 其次,给self.str 赋值时候,本质是调用了 setter方法
    NSMutableString *mStr = [NSMutableString stringWithFormat:@"abc"];
    self.str = mStr;
    NSLog(@"mstr - %@", mStr); //  mstr - abc
    NSLog(@"str - %@", self.str); // str - abc 
    [mStr appendString:@"123"];
    
    NSLog(@"mstr - %@", mStr); // mstr - abc123
    NSLog(@"str - %@", self.str); 
    // 如果使用copy修饰属性 打印结果是 str - abc,因为copy修饰在setter方法时会拷贝一个副本出来
    // 如果使用strong修饰属性 结果是 str - abc123
    
  • 方法名:定义属性时可以指定存取方法的方法名:

    • getter={name}:指定getter方法的方法名。如果某个属性时布尔类型,想为其添加is前缀,那么就可以用这个方法指定。
    • setter={name}:指定setter方法的方法名,通常不这么干。
  • 如果自己实现这些存取方法,那么应该保证在实现的时候,具备相关属性所声明的特质。比如,将某个属性设置为copy,那么就应该在setter方法中拷贝相关对象。

  • 如果想在其他地方设置属性值,同样要遵守属性定义中所宣称的语义。

@interface EOCPerson : NSObject
@property (copy) NSString *firstName;
@property (copy) NSString *lastName;
- (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName;
@end
  
// EOCPerosn.m
- (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName {
    if (self = [super init]) {
        _firstName = [firstName copy];
        _lastName = [lastName copy];
    }
    return self;
}

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

对象之外访问实例变量的时候,总是应该通过属性来访问。除了几种特殊情况外,在对象的内部读取实例变量的时候采用直接访问的形式,而在设置实例变量的时候通过属性来访问

看下面这个例子:

// EOCPerson.h
@interface EOCPerson : NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;

- (NSString *)fullName;
- (void)setFullName:(NSString *)fullName;
@end

// EOCPerson.m 使用点语法访问实例变量实现
- (NSString *)fullName {
  return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}

- (void)setFullName:(NSString *)fullName {
  NSArray *components = [fullName componentsSeparatedByString:@" "];
  self.firstName = [components objectAtIndex:0];
  self.lastName = [components objectAtIndex:1];
}

// EOCPerson.m 使用直接访问实例变量方式实现
- (NSString *)fullName {
    return [NSString stringWithFormat:@"%@ %@", _firstName, _lastName];
}

- (void)setFullName:(NSString *)fullName {
    NSArray *components = [fullName componentsSeparatedByString:@" "];
    _firstName = [components objectAtIndex:0];
    _lastName = [components objectAtIndex:1];
}

这两种写法有几个区别:

  • 由于不经过Objective-C的方法派发步骤,所以直接访问实例变量的速度比较快。编译器所生成的代码会直接访问保存对象实例变量的那块内存。
  • 直接访问实例变量,不会调用其setter方法,这样就绕过了属性定义时候内存管理语义。比如,ARC下声明了一个copy属性,那么因为直接访问实例变量,不会进行拷贝操作。
  • 如果直接访问实例变量,不会触发KVO通知。
  • 通过属性来访问实例变量,有助于排查错误,可以在settergetter方法中加断点,进行断点调试。

针对以上的问题,出现了一种折中的方案:在写入实例变量时,通过setter方法来做,而在读取实例变量时,则直接访问实例变量。此方法,既能提高读取的速度,还能控制对属性的写入,还能确保属性内存管理语义贯彻。

  • 选用这种方案,还需要注意几个问题:
    • 在初始化方法中,应该直接访问实例变量,因为子类可能会重写setter方法,如果在父类的初始化方法中使用了setter方法,当创建子类对象,父类初始化的时候,就会调用子类的重写的setter方法,从而产生报错。
    • 如果待初始化的变量在父类中,而我们又无法在子类直接访问实例变量,此时就需要调用setter方法了。
    • 懒加载,必须通过getter方法来调用,否则无法获取到真正的值,因为对象根本就没有被初始化。

相关文章

  • 读EffectiveObjective-C2.0(第六条、第七条

    第六条:理解”属性“这一概念 ”属性“是Objective-C的一项特性,用于封装对象中的数据。Objective...

  • effective object - C 读书笔记02

    第二章对象、消息、运行期 第六条:理解“属性”这一概念 第七条:在...

  • 四书五经(尚书《洪范》4)

    昨天学习了《洪范》中关于治国方针九条方法之六、七,第六条好懂,而第七条有点绕人,还好总算有点明白了。 今天继续学习...

  • 人生的十大信条

    第一条 健康 第二条 诚信 第三条 感恩 第四条 奉献 第五条 成长和学习 第六条 爱自已爱他人 第七条 ...

  • “父母规”12条

    今天看到王崧舟老师的公众号上发了一篇关于“父母规”的文章,对我有很大的警示作用,尤其是第六条和第七条,正是我现在正...

  • 我的十条优点

    第一条:心地善良 第二条:有责任心 第三条:单纯 第四条:心软 第五条:孝顺父母 第六条:疼爱老公 第七条:做事踏...

  • iOS设计模式四部曲(一):创建型模式 内附Demo

    最近刚重温完经典书籍《EffectiveObjective-C2.0编写高质量iOS与OSX代码的52个有效方法》...

  • 十条职场潜规则 你被“潜”过么?

    第一条 第二条 第三条 第四条 第五条 第六条 第七条 第八条 第九条 第十条 以上纯属娱乐,近期我们将推出干货版...

  • 读《第七条猎狗》有感

    读《第七条猎狗》有感 实验小学 五年级十班 厉耘廷 这个假期我读了一本叫《...

  • 读EffectiveObjective-C2.0(第五条)

    第五条:用枚举表示状态、选项、状态码 在以一系列常量来表示状态码或可组合的选项的时候,可以使用枚举命名。使用枚举可...

网友评论

      本文标题:读EffectiveObjective-C2.0(第六条、第七条

      本文链接:https://www.haomeiwen.com/subject/fseqvktx.html