iOS基础

1. 为什么说Objective-C是一门动态的语言?

object-c类的类型和数据变量的类型都是在运行是确定的,而不是在编译时确定。例如:多态特性,我们可以使用父类对象来指向子类对象,并且可以用来调用子类的方法。运行时(runtime)特性,我们可以动态的添加方法,或者替换方法

2. 讲一下MVC和MVVM,MVP?

http://blog.csdn.net/hudan2714/article/details/50990359

3. 为什么代理要用weak?代理的delegate和dataSource有什么区别?block和代理的区别?

防止循环引用。例如View有一个协议,需要一个代理实现回调。一个Controller添加这个View,并且遵守协议,成为View的代理。如果不用week,用strong,Controller ->View -> delegate -> Controller,就循环引用了。

delegate偏重于与用户交互的回调,有那些方法可以供我使用,例如UITableviewDelegate;dataSource偏重于数据的回调,view里面有什么东西,属性都是什么,例如UITableviewDatasource;

代理 可读性高 大部分可以属性
block 写的代码少 一般作为参数
通知 占用资源

无论是block还是delegate模式本质上都是回调,使用block,其优点是回调的block代码块直接就放在了block赋值的地方,使代码更为紧凑,缺点是block内使用到当前类的实例变量的时候,需要注意循环引用的问题,即需要使用__block(MRC下)或者__weak(ARC下)定义一个弱引用的self出来,block里面使用弱引用的self去操作属性或调用方法。delegate模式不用像block一样做特殊处理,但是如果多个对象设置的代理是同一个对象,就需要在delegate方法中判断当前执行代理的是哪个对象。

Datasource和Delegate两者的区别:

Datasource 是在告诉使用者之前的view中都有什么东西,有什么属性啊,属性的值都是多少,是只关于数据的东西。
Delegate 是在告诉使用者之前的view有什么方法可以供我调用。
一个是数据,一个是操作.

block和代理的区别:

首先两者作用是一样的,都是进行单一回调。不同的是,delegate是个对象,然后用过一个对象自己调用代理协议函数来完成整个流程。block是传递一个函数指针,利用函数指针执行来进行回调。还有在内存管理上需要注意,delegate不需要保存引用。block对引用数据有copy的处理

4. 属性的实质是什么?包括哪几个部分?属性默认的关键字都有哪些?@dynamic关键字和@synthesize关键字是用来做什么的?

属性的组成: @property = ivar + getter + setter;
实例变量+get方法+set方法,也就是说使用@property 会自动生成setter和getter方法;
我们经常使用assign,weak,strong,copy,nonatomic,atomic,readonly等关键字,下面我们列个表格去归纳一下属性关键字具体作用:


iOS基础_第1张图片
image.png

@dynamic告诉编译器,属性的setter与getter方法由用户自己实现。
@synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。

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

对于基本数据类型默认关键字是
atomic,readwrite,assign
对于普通的OC对象
atomic,readwrite,strong

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

两个都是属性关键字。
  copy是内容拷贝,创建一个和原来的对象内容相同的新对象,和原来的对象无关。
  strong是指针拷贝,对原来对象的引用,原来的对象引用计数加1。如果给它赋值的是一个可变数据,这个数据改变,那么该属性也发生变化。

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

实现NSCoping协议。如果自定义的对象分为可变版本与不可变版本,那么就要同时实现 NSCopying与NSMutableCopying协议

@protocol NSCopying

- (id)copyWithZone:(nullable NSZone *)zone;

 @end

 @protocol NSMutableCopying

 - (id)mutableCopyWithZone:(nullable NSZone *)zone;

 @end

8.  可变集合类 和 不可变集合类的 copy 和 mutablecopy有什么区别?如果是集合是内容复制的话,集合里面的元素也是内容复制么?

深浅复制https://www.jianshu.com/p/ac07c26f467d

9.为什么IBOutlet修饰的UIView也适用weak关键字?

因为 Controller ->View ->subView,所以没有必要Controller->subview了。

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

nonatomic和atomic用来决定编译器生成的getter和setter操作是否为原子操作。
atomic不是绝对的线程安全。atomic的本意是指属性的存取方法是线程安全的,并不保证整个对象是线程安全的。如:
声明一个NSMutableArray的原子属性stuff,此时self.stuff 和 self.stuff = otherstuff都是线程安全的。但是使用[self.stuff objectAtIndex:index]就不是线程安全的。需要用互斥锁来保证线程安全性。

iOS中保证线程安全的几种方式与性能对比

11. UICollectionView自定义layout如何实现?

创建自定义UICollectionView layout

12. 用StoryBoard开发界面有什么弊端?如何避免?

Storyboard的爱与恨

13. 进程和线程的区别?同步异步的区别?并行和并发的区别?

1.进程和线程的区别

线程和进程的区别主要在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式的影响下不会对其他进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等同于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
  线程是CPU独立运行和独立调度的基本单位(可以理解为一个进程中执行的代码片段)。
  进程是资源分配的基本单位(进程是一块包含了某些资源的内存区域)。
  进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。进程是线程的容器,真正完成代码执行的线程,而进程则作为线程的执行环境。一个程序至少包含一个进程,一个进程至少包含一个线程,一个进程中的所有线程共享当前进程所拥有的资源。

2.同步异步的区别

异步和同步是相对的,同步就是顺序执行,执行完一个再执行下一个,需要等待、协调运行。异步就是彼此独立,在等待某事件的过程中继续做自己的事,不需要等待这一事件完成后再工作。线程就是实现异步的一个方式。异步是让调用方法的主线程不需要同步等待另一线程的完成,从而可以让主线程干其它的事情。
  异步和多线程并不是一个同等关系,异步是最终目的,多线程只是我们实现异步的一种手段。异步是当一个调用请求发送给被调用者,而调用者不用等待其结果的返回而可以做其它的事情。实现异步可以采用多线程技术或则交给另外的进程来处理。

3.并行和并发的区别

并发行和并行性的区别可以用馒头做比喻。前者相当于一个人同时吃三个馒头和三个人同时吃一个馒头。
  并发性(Concurrence):指两个或两个以上的事件或活动在同一时间间隔内发生。并发的实质是一个物理CPU(也可以多个物理CPU) 在若干道程序之间多路复用,并发性是对有限物理资源强制行使多用户共享以提高效率。
  并行性(parallelism)指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不同CPU上同时执行。
  区别:一个处理器同时处理多个任务和多个处理器或者是多核的处理器同时处理多个不同的任务。
  前者是逻辑上的同时发生(simultaneous),而后者是物理上的同时发生。
  两者的联系:并行的事件或活动一定是并发的,但反之并发的事件或活动未必是并行的。并行性是并发性的特例,而并发性是并行性的扩展。

14. 线程间通信?

1.线程间通信简介

所谓线程间的通信,就是一个线程操作完数据之后,另一个线程再去操作这组数据。最常见的例子就是,子线程下载数据,主线程更新UI。其实线程间的通信是非常普遍的, 只要你的App进行了数据请求就一定会发生。至于很多iOS程序员没有发现,是因为几乎所有网络请求的框架的回调都是在主线程进行的,框架设计者都设计好了,所以你什么都不需要干。

2.常见的通信方式

1.GCD

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
         //子线程请求数据
        dispatch_async(dispatch_get_main_queue(), ^{

                //主线程更新UI
        });

    });

2.NSObject

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;

- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg NS_AVAILABLE(10_5, 2_0);

3.NSOperation

[NSOperationQueue mainQueue] addOperationWithBlock:^{
        //更新UI
    }

3.多线程同时操作一个数据

春运的时候,就剩下一张票了,100个人同时购买,怎么办,难道每个人都能购买成功?肯定不是,只要一个人买了,其他人就不能买了,不然票的数量得出现负数。这就出现了强占资源,多条线程同时操作一个数据。iOS多线程之2.NSThread的加锁@synchronized。这篇文章给出了一种解决方式,其实还有很多,NSLock、信号量、NSConditionLock、barrier。其实实现得方向都是一致的,就是只要有一条线程操作这个数据,其他线程就不能操作了,等这个线程操作完了才能继续操作。

15.  GCD的一些常用的函数?(group,barrier,信号量,线程同步)

iOS开发系列--并行开发其实很容易 文章很长,但是把iOS并发的所有方式都介绍了一遍,值得多读几遍。 浅谈GCD中的信号量是信号量的实际应用案例,与用group应用中的坑,没读之前,我都没意识到。

16. 如何使用队列来避免资源抢夺?

.多线程同时操作一个数据

春运的时候,就剩下一张票了,100个人同时购买,怎么办,难道每个人都能购买成功?肯定不是,只要一个人买了,其他人就不能买了,不然票的数量得出现负数。这就出现了强占资源,多条线程同时操作一个数据。iOS多线程之2.NSThread的加锁@synchronized。这篇文章给出了一种解决方式,其实还有很多,NSLock、信号量、NSConditionLock、barrier。其实实现得方向都是一致的,就是只要有一条线程操作这个数据,其他线程就不能操作了,等这个线程操作完了才能继续操作。

17.  数据持久化的几个方案(fmdb用没用过)

iOS中几种数据持久化方案:我要永远地记住你!

18. 说一下AppDelegate的几个方法?从后台到前台调用了哪些方法?第一次启动调用了哪些方法?从前台到后台调用了哪些方法?

1.点击App图标(App没有在后台运行)

1.didFinishLaunchingWithOptions
  2.applicationDidBecomeActive

2.从后台到前台

1.applicationWillEnterForeground
  2.applicationDidBecomeActive

3.从前台到后台

1.applicationWillResignActive
  2.applicationDidEnterBackground

4.通过URLScheme打开App

1.applicationWillEnterForeground
  2.- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
  3.applicationDidBecomeActive

19. NSCache优于NSDictionary的几点?

NSCache胜过NSDictionary之处在于,当系统资源将要耗尽时,它可以自动删减缓存。如果采用普通的字典,那么就要自己编写挂钩,在系统发出“低内存”通知时手工删减缓存。

NSCache并不会“拷贝”键,而是会“保留”它。此行为用NSDictionary也可以实现,然而需要编写相当复杂的代码。NSCache对象不拷贝键的原因在于:很多时候,键都是不支持拷贝操作的对象来充当的。因此,NSCache不会自动拷贝键,所以说,在键不支持拷贝操作的情况下,该类用起来比字典更方便。另外,NSCache是线程安全的,而NSDictionary则绝对不具备此优势。

对于NSCache的一些理解

20. 知不知道Designated Initializer?使用它的时候有什么需要注意的问题?

正确编写Designated Initializer的几个原则

21. 实现description方法能取到什么效果?

description方法

22. objc使用什么机制管理对象内存?

iOS内功篇:内存管理

Block

block的实质是什么?一共有几种block?都是什么情况下生成的?

A:block对象就是一个结构体,里面有isa指针指向自己的类(global malloc stack),有desc结构体描述block的信息,__forwarding指向自己或堆上自己的地址,如果block对象截获变量,这些变量也会出现在block结构体中。最重要的block结构体有一个函数指针,指向block代码块。block结构体的构造函数的参数,包括函数指针,描述block的结构体,自动截获的变量(全局变量不用截获),引用到的__block变量。(__block对象也会转变成结构体)
block代码块在编译的时候会生成一个函数,函数第一个参数是前面说到的block对象结构体指针。执行block,相当于执行block里面__forwarding里面的函数指针。

为什么在默认情况下无法修改被block捕获的变量? __block都做了什么?

A:在block中访问的外部变量是复制过去的,写操作不对原变量生效。

模拟一下循环引用的一个情况?block实现界面反向传值如何实现?

A:两个.h文件互相import了对方造成循环引用。block先声明(在要传值的controller里声明
typedef void(^MyBlock)(NSString *name);//block的重命名
@property (nonatomic,copy) MyBlock block;//block的声明),在准备接收值的页面里实现block,
secondVC.block = ^void(NSString *name)
{
_label.text = name;
};,谁要传值就在谁那里调用self.block(@"lalala");。

iOS事件传递响应链是什么?

A:当我们在使用微信等工具,点击扫一扫,就能打开二维码扫描视图。在我们点击屏幕的时候,iphone OS获取到了用户进行了“单击”这一行为,操作系统把包含这些点击事件的信息包装成UITouch和UIEvent形式的实例,然后找到当前运行的程序,逐级寻找能够响应这个事件的对象,直到没有响应者响应。这一寻找的过程,被称作事件的响应链。
不同的响应者以链式方式寻找,AppDelegate->UIApplication->UIWindow->UIViewController->UIView->UIButton。

Runtime

objc在向一个对象发送消息时,发生了什么?

Objc Runtime使得C具有了面向对象能力,在程序运行时创建,检查,修改类、对象和它们的方法,可以使用runtime的一系列方法实现。
附上OC中一个类的底层数据结构:
mac电脑上的路径/usr/include/objc/runtime.h

struct objc_class {
    Class isa OBJC_ISA_AVAILABILITY; //isa指针指向Meta Class,因为Objc的类的本身也是一个Object,为了处理这个关系,r       untime就创造了Meta Class,当给类发送[NSObject alloc]这样消息时,实际上是把这个消息发给了Class Object
    #if !__OBJC2__
    Class super_class OBJC2_UNAVAILABLE; // 父类
    const char *name OBJC2_UNAVAILABLE; // 类名
    long version OBJC2_UNAVAILABLE; // 类的版本信息,默认为0
    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; // 方法缓存,对象接到一个消息会根据isa指针查找消息对象,这时会在method       Lists中遍历,如果cache了,常用的方法调用时就能够提高调用的效率。
    struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 协议链表
    #endif

    } OBJC2_UNAVAILABLE;

OC中一个类的对象实例的数据结构(/usr/include/objc/objc.h)

typedef struct objc_class *Class;
/// 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;

就是定义了一个向object发送消息时,Runtime库会根据object的isa指针找到这个实例object所属于的类,然后在类的方法列表以及父类方法列表寻找对应的方法运行。id是一个objc_object结构类型的指针,这个类型的对象能够转换成任何一种对象。

消息发送函数:objc_msgSend
objc_msgSend()是[obj foo]的具体实现;
在runtime中,objc_msgSend()是一个c函数,[obj foo]会被翻译成这样的形式objc_msgSend(obj, foo)。

去obj的对应的类中找方法
先找缓存,找不到再去找方法列表
再找父类,如此向上传递
最后再找不到就要转发

什么时候会报unrecognized selector错误?iOS有哪些机制来避免走到这一步?

https://www.jianshu.com/p/4d40ff407d0e

能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?

不能向编译后得到的类中增加实例变量;
能向运行时创建的类中添加实例变量;

原因如下:

因为编译后的类已经注册在 runtime 中,类结构体中的 objc_ivar_list 实例变量的链表 和 instance_size 实例变量的内存大小已经确定,同时runtime 会调用 class_setIvarLayout 或 class_setWeakIvarLayout 来处理 strong weak 引用。所以不能向存在的类中添加实例变量;

运行时创建的类是可以添加实例变量,调用 class_addIvar 函数。但是得在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上。

runtime如何实现weak变量的自动置nil?

runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。

weak 修饰的指针默认值是 nil (在Objective-C中向nil发送消息是安全的)

给类添加一个属性后,在类结构体里哪些元素会发生变化?

class object/metaclass
咱们先看一下结构体objc_class的定义

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;
/* Use `Class` instead of `struct objc_class *` */

创建一个类属性很简单,主要有以下几个步骤:

使用@property (class)来声明一个类属性;
为类属性创建一个存储变量,通常为全局变量;
实现类属性的getter与setter方法,如果是只读属性,只需要实现getter方法。
类相当于一个对象, 当对象执行这个方法即会发送一条消息, 之后根据isa指针查找消息对象,这时会在methodLists中遍历,如果cache了,常用的方法调用时就能够提高调用的效率。

RunLoop

1.runloop是来做什么的?runloop和线程有什么关系?主线程默认开启了runloop么?子线程呢?

runloop和线程有什么关系?

2.runloop的mode是用来做什么的?有几种mode?

model 主要是用来指定事件在运行循环中的优先级的,分为:

NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默认,空闲状态
UITrackingRunLoopMode:ScrollView滑动时
UIInitializationRunLoopMode:启动时
NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合
苹果公开提供的 Mode 有两个:

NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
NSRunLoopCommonModes(kCFRunLoopCommonModes)

3.为什么把NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环以后,滑动scrollview的时候NSTimer却不动了?

RunLoop只能运行在一种mode下,如果要换mode,当前的loop也需要停下重启成新的。利用这个机制,ScrollView滚动过程中NSDefaultRunLoopMode(kCFRunLoopDefaultMode)的mode会切换到UITrackingRunLoopMode来保证ScrollView的流畅滑动:只能在NSDefaultRunLoopMode模式下处理的事件会影响ScrollView的滑动。

如果我们把一个NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环中的时候, ScrollView滚动过程中会因为mode的切换,而导致NSTimer将不再被调度。

同时因为mode还是可定制的,所以:

Timer计时会被scrollView的滑动影响的问题可以通过将timer添加到NSRunLoopCommonModes(kCFRunLoopCommonModes)来解决。代码如下:

// http://weibo.com/luohanchenyilong/ (微博@iOS程序犭袁)
// https://github.com/ChenYilong
//将timer添加到NSDefaultRunLoopMode中
[NSTimer scheduledTimerWithTimeInterval:1.0
     target:self
     selector:@selector(timerTick:)
     userInfo:nil
     repeats:YES];
//然后再添加到NSRunLoopCommonModes里
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
     target:self
     selector:@selector(timerTick:)
     userInfo:nil
     repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

4. 苹果是如何实现Autorelease Pool的?

autoreleasepool 以一个队列数组的形式实现,主要通过下列三个函数完成.

objc_autoreleasepoolPush
objc_autoreleasepoolPop
objc_autorelease
看函数名就可以知道,对 autorelease 分别执行 push,和 pop 操作。销毁对象时执行release操作。

类结构

1.isa指针?(对象的isa,类对象的isa,元类的isa都要说)

Objective-C 是一门面向对象的编程语言。每一个对象都是一个类的实例。在objective-c语言的内部,每一个对象都有一个名为 isa 的指针,指向该对象的类。每一个类描述了一系列它的实例的特点,包括成员变量的列表,成员函数的列表等。每一个对象都可以接受消息,而对象能够接收的消息列表是保存在它所对应的类中。


iOS基础_第2张图片
image.png

iOS class深入理解: 实例对象、类对象、元类和isa指针

2.类方法(class method)和实例方法(instance method)有什么区别?

类方法,也称静态方法,指的是用static关键字修饰的方法。此方法属类本身的方法,不属于类的某一个实例(对象)。类方法中不可直接使用实例变量。其调用方式有三种:可直接调用、类名.方法名、对象名.方法名。实例方法指的是不用static关键字修饰的方法。每个实例对象都有自身的实例方法,互相独立,不共享一个。其调用方式只能是对象名.方法名。

instance method 以减号 "-" 开头
class method 以加号 “+” 开头,相当于static方法
区别:
静态方法在程序开始时生成内存,实例方法在程序运行中生成内存,
所以静态方法可以直接调用,实例方法要先成生实例,通过实例调用方法,静态速度很快,但是多了会占内存。
静态内存是连续的,因为是在程序开始时就生成了,而实例申请的是离散的空间,所以当然没有静态方法快,
而且静态内存是有限制的,太多了程序会启动不了。

使用场景:
如果需要访问或者修改某个实例的成员变量时,将该方法定义成实例方法。
类方法正好相反,它不需要访问或者修改某个实例的成员变量。
类方法一般用于实现一些工具方法,比如对某个对象进行扩展,或者实现单例。

类方法常驻内存,实例方法不是,所以类方法效率高但占内存。
类方法在堆上分配内存,实例方法在堆栈上。
事实上所有的方法都不可能在堆或者堆栈上分配内存,方法作为代码是被加载到特殊的代码内存区域,这个内存区域是不可写的。
实例方法需要先创建实例才可以调用,比较麻烦,类方法不用,比较简单。

事实上如果一个方法与他所在类型的实例无关,那么它就应该是静态的,决不会有人把它写成实例方法。所以所有的实例方法都与实例有关,既然与实例有关,那么创建实例就是必然的步骤,没有麻烦简单一说。实际上上你可以把所有的实例方法都写成静态的,将实例作为参数传入即可。

3.介绍一下分类,能用分类做什么?内部是如何实现的?它为什么会覆盖掉原来的方法?

类别(Category)主要有3个作用:

将类的实现分散到多个不同文件或多个不同框架中。
创建对私有方法的前向引用。
向对象添加非正式协议。
声明:@interface 类名(分类名称) @end
实现:@implementation 类名(分类名称) @end

注意:
(1)在分类只能增加方法,不能增加成员变量,如果要增加成员变量的话该考虑用继承去实现
(2)在分类实现方法中可以访问类中的成员变量但是不能访问类中的属性@property
(3)在分类中可以重新实现原类中的方法,但会将原类中的方法覆盖而失效。

因为在执行对象成员方法的时候会优先去分类中查找,然后再去原类中去查找,最后去父类中去查找

(4)如果一个类有多个分类,而且分类中有同名的方法那么最后编译的分类会将前面编译的分类覆盖而执行输出

// 另外一份解释来自《招聘一个靠谱的 iOS》—参考答案(上)---24
// 类方法:
   - 类方法是属于类对象的
   - 类方法只能通过类对象调用
   - 类方法中的self是类对象
   - 类方法可以调用其他的类方法
   - 类方法中不能访问成员变量
   - 类方法中不能直接调用对象方法

//  实例方法:
   -  实例方法是属于实例对象的
   -  实例方法只能通过实例对象调用
   -  实例方法中的self是实例对象
   -  实例方法中可以访问成员变量
   -  实例方法中直接调用实例方法
   -  实例方法中也可以调用类方法(通过类名)

4.运行时能增加成员变量么?能增加属性么?如果能,如何增加?如果不能,为什么?

Category中不能动态添加成员变量;
在Objective-C提供的runtime函数中,确实有一个class_addIvar()函数用于给类添加成员变量,但是阅读过苹果的官方文档的人应该会看到:

This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.

大概的意思说,这个函数只能在“构建一个类的过程中”调用。一旦完成类定义,就不能再添加成员变量了。经过编译的类在程序启动后就被runtime加载,没有机会调用addIvar。程序在运行时动态构建的类需要在调用objc_registerClassPair之后才可以被使用,同样没有机会再添加成员变量。
因为方法和属性并不“属于”类实例,而成员变量“属于”类实例。我们所说的“类实例”概念,指的是一块内存区域,包含了isa指针和所有的成员变量。所以假如允许动态修改类成员变量布局,已经创建出的类实例就不符合类定义了,变成了无效对象。但方法定义是在objc_class中管理的,不管如何增删类方法,都不影响类实例的内存布局,已经创建出的类实例仍然可正常使用。

然而如果在运行时动态生成一个类,就可以为其添加成员变量和方法, 如下图所示:
添加的方法必须是已经实现的,所以先手写这个方法


iOS基础_第3张图片
image.png
iOS基础_第4张图片
image.png

如上就动态创建了一个类。下面我们开始使用这个类。


iOS基础_第5张图片
image.png
iOS基础_第6张图片
image.png

5.objc中向一个nil对象发送消息将会发生什么?(返回值是对象,是标量,结构体)
objc中向一个nil对象发送消息将会发生什么?

2017年5月iOS招人心得答案总结(高级篇)

你可能感兴趣的:(iOS基础)