看了眼 tinyfool 经常提的日历控件

2016-11-27 18:49:43 +08:00
 EagleB

先贴上源码地址:Here 代码是是 08 年写的,这点还是非常服的,这个时候我还在上高中。 08 年到现在 OC 在一些特性上改进很多,其中使用的CFGregorianDate相关 api 在 iOS 8.0 之后也不建议使用了,所以这里我只说我确认不是好的实践的部分。

先来看下头文件:

//
//  CalendarView.h
//  ZhangBen
//
//  Created by tinyfool on 08-10-26.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

#import <UIKit/UIKit.h>

@protocol CalendarViewDelegate;

@interface TdCalendarView : UIView {
	CFGregorianDate currentMonthDate;
	CFGregorianDate currentSelectDate;
	CFAbsoluteTime	currentTime;
	UIImageView* viewImageView;
	id<CalendarViewDelegate> calendarViewDelegate;
	int *monthFlagArray; 
}

@property CFGregorianDate currentMonthDate;
@property CFGregorianDate currentSelectDate;
@property CFAbsoluteTime  currentTime;

@property (nonatomic, retain) UIImageView* viewImageView;
@property (nonatomic, assign) id<CalendarViewDelegate> calendarViewDelegate;
-(int)getDayCountOfaMonth:(CFGregorianDate)date;
-(int)getMonthWeekday:(CFGregorianDate)date;
-(int)getDayFlag:(int)day;
-(void)setDayFlag:(int)day flag:(int)flag;
-(void)clearAllDayFlag;
@end



@protocol CalendarViewDelegate<NSObject>
@optional
- (void) selectDateChanged:(CFGregorianDate) selectDate;
- (void) monthChanged:(CFGregorianDate) currentMonth viewLeftTop:(CGPoint)viewLeftTop height:(float)height;
- (void) beforeMonthChange:(TdCalendarView*) calendarView willto:(CFGregorianDate) currentMonth;
@end

代码格式我就不说了,空格写的很随意,大小写对于我这种强迫症来说也很受不了。最重要的是我认为稍微参考下苹果 framework 中函数的命名方式,就不会写出协议中这么扯淡的命名。 interface 中的命名大多数都还不错,但是-(void)setDayFlag:(int)day flag:(int)flag;这个是什么鬼,我认为- (void)setFlag:(int)flag forDay:(int)day;更好。

下面来看下源文件:

源文件中定义了全局的几个变量:

const float headHeight=60;
const float itemHeight=35;
const float prevNextButtonSize=20;
const float prevNextButtonSpaceWidth=15;
const float prevNextButtonSpaceHeight=12;
const float titleFontSize=30;
const int	weekFontSize=12;

这几个变量主要是来控制试图的大小、字体的大小,只有在这个文件内部试哟哦难过,没有用static修饰,不是很明白,可能 tinyfool 记忆里好,不会再工程中其他地方使用相同名字的变量。

学 C 语言的时候,大概都做过这么一个练习:获取某年某月的天数。来看看 tinyfool 的实现:**

-(int)getDayCountOfaMonth:(CFGregorianDate)date{
	switch (date.month) {
		case 1:
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		case 12:
			return 31;
			
		case 2:
			if((date.year % 4==0 && date.year % 100!=0) || date.year % 400==0)
				return 29;
			else
				return 28;
		case 4:
		case 6:
		case 9:		
		case 11:
			return 30;
		default:
			return 31;
	}
}

interesting ,自行体会一下。

实现中有很多类似的实践(个人认为这不是好的实践):

- (void)movePrevMonth{
	if(currentMonthDate.month>1)
		currentMonthDate.month-=1;
	else
	{
		currentMonthDate.month=12;
		currentMonthDate.year-=1;
	}
	[self movePrevNext:0];
}

再来看下这个类的析构函数:

- (void)dealloc {
    [super dealloc];
    free(monthFlagArray);
}

Excuse me. 这里虽然不太可能出现崩溃的情况,但不是应该先释放子类持有的资源?

最后,总结一些 tinyfool 的变量命名风格:title_MonthweekfonttabHeights_width


我观察到的是,如果有人在微博上反驳他什么,他通常的回应是毫无道理(有道理的时候也有,少),其他的不想评价,怕被喷。

8508 次点击
所在节点    程序员
63 条回复
jun0205
2016-11-28 10:03:33 +08:00
早期 iOS 开发和现在差别太大,那时候资料少,开源的更少,什么东西都得自己写。
不像现在什么各种优秀的资源都有,没法比较。
tinyfool
2016-11-28 10:13:47 +08:00
@muziki 这个谁要写这个代码在我的项目里面,我要打不及格。

第一,不见得快,这个可以跑一个测试,比如比较 100 万次,试试看,我估计不见得快,或者说不见得有显著的性能收益。
第二,可读性下降。
第三,什么场景需要快速计算闰年,全人类才走了几千年,枚举一边,用最慢的算法,也很快出结果。这么去追求效率得不偿失。
tinyfool
2016-11-28 10:21:13 +08:00
@juice 续啥费?
alexsunxl
2016-11-28 10:22:50 +08:00
@LedChang 喷一喷代码其实没问题, 但总体上感觉楼主太针对人, 这就不太好了。
所以 tiny 叔才不得不出来解释一通
holyghost
2016-11-28 10:24:22 +08:00
& 3 比 % 4 快难道不是常识么

我咋就这么瞧不上这些技术圈的网红呢,还有那个什么 justjavac
wanttofly
2016-11-28 10:40:25 +08:00
@LedChang “代码格式我就不说了,空格写的很随意,大小写对于我这种强迫症来说也很受不了。最重要的是我认为稍微参考下苹果 framework 中函数的命名方式,就不会写出协议中这么扯淡的命名。 interface 中的命名大多数都还不错,但是-(void)setDayFlag:(int)day flag:(int)flag;这个是什么鬼,我认为- (void)setFlag:(int)flag forDay:(int)day;更好。” 态度有问题,说啥都是白扯。内容没毛病,但是比如说可以小声的告诉“ hi ,你裤子好像有点问题”的情况下为什么要拿着喇叭打声的说“我靠,你的前开门没拉”呢
tinyfool
2016-11-28 10:41:22 +08:00
@holyghost 没错,这是常识,不过我懒得细看你的实现。

我做了一个简单的 benchmark 你可以看看

//
// main.m
// testleapyear
//
// Created by Peiqiang Hao on 2016/11/28.
// Copyright © 2016 年 Peiqiang Hao. All rights reserved.
//

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...

NSTimeInterval t1 = [[NSDate date] timeIntervalSince1970];

for (int i = 0; i< 1000000; i++) {

int year = i;
int leapYear = (year & 3) == 0 && ((year % 25) != 0 || (year & 15) == 0);
}
NSTimeInterval t2 = [[NSDate date] timeIntervalSince1970];
NSLog(@"%g",t2-t1);

t1 = [[NSDate date] timeIntervalSince1970];
for (int i = 0; i< 1000000; i++) {

int year = i;
int leapYear = ((year % 4==0 && year % 100!=0) || year % 400==0);
}
t2 = [[NSDate date] timeIntervalSince1970];
NSLog(@"%g",t2-t1);

}
return 0;
}

结果是:

2016-11-28 10:37:54.885253 testleapyear[16140:987950] 0.00339603
2016-11-28 10:37:54.893403 testleapyear[16140:987950] 0.00789499

在 100 万次调用下,你的实现比我快了 4 个毫秒。在我看来,这样的优化,千万不要做,得不偿失。当然如果你觉得我的测试条件写错了,你可以改,我可以再跑一次。运行环境是我的 rmbp 15 寸低配,去年中期的配置。
caiyouzai
2016-11-28 10:47:09 +08:00
小码农提一句,这种实际上现实中每天调用都不会超过 5000 次的接口,代码可读性,比那 0.0001 秒的优化有意义多了把
tinyfool
2016-11-28 10:50:21 +08:00
@caiyouzai 对的。我以前做搜索, 1 个 ms 都很重要。但是,不是每个代码的 1 个 ms 都要去揪。要找到核心代码,去压榨一点点性能改善,但是调用比较少的,对整体影响不大的,连看都不看。
tinyfool
2016-11-28 11:07:05 +08:00
@holyghost 我没有说你的知识不对的意思。包括 @EagleB

高德纳老师有句话叫做“过早优化是万恶之源。”,原文, We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.

当你关心了一个明显不是性能攸关的点的时候,我就担心你的大局观了。刚才说了求月日数,一次翻页才调用一次,闰年判断也如此。这根本不是性能攸关点。

其次,一个操作比另外一个操作快,是很常见的。这很正常,但是,按照刚才的 benchmark 在当前测试条件下快了不到一倍。如果本身很耗时,不到一倍也是不错的回报。问题是运行 100 万次都才 7 个 ms ,这快了一倍叫做没有收益。

在实战的性能优化里面,我们关心的第一是运行多次的,性能攸关点。复杂度的增长速度。以及那些差异到了 100-1000 倍,甚至更大的操作。比如内存和硬盘,有了 SSD 没差那么远了,但是仍旧很大。比如硬盘的连续读写和随机读写,比如硬盘,内存和网络。

现在的问题是,卡马克的某个优化很牛逼,某个图形算法里面乘除 2 用位运算(不管是图形算法啦,系统库里面多了去了),这些都没错。

但是人家也不是每句话,都要把乘除 2 改为位运算的。计算机发展的早期,确实有很多这么干的,但是慢慢的都认识到了,有些优化要做,某些最好不做,最好不早做。
tinyfool
2016-11-28 11:19:37 +08:00
全文是 We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%. 刚才少引用了最后一句。

跟我之前做 Java 性能优化的体验一致,一个日撑 10 万次请求的服务,我优化到了 30 万,后来 300 万,后来 3000 万。其实没改多少代码,每一步的核心都是找当年的性能瓶颈在哪里。

我们家 CTO 当年优化的一个 iOS 的地图点聚合问题,源代码是苹果 WWDC 的一个 Demo 代码,从全屏 5000 个点,聚合一次需要 22 秒,优化到了几十毫秒,他优化了 7-8 个不同的地方,但是优化的代码量并不大。核心还是找出关键问题。
tommybiteme
2016-11-28 12:03:30 +08:00
@tinyfool tiny 大神好 6 啊
sumstain77
2016-11-28 13:15:17 +08:00
@zonghua
ren2881971
2016-11-28 13:24:56 +08:00
我觉得这是一个圈粉帖。。。
holy_sin
2016-11-28 13:25:55 +08:00
不要把时间花在指责上
Jaylee
2016-11-28 14:10:11 +08:00
@holyghost justjavac 就是一个 sb, 见一次骂一次
timeship
2016-11-28 14:55:46 +08:00
某位成功把话题转到了优化上
tinyfool
2016-11-28 15:03:12 +08:00
@timeship 嗯,成功转移了
isaced
2016-11-28 15:17:53 +08:00
“正在看这个贴,老大默默走到我的身后,一巴掌拍向我的脑袋说道:少说话,多做事!”
tinyfool
2016-11-28 15:25:57 +08:00
@isaced 嗯,这个代码才 500 行,目前这个帖子应该早就超过 500 行了吧

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/323650

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX