NSString* testObject = [[NSData alloc] init]

首先,声明 NSString *testObject 是告诉编译器,testObject是一个指向某个Objective-C对象的指针。因为不管指向的是什么类型的对象,一个指针所占的内存空间都是固定的,所以这里声明成任何类型的对象,最终生成的可执行代码都是没有区别的。这里限定了NSString只不过是告诉编译器,请把testObject当做一个NSString来检查,如果后面调用了非NSString的方法,会产生警告。

接着,你创建了一个NSData对象,然后把这个对象所在的内存地址保存在testObject里。那么运行时,testObject指向的内存空间就是一个NSData对象。你可以把testObject当做一个NSData对象来用。

610122816dbe5.png

super class

1.消息接收者任然是自己 只是self 是从自己开始找 super 是从父类开始找

AFNetworking3.0后为什么不再需要常驻线程?

AFN3.0 NSURLSession 不需要2.0NSURLConnection 的常驻线程。

2.0需要常驻线程是因为请求回调依赖当前线程,而AFN3.0 NSURLSession的请求回调不需要依赖当前线程,可以指定回调的delegateQueue,这样也就不需要再对线程进行保活。

kvo和kvc

1.使用setter方法改变值

2.使用setValue:forKey改变值

3.成员变量直接修改需手动添加kvo才会生效 也就是重写

[self willChangeValueForKey:"value"]

_value += 1

[self didChangeValueForKey:"value"]

多层自动释放池嵌套的对象在哪一层释放?

在多层自动释放池嵌套的情况下,对象的释放是在最内层的自动释放池被销毁时进行的。当最内层的自动释放池结束时,池中的对象会被释放。

考虑以下示例代码:

objectiveCopy code

- (void)someMethod {
	@autoreleasepool {
	// 最外层自动释放池
	// 创建对象A,加入最外层自动释放池
		@autoreleasepool {
			// 内层自动释放池
			// 创建对象B,加入内层自动释放池
		}
		// 内层自动释放池结束,对象B被释放
		// 创建对象C,加入最外层自动释放池
	}
	// 最外层自动释放池结束,对象A和C被释放 
}

在这个例子中,对象A和C是在最外层自动释放池中创建并加入的,它们的生命周期与最外层自动释放池的生命周期一致,当最外层自动释放池结束时,对象A和C会被释放。

而对象B是在内层自动释放池中创建并加入的,它的生命周期与内层自动释放池的生命周期一致,当内层自动释放池结束时,对象B会被释放。

因此,在多层自动释放池嵌套的情况下,对象的释放是在其所属的最内层自动释放池结束时进行的。

weak原理

在 iOS 中,`weak` 是一种特殊的属性修饰符,用于指定对对象的弱引用。相比于强引用(`strong`),弱引用不会增加对象的引用计数,也不会保持对象在内存中。当没有其他强引用指向对象时,对象会被释放,并且弱引用会自动被设置为 nil

weak 属性的实现原理依赖于 Objective-C 的运行时(Runtime)系统和引用计数(Reference Counting)机制的支持。

下面是 weak 属性的一般工作原理:

1. 弱引用的底层数据结构是一个叫做 __weak 的内存表。它是一个散列表(hash table),用于存储所有当前的弱引用。

2. 当一个对象被声明为 weak,运行时系统会自动将其添加到相应的 __weak 表中。

3. 当对象的引用计数为零时,即没有强引用指向它,引用计数机制会将其内存释放。

4. 在释放对象内存时,运行时系统会遍历 __weak 表,并将其中的所有弱引用设置为 nil。这样,避免了弱引用指向已经释放的对象。

需要注意的是,`weak` 属性只能用于 Objective-C 对象,而不能用于纯 C 类型、基本数据类型或结构体等。此外,`weak` 属性也不能修饰 IBOutlet,因为 IBOutlet 是在 xib 或者 Storyboard 中通过 Interface Builder 进行连接的,而不是通过代码来创建的。

总结:

- weak 是一种特殊的属性修饰符,用于指定对对象的弱引用。

- 弱引用不会增加对象的引用计数,也不会保持对象在内存中。

- weak 属性的实现依赖于 Objective-C 运行时系统和引用计数机制的支持。

- 弱引用底层使用 __weak 表来存储弱引用对象,当对象被释放时,相关的弱引用会被自动设置为 nil

- weak 属性只能用于 Objective-C 对象,不能用于纯 C 类型、基本数据类型或结构体等。

atomic nonatomic

都是为对象添加get和set方法

atomic为get方法加了一把安全锁(及原子锁),使得方法get线程安全,执行效率慢

只对赋值和获取有保护作用 对添加和移除对象不起作用

nonatomic没有添加安全锁,执行效率快

一般iOS程序中,所有属性都声明为nonatomic。这样做的原因是:

在iOS中使用同步锁的开销比较大, 这会带来性能问题。一般情况下并不要求属性必须是“原子的”,因为这并不能保证“线程安全”(thread safety),若要实现“线程安全”的操作,还需采用更为深层的锁定机制才行

浅拷贝和深拷贝

一个只是复制内存地址 目标和原对象的指针指向的都是同一个内存空间

一个是内存地址和内存空间都复制 指向不同空间

判断:

是否开辟的新的内存空间

是否影响了引用计数

多线程中栈与堆是公有的还是私有的

栈私有,堆公有

什么时候调用delegate,什么时候调用Notification?

1.delegate 针对one-to-one关系。并且reciever可以返回值给sender

2.notification 可针对one-to-one/many/noce,reciever无法返回值给sender

所以delegate 用于sender希望接受到reciever的某个功能反馈值

notification 用于通知多个object某个事件

在iOS开发中,类都是懒加载的,只有用到类的时候才会将类加载到内存中,那么如何将类变成非懒加载类呢?在类中添加load方法

+ (void)load{

}

下面的方式能接收到通知吗?为什么

- (void)test3{

    // 注册通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"TestNotification" object:@1];

    // 发送通知
    [NSNotificationCenter.defaultCenter postNotificationName:@"TestNotification" object:nil];

}

- (void)handleNotification:(NSNotification *)notification{
    NSLog(@"123");
}

不能 这个通知存储在named表里,原本记录的通知观察对象内部会用object作为字典里的key,查找的时候没了object无法找到对应观察者和处理方法。

实例方法和类方法的区别?

  1. 实例方法能访问成员变量。

  2. 类方法中必须创建或者传入对象才能调用对象方法。

  3. 实例方法存储在类对象的方法列表里,类方法存在于元类的方法列表里。

  4. 类方法可以和对象方法重名。

Runtime中,SEL、Method 和 IMP有什么区别,使用场景?

/// Method

struct objc_method {

    SEL method_name;

    char *method_types;

    IMP method_imp;

 };

翻书 目录sel和页数imp

讲一下对象,类对象,元类结构体的组成以及他们是如何相关联的?

  1. 实例对象的结构体是objc_object,主要存储的是isa以及相关的一些函数,例如getIsa、initIsa……还有一些关于对象内存管理的相关方法,isTaggedPointer、isWeaklyReferenced……

  2. 类对象和元类对象其实都是Class,其结构体都是objc_class,里面存储了isa、superClass、成员变量集合、方法集合、协议集合、cache(方法缓存)等等……

  3. 成员变量的isa指向其类对象、类对象的isa指向元类、元类的isa指向根元类,也就是NSObject的元类。

苹果为什么要设计元类?

isa指向类,类是一个objc_class结构体,包含实例的方法列表,参数列表,category等,除此之外,objc_class中还有一个super_class,指向其类的父类,isa指针,这里的isa指针指向元类,即metaClass,元类存储类方法等信息

元类里也包含isa指针,元类里的isa指针指向 根元类,根元类的isa指针指向自己

obj_msgSend发送实例消息的时候,先找到实例,然后通过实例的isa指针找到类的方法列表及参数列表等,如果找到,返回,如果没有找到,则通过super_class在其父类中重复此过程

obj_msgSend发送类消息的时候,通过类的isa,找到元类,然后流程与步骤与上面相同

在Masonry的block中,使用self,会造成循环引用吗?如果是在普通的block中呢?

不会,因为这是个栈block,没有延迟使用,使用后立刻释放

普通的block会,一般会使用强引用持有,就会触发copy操作

在普通的block中只使用下划线属性去访问,会造成循环引用吗

会,和调用self.是一样的

消息发送->消息机制

  1. 快速查找,方法缓存

  2. 慢速查找,方法列表

  3. 消息转发

  • 3-1、方法的动态解析,resolveInstanceMethod

  • 3-2、快速消息转发,forwardingTargetForSelector

  • 3-3、标准消息转发,methodSignatureForSelector & forwardInvocation

NSString类型为什么要用copy修饰 ?

主要防止NSString被修改。

当NSString的赋值来源是NSString时,strong和copy作用相同。

当NSString的赋值来源是NSMutableString,copy会做深拷贝重新生成一个新的对象,修改赋值来源不会影响NSString的值。而strong则会改变 因为strong的意思是指针指向原对象,并且引用计数+1 所以为了避免NSString类型的值被修改,一般建议用copy修饰符修饰。

runtime能做什么?

获取类的成员变量、方法、协议

为类添加成员变量、方法、协议

动态改变方法实现

class_ro_t和class_rw_t的区别?那为什么还要rw_e

class_rw_t提供了运行时对类拓展的能力,class_rw_t结构体中存储了class_ro_t。

class_ro_t存储的是类在编译时已经确定的信息,是不可改变的。

二者都存有类的方法、属性(成员变量)、协议等信息,不过存储它们的列表实现方式不同。简单的说class_rw_t存储列表使用的二维数组,class_ro_t使用的一维数组。

运行时修改类的方法,属性,协议等都存储于class_rw_t中

一经编译,就不可修改的数据。(ro)

运行过程中可修改的数据。(rw)

ro:数据是只读的,它属于clean Memory。它是从沙盒读取,ro的数据在编译的时候就已经确定了。

rw:数据是可读可写的,它属于dirty Memory。rw的数据存放的是运行时动态修改的数据。

rwe:是苹果为rw做的优化,从rw拆出来那些平时不常用的部分,以减少rw的开销,大约 90% 的类从来不需要这些扩展数据,这在系统范围内可节省大约14MB 的内存。

什么是 Method Swizzle(黑魔法),什么情况下会使用?

方法交换最好在+(load)方法中进行

+initialize 里面使用要加dispatch_once

Method Swizzle 是改变一个已存在的选择器(SEL)对应的实现(IMP)的过程。

类的方法列表存放着SEL的名字和IMP的映射关系。

开发者可以利用 method_exchangeImplementations 来交换2个方法中的IMP

开发者可以利用 method_setImplementation 来直接设置某个方法的IMP

这就可以在运行时改变SEL和IMP的映射关系,从而实现方法替换。

为什么对象方法中没有保存在对象结构体里面,而是保存在类对象的结构体里面?

每个对象都存储同一份实例方法列表太浪费。调用的时候对象只需要通过isa找到类对象,在其方法列表里查找就可以了。

实际开发中,如何对内存进行优化呢?

使用ARC管理内存

使用Autorelease Pool

优化算法

避免循环引用

定期使用Instrument的Leak检测内存泄漏

用户是如何通过url地址访问到服务器的,它怎么知道要访问哪个浏览器

DNS域名解析,通过这一步还原IP(迭代查询,递归查询)

TCP连接,三次握手

HTTP请求,请求报文主要包括请求行,请求头,请求正文

处理请求返回HTTP响应,返回Response,主要包括状态码,响应头,响应报文三个部分

页面渲染

关闭连接,四次挥手

事件的传递和响应

当一个事件在发生后,先是被逐级上报以确定响应的App(如果不能确定,事件就会被丢弃),然后被逐级分发,通过hitTest:withEvent:和pointInside:withEvent:两个方法以确定最终响应事件的对象,即第一响应者,如果不能确定,事件会被逐级上抛,如果到AppDelegate仍未确定第一响应者来响应事件,则事件会被丢弃,这就是事件响应链规则。

如果 Swizzle 了 父 View 的 touchesBegan 的方法,对子View没有任何影响。

对于一个点击操作,如果响应链无法确定一个明确的第一响应者,那么会发生touchesEnded:withEvent:方法的递归上抛;

如果响应者链条中有响应者实现了touchesBegan:withEvent:方法,并且被响应,那么touchesEnded:withEvent:方法的递归上抛截止到该响应者为止;

如果响应者链条中没有响应者实现了touchesBegan:withEvent:方法,那么touchesEnded:withEvent:方法的递归上抛将一直上抛到AppDelegate,然后结束。

socket连接和http连接的区别?

socket连接,socket连接就是所谓的长链接,理论上客户端和服务端一旦建立起连接不会主动断掉。

但是由于各种环境因素可能会是连接断开,比如说:服务器端或客户端主机down了。网络故障,或者两者之间长时间没有数据传输,网络防火墙可能会断开该连接以释放网络资源。

具体心跳消息格式是开发者自己定义的

http连接。http连接就是所谓的短连接,即客户端向服务端发送一次请求,服务器响应后连接即会断掉。

由于UDP在传输数据报前不用在用户和服务端之间建立一个连接,且没有超时重发等机制。故而传输速度很快所以当一个socket连接中没有数据的传输,那么为了维持连接需要发送心跳消息具体心跳消息格式是开发者自己定义的

http连接。http连接就是所谓的短连接,即客户端向服务端发送一次请求,服务器响应后连接即会断掉。

由于UDP在传输数据报前不用在用户和服务端之间建立一个连接,且没有超时重发等机制。故而传输速度很快

keyWindow 和 delegate的window有何区别

delegate.window 程序启动时设置的window对象。

keyWindow 这个属性保存了[windows]数组中的[UIWindow]对象,该对象最近被发送了[makeKeyAndVisible]消息

一般情况下 delegate.window 和 keyWindow 是同一个对象,但不能保证keyWindow就是delegate.window,因为keyWindow会因为makeKeyAndVisible而变化,例如,程序中添加了一个悬浮窗口,这个时候keywindow就会变化。

什么是多继承 OC是否可以多继承 那么用什么方法可以实现多继承

c++ 可以多继承但是OC不能多继承 间接的实现多继承可以用 组合的方式 代理 分类 消息转发

copy关键字

62104e8014a74.png

可变对象的copy和mutableCopy都是深拷贝

不可变对象的copy是浅拷贝,mutableCopy是深拷贝

copy方法返回的都是不可变对象

62104f601bbad.png

导致无法调用add remove对象的操作

对象 类对象 元类对象 根元类对象

类对象储存实例方法列表等信息

元类对象储存类方法列表等信息

通过isa指针找到 一级一级往上找

能否向编译后的类中添加实例变量?

是不可以的

只能向动态运行时的类中添加

引用计数通过什么来实现的

通过hash表来实现的

提高查找效率 插入和获取都是通过同一个hash算法或者是hash函数来实现的

notification是同步还是异步 kvo呢

默认是同步 耗时操作可能造成卡顿 可以把通知的方法放入子线程或者把通知的发送放在子线程

kvo也是一样的

weak assign区别

weak只可以修饰对象 不会产生野指针 适用于delegate和block引用类型 也不会循环引用 非常的安全

assign 可以修饰对象和基本数据类型 数据类型是安全的 因为会放入栈中 但是修饰对象释放后 是放入堆中 需要我们手动的管理或者arc管理 可能会造成指针不会自动被置空 也产生野指针

ARC 自动引用计数

ARC是LLVM和Runtime协作的结果

同时禁止手动调用retain/release/retaimCount/dealloc 并且新增了weak strong属性关键字

添加弱引用weak变量

可以通过弱引用对象添加hash算法计算查找

系统是怎么移除一个isFinished=YES的NSOperation的

通过KVO

Runloop 会在哪些情况下唤醒呢?

Source1 Timer事件 外部手动唤醒

Charles抓包原理是怎样的?

利用http中间人攻击

https的建立连接的流程?

客服端请求服务器端 要先进行证书校验 通过之后才能发起连接

HTTPS都使用了哪些加密手段?为什么?

连接建立过程使用费对称加密。非对称加密很耗时

后续通信过程使用户对称加密

非对称加密

一个加密用公钥或者私钥,解密私钥或者公钥

对称加密

加密解密都是同一个密钥

架构/框架相关的问题

图片缓存 阅读时长设计 复杂页面架构 客户端整体架构

内存的设计需要考虑哪些问题

存储的size大小

淘汰策略

队列先进先出的方式淘汰

LRU算法,如30分钟内是否使用

磁盘设计需要考虑哪些问题

存储方式

大小限制(100MB)

淘汰策略(如存储时间超过多少天)

网络设计需要考虑哪些问题

图片请求的最大并发量

请求的超时策略

请求的优先级

图片解码

对不同格式的图片,采用什么方式呢

应用策略模式对不同的图片进行解码

在哪个阶段做图片解码处理呢?

磁盘读取后,网络请求返回后

线程处理

先去内存找 再去硬盘找 最后发起请求

记录上传器 上传时机

前后台切换

从无网到有网的变化

通用轻量化接口捎带

立刻 延时 定时

业务之间的解耦方式

OpenUrl

依赖注入

如何实现一个常驻线程

利用NSThread 和RunLoop

开启NSThread的时候 加入到CommonModes的Runloop中

然后再添加一个port端口用来监听事件

最后把所有的任务都放到一个@autoreleasepool自动释放池中去

dispath_barrier 栅栏函数

使用Concurrent Dispatch Queue 和dispatch_barrier_async 函数可实现高效率的数据库访问和文件访问

但是dispath_barrier不能拦截全局队列函数

势与响应者链

手势与响应者链有一些差别,触摸事件首先会传递到手势上,如果手势识别成功,就会取消事件的继续传递。如果手势识别失败,事件才会被响应链处理

分类和子类

一个简单的业务场景

BC 都继承A D需要用到BC的功能 那个分类就可以起到很好的作用

runtime 方法交换的使用场景

对底层的一些方法可以适当的做一些处理 对业务层的不要去用 因为大家可以都在用这个