美文网首页iOS日常须知
iOS 开发总结(精华篇一)

iOS 开发总结(精华篇一)

作者: Lizzzzzzhen | 来源:发表于2017-06-21 10:39 被阅读316次

    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)。

    • 内存管理:strongretainassignweakcopy
      - strong:ARC 模式下,对对象进行强引用,释放旧值,设置新值,引用计数加1。
      - retain:MRC 模式下,对对象进行强引用,释放旧值,设置新值,引用计数加1;
      - assign:修饰基本数据类型,也可修饰 OC 对象,释放旧值,不保留新值,修饰的属性被释放后内存地址仍在,没有被释放,没被置为 nil,造成野指针,当再次使用这个属性,发生崩溃。
      - weak:只能修饰 OC 对象,弱引用,修饰对象被释放后,指针地址也被置为 nil。

    • 读写权限:readwrite(可读可写)、readonly(只读)。

    @dynamic 系统不会自动生成 settergetter 方法,编译时不会报错,运行时用到 settergetter 方法会崩溃。
    @synthesize(默认) 系统自动生成 settergetter 方法。

    5. 属性的默认关键字是什么?

    对于基本数据类型:atomicreadwriteassign;
    对于普通 OC 对象:atomicreadwritestrong

    6. NSString为什么要用copy关键字,如果用strong会有什么问题?(注意:这里没有说用strong就一定不行。使用copy和strong是看情况而定的)

    因为 NSStringNSDictionaryNSArray 有对应的可变类型,他们经常会用到赋值操作,为保证字符串数值不会无意间被改动,应该在设置新属性值是拷贝一份。
    若使用 strong,那么这个属性就可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性。

    7. 如何令自己所写的对象具有拷贝功能?

    若想另自己写的代码具有拷贝功能,则需要实现 NSCoping 协议,如果自定义的对象分为可变版本和不可变版本,那么就要同时实现NSCopingNSMutableCoping协议。
    例子:

    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---张三---男

    发现第三次的打印结果和第一次相比没有变化。

    代码demo

    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属性时,我们仅仅在 ViewController中使用,没有必要拥有它,所有是weak

    10. nonatomic和atomic的区别?atomic是绝对的线程安全么?为什么?如果不是,那应该如何实现?

    nonatomicatomic主要区别在于系统生成的 setter/getter 方法不一样。如果自己写 setter/getter,那 atomic/nonatomic/retain/assign/copy 这些关键字只起提示作用,写不写都一样。

    对于atomic的属性,系统生成的setter/getter会保证getset操作的完整性,不受其他线程影响。比如,线程 A 的 getter方法运行到一半,线程 B 调用了setter方法,那么线程 A 的getter还是能得到一个完整的对象。

    nonatomic就不能保证线程完整了,所有,nonatomicatomic速度要快。

    不过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:调用方法处理流程:

    1. 首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内;
    1. 若返回NO,则hitTest:withEvent:返回nil;
    2. 若返回YES,则向当前视图的所有子视图(subviews)发送hitTest:withEvent:消息,所有子视图的遍历顺序是从top到bottom,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕;
    3. 若第一次有子视图返回非空对象,则hitTest:withEvent:方法返回此对象,处理结束;
    4. 如所有子视图都返回非,则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;
            }
        }
    }
    

    demo 下载地址

    相关文章

      网友评论

        本文标题:iOS 开发总结(精华篇一)

        本文链接:https://www.haomeiwen.com/subject/adpvittx.html