iOS 开发中的争议(一)

这是该系列的第一篇,我想讨论的是:类的成员变量应该如何定义?

在 Objective-C 的语言的早期,类的私有成员变量是只能定义在 .h 的头文件里面的。像如下这样:

@interface ViewController : UIViewController {
    @private
    NSInteger _value;
}

之后,苹果改进了 Objective-C,允许在 .m 里面添加一个特殊的匿名 Category,即没有名字的 Category,来实现增加类的成员变量。像如下这样:

@interface ViewController ()

@property (nonatomic) NSInteger value;

@end

这样的好处是,这些变量在头文件中被彻底隐藏起来了,不用暴露给使用者。

接着,在 2013 年的 WWDC 中,苹果进一步改进了 Objective-C,允许在 .m 的
@implementation 中直接添加类的私有成员变量。像如下这样:

@implementation ViewController {
    NSInteger _value;
}

于是,大家对于如何定义私有的成员变量上就产生的分歧。许多人喜欢用匿名的 Category 的方式来定义私有成员变量。但是,我个人更推荐在 @implementation 中直接添加类的私有成员变量。下面我做一些解释。

历史原因

首先早期的 iOS 程序员一定知道,在 2011 年 ARC 被推出之前,Objective-C 是需要手工地管理引用计数的。而对类的所有私有成员使用 self.property 的形式,就可以使编译器为我们自动生成管理引用计数的代码。在 2012 年前,这个 feature 还需要使用 @synthesize 关键字来启用的。于是,苹果通过在代码规范中推荐和强调使用 self.property 的编程习惯,来让大家避免在内存管理中遇到问题。而在 ARC 时代,这个编程习惯带来的优势不再存在了,因为编译器会自动为我们管理引用计数,我们只需要关心不要造成循环引用问题就行了。

省心省事

刚刚说到,在类中完全使用 _property 的方式来访问私有成员变量,是不会有内存管理上的问题的。但是使用self.property 的方式来访问私有变量是不是也是一样不会有内存管理上的问题呢?确实也是,但是有一点需要注意:我们最好不要在 init 和 dealloc 中使用 self.property 的方式来访问成员变量。

所以,如果你用 self.property 来访问私有成员变量。那么你需要注意,在 init 和 dealloc 中不使用这种方式。这其实对程序员来说是一个负担,你需要不停提醒自己有没有犯错。如果你使用完全的 _property 的方式来访问私有成员变量,就不用想这一类问题了。

关于隐藏

大家知道,self.property 其实是调用了类的 [self property] 方法,所以这其实是有一层方法调用的隐藏,很多时候,我们需要延迟初使化一个类成员的时候,就会把这个成员的初使化方法写在这个 [self property] 方法的实现中。

那么问题来了,当你在阅读别人代码时,看到 self.property 的时候,你会想:这里会不会有一些隐藏的函数实现?于是你需要跳转到其方法实现中去查找。但是在实际开发中,大部分的 property 其实是使用编译器自动生成的 Getter 和 Setter 方法,于是你会找不到实现,这个时候,你才知道:“哦,原来这段代码并没有做自定义的成员初使化工作”。

这种默认的隐藏在代码中多了,会影响代码的阅读和维护。其实大部分的类成员变量都需要在类初使化方法中赋值,大部分的 UIViewController 的成员变量,都需要在 viewDidLoad 方法中赋值。那既然这样,不如直接在相应的方法中用一个名为setupProperty 方法直接进行初使化。这样的好处是,代码的可读性更好了,self.property 只有需要延迟初使化的情况下才被使用。

关于这个还有一个小故事,我之前 Review 过一个同事的 iOS 端代码,那个同事喜欢把 table view 的数据另外封装成一个类,而我觉得这些数据其实就是一个数组,没必要进行这一层封装,最终我们争论了比较久。我的观点是,一切隐藏都是对代码复杂性的增加,除非它带来了好处,例如达到了代码复用,提高了代码的可维护性等,否则,没有好处的封装只会给代码阅读理解带来成本。就我现在的经历中,大部分的 table view 的数据都可以放在一个数组中,没必要把这个数组封装起来,另外提供一套操作这个数组数据的方法。

简短的代码更易读

_property 的写法比 self.property 更短,也更简单。我认为这样写出来的代码更方便阅读。

执行速度更快,IPA体积更小

我之前从来没想到过这两者之间的速度和应用体积会有很大差别。不过一个同行(来自国外著名的社交网络公司)告诉我,他们公司发现二者还是有不小的差距,如果你们的应用需要做一些深度优化,可以考虑一下把 self.property 换成_property。但我觉得,大部分应用都应该是不需要做这种深度优化的。

KVO 和 KVC

是的,如果用 _property 这种写法,就不能使用 KVO 和 KVC 了。但是我得反问一下,在一个类的内部,KVO 自己的私有成员变量算是一个好设计吗?我们讲类要”高内聚,低耦合”,KVO 是为了实现观察者模式,让对象之间相互解耦的。如果把 KVO 用在类的内部,KVO 自己的私有成员,我认为其实这不是一个很好的设计。

Computed Properties

在 Swift 中,引入了 Computed Properties (https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html#//apple_ref/doc/uid/TP40014097-CH14-ID259)的概念,其实这在 Objective-C 中也有,只是没有专门给它名字。如果一个 property 我们提供了对应的 setter 和 getter,并且没有直接使用其对应的 _property 变量,那么这个 property 就是所谓的 Computed Properties。

是的,在类的内部如果直接使用 _property 形式,也无法使用 Computed Properties 了,但我认为这影响不大。其实 Computed Properties 也就是一层对数据存取的封装,我们另外实现两个函数,分别对应数据的 setter 和 getter 功能,就可以达到同样的效果。

循环引用问题

微博上的@王_晓磊在评论中提到:直接用私有变量有个需要特别注意的地方,在 block 里直接写 _property 相当于self->_property,虽然没写 self,但是暗含了对 self 的retain,容易造成循环引用。要记得用 weakSelf/strongSelf 大法。

这一点确实是被很多人忽视的,所以我也一并写在这里,感谢他的补充。

写在最后

其实我上面提到的这些问题都是小问题,影响不大。但是代码风格的统一却是大问题。所以不管你们项目中使用的是self.property 风格还是 _property 风格,问题都不大,但是如果你们同时使用这两种风格,那么就非常不好了。

希望这篇文章能让大家了解到在这方面的争论,也希望大家能够在这一点上,在公司内部达成统一。

时间: 2024-11-10 13:11:48

iOS 开发中的争议(一)的相关文章

iOS 开发中的争议(二)

这是该系列的第二篇.在本文中,我想讨论的是:对于 UI 界面的编写工作,到底应该用 xib/storyboard 完成,还是用手写代码来完成? 本着 “使用过才有发言权” 原则,我介绍一下我的经历: 最早在网易开发 “ 有道云笔记 “ 的时候,我们是使用 xib 来制作界面的. 三前年创业编写 “ 粉笔网 “ 的时候,我也是使用 xib 来制作界面的. 之后开发 “ 猿题库 “ 的时候,我尝试了一下使用 storyboard 来制作界面,但最后放弃了.之后我把大部分界面编写工作都改成由手写代码来

Re:iOS 开发中的争议(一、二)

原文:http://blog.devtang.com/blog/page/2/ 既有像 Twitter,Mailbox,objcio 这样完全不使用 xib 做界面的情况. 也有像 QQ.微信.滴滴打车.网易新闻.猿题库这样少量使用 xib 的情况. 也有像支付宝.大众点评这样重度使用 xib 的情况. 总结 其实,你完全不需要做一个 “艰难的决定”,你可以像 QQ 和微信那样,根据具体情况来选择性的使用 xib 和 storyboard.这里有我的一些建议: 对于复杂的.动态生成的界面,建议使

iOS开发中UIPopoverController的使用详解

这篇文章主要介绍了iOS开发中UIPopoverController的使用,代码基于传统的Objective-C,需要的朋友可以参考下 一.简单介绍 1.什么是UIPopoverController 是iPad开发中常见的一种控制器(在iPhone上不允许使用) 跟其他控制器不一样的是,它直接继承自NSObject,并非继承自UIViewController 它只占用部分屏幕空间来呈现信息,而且显示在屏幕的最前面 2.使用步骤 要想显示一个UIPopoverController,需要经过下列步骤

iOS开发中一些有用的小代码

1.判断邮箱格式是否正确的代码: //利用正则表达式验证 -(BOOL)isValidateEmail:(NSString *)email { NSString *emailRegex = @"[A-Z0-9a-z._%+-][email protected][A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"; NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES%@&qu

IOS开发中使用CNContact对通讯录增删改查

IOS开发中使用CNContact对通讯录增删改查 首先当然是把CNcontact包含在工程中: 1 @import Contacts; 1.下面是增加联系人的程序段: 1 CNMutableContact * contact = [[CNMutableContact alloc]init]; 2 contact.imageData = UIImagePNGRepresentation([UIImage imageNamed:@"22"]); 3 //设置名字 4 contact.gi

ios开发中-AFNetworking 的简单介绍

Blog: Draveness 关注仓库,及时获得更新: iOS-Source-Code-Analyze 在这一系列的文章中,我会对 AFNetworking 的源代码进行分析,深入了解一下它是如何构建的,如何在日常中完成发送 HTTP 请求.构建网络层这一任务. AFNetworking 是如今 iOS 开发中不可缺少的组件之一.它的 github 配置上是如下介绍的: Perhaps the most important feature of all, however, is the ama

iOS 开发中用户记住账户,密码

在iOS开发中经常会用到记住账户.密码,以此来提高用户的体验.下面就浅谈一下账户.密码的存储. 一.登录 记录已登录用户步骤,存入偏好设置中存储放入一个数组. 具体存储 1:存储用户到偏好设置中,其中用户是一个数组向服务器响应客户端后的一些操作(如果响应数据成功)其中用户和密码是一一对应的 1.1先从沙盒中偏好设置中读取对应的用户集合 读取用户名: NSMutableArray *AccArys = [NSMutableArray arrayWithArray:[[NSUserDefaults

iOS开发中KVC、KVO简介

在iOS开发中,KVC和KVO是经常被用到的.可以使用KVC对对象的属性赋值和取得对象的属性值,可以使用KVO监听对象属性值的变化.简单介绍一下KVC和KVO. 一:键值编码(KVC) KVC,全称 Key Value Coding(键值编码),是OC 语言的一个特性,使用KVC,可以对对象的属性进行动态读写. KVC的操作方法由 NSKeyValueCoding协议提供,而NSObject已经实现了这个协议,因此OC中的几乎所有对象都可以使用KVC操作.常用的KVC操作方法有: (1)设置属性

iOS开发中打电话发短信等功能的实现

在APP开发中,可能会涉及到打电话.发短信.发邮件等功能.比如说,通常一个产品的“关于”页面,会有开发者的联系方式,理想情况下,当用户点击该电话号码时,能够自动的帮用户拨出去,就涉及到了打电话的功能. iOS开发中,有三种方式可以打电话: (1)直接跳到拨号界面,代码如下 1 2 NSURL *url = [NSURL URLWithString:@"tel://10010"];  [[UIApplication sharedApplication] openURL:url]; 缺点: