iOS 笔记

UIView动画

使用[UIView animateWithDuration:…]执行动画改变view期间不宜重新加载view中的数据,尤其是有滑动操作的时候。(比如UITableView、UICollectionView等,最好在动画执行之前重载数据。)

1
2
3
4
5
6
7
8
9
10
11
12
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//最好在这里执行reloadData
[self.tableView reloadData];
[UIView animateWithDuration:0.4 animations:^{
self.tableView.contentOffset = CGPointZero;
self.tableView.frame = CGRectMake(0, 100, 414, 500);
}];

//在这里执行reloadData容易crash
[self.tableView reloadData];
}

runtime理解

runtime是一套可以进行一些非常底层的用OC无法实现的操作的纯C语言的API

Objective-C类由一个指向objc_class结构体的指针来表示:

1
2
//An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

objc/runtime.h中objc_class结构体的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;//类名
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;//该类的成员变量列表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;//缓存最近使用的方法
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

一个类的实例由结构体objc_object来表示:

1
2
3
4
5
6
7
//Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};

//A pointer to an instance of a class.
typedef struct objc_object *id;

当我们向一个Objective-C对象发送消息时,运行时库会根据实例对象的isa指针找到这个实例对象所属的类。Runtime库会在类或其父类的方法列表中查询并执行与消息对应的selector指向的方法。

元类(MetaClass)是一个类对象的类

所有的类自身也是一个对象,我们可以向这个对象发送消息—调用类方法。

meta-class也是一个类,也可以向它发送一个消息。Objective-C的设计者让所有的meta-class的isa指向基类的meta-class,以此作为它们的所属类。即,任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类,而基类的meta-class的isa指针是指向它自己。这样就形成了一个完美的闭环。

1
2
3
4
5
6
7
8
9
10
11
12
UIView *testView = [[UIView alloc] init];

NSLog(@"%p", self);
NSLog(@"%s", class_getName([testView class] ));
NSLog(@"class is %s, superclass is %s", class_getName([self class]),
class_getName([self superclass]));

Class currentClass = [self class];
for (int i = 0; i < 3; i++) {
NSLog(@"Following the isa pointer %d times gives %p", i, currentClass);
currentClass = objc_getClass((__bridge void *)currentClass);
}

控制台输出:

1
2
3
4
5
6
0x7fa410c1f990
UIView
class is ViewController, superclass is UIViewController
Following the isa pointer 0 times gives 0x101c4e6c0
Following the isa pointer 1 times gives 0x0
Following the isa pointer 2 times gives 0x0

日历(NSCalendar)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//日历
static const NSCalendarUnit CalendarUnitYMD = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay;
NSCalendar *calendar = [NSCalendar currentCalendar];
//计算当月第一天
NSDateComponents *component = [calendar components:CalendarUnitYMD fromDate:[NSDate date]];
[component setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
component.day = 1;
NSDate *currentDateFirstMonthDay = [calendar dateFromComponents:component];
NSLog(@"%@", currentDateFirstMonthDay);

//计算当月最后一天
component.month++;
component.day = 0;
NSLog(@"%@", [calendar dateFromComponents:component]);

//计算相对日期
NSDateComponents *offsetComponent = [[NSDateComponents alloc] init];
offsetComponent.day = 50;
NSLog(@"%@", [calendar dateByAddingComponents:offsetComponent toDate:currentDateFirstMonthDay options:0]);

//日期对应的星期
NSArray *weekDaySymbols = [[[NSDateFormatter alloc] init] shortWeekdaySymbols];
NSDateComponents *dateComponents = [calendar components:NSCalendarUnitWeekday fromDate:currentDateFirstMonthDay];
NSLog(@"%@", weekDaySymbols[dateComponents.weekday - 1]);

//每个月的周数,iOS8以下使用NSWeekCalendarUnit代替NSCalendarUnitWeekOfMonth
NSRange rangeOfweeks = [calendar rangeOfUnit:NSCalendarUnitWeekOfMonth inUnit:NSCalendarUnitMonth forDate:currentDateFirstMonthDay];
NSLog(@"%ld", (long)rangeOfweeks.length);

//计算日期间的天数,通过修改NSCalendarUnit可以间隔的月、年等等
NSLog(@"%ld", (long)[calendar components:NSCalendarUnitDay fromDate:currentDateFirstMonthDay toDate:[NSDate date] options:0].day);

控制台输出:

1
2
3
4
5
6
2016-01-26 23:11:27.038 Test123[6502:707948] 2016-01-01 00:00:00 +0000
2016-01-26 23:11:27.038 Test123[6502:707948] 2016-01-31 00:00:00 +0000
2016-01-26 23:11:27.038 Test123[6502:707948] 2016-02-20 00:00:00 +0000
2016-01-26 23:11:27.039 Test123[6502:707948] 周五
2016-01-26 23:11:27.039 Test123[6502:707948] 6
2016-01-26 23:11:27.040 Test123[6502:707948] 25