1. Objective-C是动态运行时语言是什么意思?
主要是将数据类型的确定由编译时, 推迟到了运行时。
主要涉及两个概念: 运行时
、多态
。
运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象的指定方法。
多态
:不同对象以自己的方式响应相同的消息的能力叫多态。
例如:生物类都有一个共同方法 - (void)eat
;人属于生物,狗也属于生物,都继承相同东西,实现各自的eat
,但是调用时我们只需调用各自eat
方法。
不同对象以自己的方式响应了相同的消息,因此可以说运行时是多态的基础。
2. 讲一下MVC和MVVM,MVP?
MVC:
MVC 模式- Model 与 View 永远不能相互通信,只能通过 Controller 传递。
- Controller 可以直接与 Model 对话(读写调用 Model),Model 通过 Notification 和 KVO 机制与 Controller 间接通信。
- Controller 可以直接与 View 对话,通过 outlet,直接操作 View,outlet 直接对应到 View 中的控件,View 通过 action 向 Controller 报告事件的发生(如用户 Touch 了我),Controller 是 View 的直接数据源(数据很可能是 Controller 从 Model 中取得并经过加工了)。Controller 是 View 的代理(delegate),以同步 View 和 Controller。
MVP:
MVP 模式将 Controller 改名为 Presenter(提出者,任命者),同时改变了通信方向。
MVP 模式
- 各部分之间的通信都是双向的。
- View 和 Model 不发生关系,都通过 Presenter 传递。
- View 非常薄,不部署任何业务逻辑,称为"滚动视图"(Passive View),即没有任何主动性,而 Presenter 非常厚,所有部署都部署在这里。
3. 为什么代理要用weak?代理的delegate和dataSource有什么区别?block和代理的区别?
-
weak
修饰的对象不持有 delegate 这个对象,delegate 这个对象的销毁由外部控制。
strong
修饰的对象强引用,持有这个对象,外部不能销毁 delegate 对象,引起循环引用。 -
DataSource 是数据源,用来连接数据的,提供获取数据方法。
Delegate 是代理函数,提供一些操作方法。 -
代理和 block 都是回调机制,代理方法比较多,分散,block 代码比较集中统一。
block 出栈需要将数据从栈内存拷贝到堆内存,对象引用计数加1,使用完或者 block 置为nil 后才清除。
delegate 只是保存了一个对象指针,直接回调,没有额外消耗。
4.属性的实质是什么?包括哪几个部分?属性默认的关键字都有哪些?@dynamic关键字和@synthesize关键字是用来做什么的?
属性的本质:
@property = ivar + setter + getter;
属性包括三部分:
-
线程安全:
nonatomic
(线程不安全),atomic
(线程安全,setter、getter)。 -
内存管理:
strong
,retain
,assign
,weak
,copy
。
- strong:ARC 模式下,对对象进行强引用,释放旧值,设置新值,引用计数加1。
- retain:MRC 模式下,对对象进行强引用,释放旧值,设置新值,引用计数加1;
- assign:修饰基本数据类型,也可修饰 OC 对象,释放旧值,不保留新值,修饰的属性被释放后内存地址仍在,没有被释放,没被置为 nil,造成野指针,当再次使用这个属性,发生崩溃。
- weak:只能修饰 OC 对象,弱引用,修饰对象被释放后,指针地址也被置为 nil。 -
读写权限:
readwrite
(可读可写)、readonly
(只读)。
@dynamic
系统不会自动生成 setter
、getter
方法,编译时不会报错,运行时用到 setter
、getter
方法会崩溃。
@synthesize
(默认) 系统自动生成 setter
、getter
方法。
5. 属性的默认关键字是什么?
对于基本数据类型:atomic
、 readwrite
、assign
;
对于普通 OC 对象:atomic
、readwrite
、strong
;
6. NSString为什么要用copy关键字,如果用strong会有什么问题?(注意:这里没有说用strong就一定不行。使用copy和strong是看情况而定的)
因为 NSString
、NSDictionary
、NSArray
有对应的可变类型,他们经常会用到赋值操作,为保证字符串数值不会无意间被改动,应该在设置新属性值是拷贝一份。
若使用 strong,那么这个属性就可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性。
7. 如何令自己所写的对象具有拷贝功能?
若想另自己写的代码具有拷贝功能,则需要实现 NSCoping
协议,如果自定义的对象分为可变版本和不可变版本,那么就要同时实现NSCoping
、NSMutableCoping
协议。
例子:
ViewController.m
#import "ViewController.h"
#import "SecondViewController.h"
#import "PersonModel.h"
@interface ViewController ()
@end
@implementation ViewController
{
PersonModel *model;
}
- (void)viewDidLoad {
[super viewDidLoad];
model = [[PersonModel alloc] init];
model.name = @"张三";
model.sex = @"男";
}
- (void)viewWillAppear:(BOOL)animated
{
NSLog(@"%@---%@---%@", [self class], model.name, model.sex);
}
- (IBAction)buttonClicked:(UIButton *)sender
{
SecondViewController *vc = [[SecondViewController alloc] init];
vc.model = model;
[self.navigationController pushViewController:vc animated:YES];
}
Person.h
#import <Foundation/Foundation.h>
@interface PersonModel : NSObject <NSCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *sex;
@end
Person.m
#import "PersonModel.h"
@implementation PersonModel
//- (id)copyWithZone:(NSZone *)zone
//{
// PersonModel *model = [PersonModel allocWithZone:zone];
// return model;
//}
@end
SecondViewController.h
#import <UIKit/UIKit.h>
#import "PersonModel.h"
@interface SecondViewController : UIViewController
@property (nonatomic, strong) PersonModel *model;
//@property (nonatomic, copy) PersonModel *model;
@end
SecondViewController.m
#import "SecondViewController.h"
@interface SecondViewController ()
@end
@implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
_model.name = @"哈哈";
NSLog(@"%@---%@", _model.name, _model.sex);
}
@end
使用 Strong 传输自定义 Model 输出结果:
2017-06-13 18:03:06.739 NSCoping[8834:261731] ViewController---张三---男
2017-06-13 18:03:53.154 NSCoping[8834:261731] 哈哈---男
2017-06-13 18:04:12.714 NSCoping[8834:261731] ViewController---哈哈---男
分析:第一次打印为进入首页时数据,
第二次打印为跳转后修改了 name 后打印的结果,
第三次打印为返回首页打印的结果,
第三次打印结果中因为 SecondViewcontroller.h 中 Person 使用了 strong, 不需要遵循 NSCoping 协议,返回首页时把原来的值也给修改了。
使用 copy 传输自定义 Model 输出结果:
2017-06-13 18:10:07.668 NSCoping[8979:265669] ViewController---张三---男
2017-06-13 18:10:09.105 NSCoping[8979:265669] 哈哈---(null)
2017-06-13 18:10:10.997 NSCoping[8979:265669] ViewController---张三---男
发现第三次的打印结果和第一次相比没有变化。
8. 可变集合类 和 不可变集合类的 copy 和 mutablecopy有什么区别?如果是集合是内容复制的话,集合里面的元素也是内容复制么?
例子:
mutablecopy:
ViewController.h
- (IBAction)buttonClicked:(UIButton *)sender
{
NSArray *dataSource = @[@"张三", @"李四", @"王五"];
NSLog(@"%p", dataSource);
ThirdViewController *vc = [[ThirdViewController alloc] init];
vc.dataSource = [dataSource mutableCopy];
[self.navigationController pushViewController:vc animated:YES];
}
ThirdViewController.h
#import <UIKit/UIKit.h>
@interface ThirdViewController : UIViewController
@property (nonatomic, copy) NSArray *dataSource;
@end
ThirdViewController.m
#import "ThirdViewController.h"
@interface ThirdViewController ()
@end
@implementation ThirdViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
NSLog(@"%p", _dataSource);
}
@end
输出结果
2017-06-14 10:28:58.155 NSCoping[2369:51665] 0x600000057850
2017-06-14 10:28:58.164 NSCoping[2369:51665] 0x60000004bd00
copy
ViewController.h
- (IBAction)buttonClicked:(UIButton *)sender
{
NSArray *dataSource = @[@"张三", @"李四", @"王五"];
NSLog(@"%p", dataSource);
ThirdViewController *vc = [[ThirdViewController alloc] init];
vc.dataSource = [dataSource mutableCopy];
[self.navigationController pushViewController:vc animated:YES];
}
输出结果:
2017-06-14 10:33:06.690 NSCoping[2433:53481] 0x600000052660
2017-06-14 10:33:06.698 NSCoping[2433:53481] 0x600000052660
由此得出结论:
**不可变集合的 copy 是浅拷贝,mutablecopy 是深拷贝,重新拷贝了对象的指针地址。
同理实验可以得出:可变集合的 copy 和 mutablecopy 都是深拷贝,因为对象的指针地址都发生了改变。
**
9. 为什么IBOutlet修饰的UIView也使用weak关键字?
当我们把控件拖到 StoryBoard 上时,相当于把这个控件加到了视图控制器的 View
上,View
上有一个 addSubView
属性,这个属性是一个数组,里面所有的 View
都添加在这个数组中, View
对加到它上面的对象是强引用,当我们使用 Outlet
属性时,我们仅仅在 View
,Controller
中使用,没有必要拥有它,所有是weak
。
10. nonatomic和atomic的区别?atomic是绝对的线程安全么?为什么?如果不是,那应该如何实现?
nonatomic
和atomic
主要区别在于系统生成的 setter/getter
方法不一样。如果自己写 setter/getter
,那 atomic/nonatomic/retain/assign/copy
这些关键字只起提示作用,写不写都一样。
对于atomic
的属性,系统生成的setter/getter
会保证get
、set
操作的完整性,不受其他线程影响。比如,线程 A 的 getter
方法运行到一半,线程 B 调用了setter
方法,那么线程 A 的getter
还是能得到一个完整的对象。
而nonatomic
就不能保证线程完整了,所有,nonatomic
比atomic
速度要快。
不过atomic
并不能保证线程安全,如果 A 调了getter
,与此同时,线程 B、线程 C 都调了setter
,---那最后线程 A get 到的值,3种都有可能:可能 B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同时,最终这个属性的值,可能是 B set 的值, 也可能是 C set 的值。
11. 了解hitText 方法事件传递?
hitText作用: 当在一个屏幕上添加一个屏幕罩,但又不影响下面 view 的操作时,也就是透过屏幕罩对下面的 view 进行操作。
举例说明:(陌陌面试题)
问题描述:将 UIScrollView 和 UIView 都添加到 Controller 的 view 上,可以拖动 UIView, UIScrollView 也可以滚动。
示例图
调用过程:
iOS系统检测到手指触摸(Touch)操作时会将其放入当前活动Application的事件队列,UIApplication会从事件队列中取出触摸事件并传递给key window(当前接收用户事件的窗口)处理,window对象首先会使用
hitTest:withEvent:
方法寻找此次Touch操作初始点所在的视图(View),即需要将触摸事件传递给其处理的视图,称之为hit-test view。
window对象会在首先在view hierarchy的顶级view上调用hitTest:withEvent:,此方法会在视图层级结构中的每个视图上调用pointInside:withEvent:,如果pointInside:withEvent:
返回YES,则继续逐级调用,直到找到touch操作发生的位置,这个视图也就是hit-test view。
hitText:withEvent:调用方法处理流程:
- 首先调用当前视图的
pointInside:withEvent:
方法判断触摸点是否在当前视图内;
- 若返回NO,则
hitTest:withEvent:
返回nil; - 若返回YES,则向当前视图的所有子视图(subviews)发送
hitTest:withEvent:
消息,所有子视图的遍历顺序是从top到bottom,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕; - 若第一次有子视图返回非空对象,则
hitTest:withEvent:
方法返回此对象,处理结束; - 如所有子视图都返回非,则
hitTest:withEvent:
方法返回自身 self 。
主要代码:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
//1.判断下自己能否接收事件
if (self.alpha <= 0.01) {
return nil;
}
//2.判断是否在当前区域内点击
if ([self pointInside:point withEvent:event] == NO) {
return nil;
}
else
{
//3.把当前坐标系上的点转换成父视图上的点
CGPoint newPoint = [self convertPoint:point toView:self.superview];
//4.获取想要响应的 view
UIView *view = self.superview.subviews[0];
if (CGRectContainsPoint(view.frame, newPoint)) {
return view;
}
else
{
//如果没有比自己合适的子控件,最适合的 View 就是自己
return self;
}
}
}
网友评论