我的笔记整理(二)

前言

这是近期的笔记整理,主要是项目里碰到的一些小问题,自己记录下来,在小小的拓展一下,毕竟“知其所以然”也是非常重要的嘛~

frame 与 bounds

在定义各种 view 的位置时,我们很经常会用到 frame 这个属性,又时候也会用到 bound,他们两者有什么区别呢?

是最近项目里一个诡异的现象引起我的注意,为了更好的展示效果,我现在去复现一下这个bug,我真是机智:

我描述一下:

这是一个地点选择的页面,左上角的 button 根据所选泽的地点的名称长短动态的去修改 button 的宽。在自定义button文件用的代码大概是这样:

1
[self setFrame:CGRectMake(0, 0, length, 40)];

而这个button在页面初始化的时候,就已经:

1
2
UIBarButtonItem *btnItem = [[UIBarButtonItem alloc] initWithCustomView:button];
[self.viewController.navigationItem setRightBarButtonItem:btnItem animated:YES];

而当每次我点击 pickerview 为 其选择地点而导致调用了:

1
[self setFrame:CGRectMake(0, 0, length, 40)];

你发现了吗?!!
按钮先飘逸到导航栏的坐标,然后再次漂移回来。excuse me???

iOS开发的一个缺点就是看不见源码,这有时候会让我们看到一些现象感觉莫名其妙。

但是解决的方法总是有的:

通过打印 button 的父视图,我发现竟然不是我所以为的 btnItem 或者是说 navigationItem;而是直接的这个:UINavigationBar

这样它会漂移到左边的原因就比较明显了,我以为它的父控件会是已经固定好在左上角的 navigationItem,所以设置了他的xy坐标为(0,0);导致他位移。

后来又能漂移回来,可能是 iOS 本身对 navigationItem 的处理吧?这个也只是我的猜测,没有源码,只能进行最大程度的推理。

那么应该怎么解决呢?

这时候就需要 bounds 的登场了。

Standford 的教学视频里面的是这样区别 frame 和 bounds 的。

frame: 该view在父view坐标系统中的位置和大小。(参照点是,父亲的坐标系统)

bounds: 该view在本地坐标系统中的位置和大小。(参照点是,本地坐标系统,就相当于自己的坐标系统,以0,0点为起点)

下面这段代码会比较清楚的解释:

1
2
3
4
5
6
7
8
9
10
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 280, 250)];  
[view1 setBounds:CGRectMake(-20, -20, 280, 250)];
view1.backgroundColor = [UIColor redColor];
[self.view addSubview:view1];//添加到self.view
NSLog(@"view1 frame:%@========view1 bounds:%@",NSStringFromCGRect(view1.frame),NSStringFromCGRect(view1.bounds));

UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
view2.backgroundColor = [UIColor yellowColor];
[view1 addSubview:view2];//添加到view1上,[此时view1坐标系左上角起点为(-20,-20)]
NSLog(@"view2 frame:%@========view2 bounds:%@",NSStringFromCGRect(view2.frame),NSStringFromCGRect(view2.bounds));

运行的结果如下:

总结一下:bounds 改变的是自己本身坐标原点的相对坐标值,影响的的是自己的子视图的位置。

所以上面,bug的解决方式就是:

1
[self setBounds:CGRectMake(0, 0, length, 40)];

单例模式的实现

什么是单例模式

单例模式用于当一个类只能有一个实例的时候, 通常情况下这个“单例”代表的是某一个物理设备比如打印机,或是某种不可以有多个实例同时存在的虚拟资源或是系统属性比如一个程序的某个引擎或是数据。用单例模式加以控制是非常有必要的。

举个最简单的例子,网易云音乐的播放器controller就是用的就是单例实现,我们可以从不同的入口访问到同一个实例。

单例模式需要达到的目的

  • 封装一个共享的资源

  • 提供一个固定的实例创建方法

  • 提供一个标准的实例访问接口

创建的方法

以创建一个MySingletonClass的单例模式为例。首先,我们需要定义一个类MySingletonClass.

1
2
3
- @interface MySingletonClass:NSObject {
-
- }

synchronized

1
2
3
4
5
6
7
8
9
static MySingletonClass *sharedCLDelegate = nil;
+(MySingletonClass *)sharedInstance{
@synchronized(self) {
if(sharedCLDelegate == nil) {
[[[self class] alloc] init]; // assignment not done here
- }
- }
- return sharedCLDelegate;
- }

在上面的代码中(用到了关键字@synchronized是为了保证我们的单例的线程级别的安全,可以适用于多线程模式下。)static变量sharedCLDelegate用于存储一个单例的指针,并且强制所有对该变量的访问都必须通过类方法 +(id)sharedInstance,在对 +(id)sharedInstance 第一次调用时候完成实例的创建。这里值得留意一下的是,上面代码中用的是[[selfclass] alloc],而不是 [MySingletonClass alloc],一般情况下这两种写法产生同样的效果。

但是这里这样做是为了更好的利用面向对象的性质,[self class]可以动态查找并确定类的类型从而便于实现对该类的子类化。

GCD

这是官方推荐的方法:

1
2
3
4
5
6
7
8
9
+ (AccountManager *)sharedManager
{
static MySingletonClass *sharedCLDelegate = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
sharedCLDelegate = [[self alloc] init];
});
return sharedCLDelegate;
}