- 如何令自定义的对象居右拷贝功能?
对象需要实现 NSCopying 协议,实现 NSCpoying 协议中的copyWithZone方法
- (id)copyWithZone:(NSZone *)zone{
PersonModel *model = [[[self class] allocWithZone:zone] init];
model.firstName = self.firstName;
model.lastName = self.lastName;
//未公开的成员
model->_nickName = _nickName;
return model;
}
在- (id)copyWithZone:(NSZone *)zone
方法中,一定要通过[self class]
方法返回的对象调用allocWithZone:
方法。因为指针可能实际指向的是PersonModel
的子类。这种情况下,通过调用[self class]
,就可以返回正确的类的类型对象。
-
说说你理解的 weak 属性?
weak 是在 arc 机制下定义属性的修饰关键字,它修饰的属性不会强引用对象,当这个对象释放了,那么这个属性指针指向的地址也就为 nil。weak 修饰的属性会被放进一个 hash 链表里面,每一个节点的key 为这个对象的地址运用 hash 函数,节点的 value 是一个数组,将这个对象插入到数组中;释放的时候也是根据对象地址的 hash 函数值来将节点清空。 -
Swift mutating 关键字作用?
struct 和 enum 中用 mutating 修饰的方法可以修改属性。 -
UIView 和 CALayer 层级关系?
UIView继承 UIRepsond,能响应事件,
CALayer 继承 NSObject,不能响应事件,只能用来渲染 -
下面的代码输出什么?
@implementation Son : Father
-(id)init{
if(self = [super init]){
NSLog(@"%@",[self class]);
NSLog(@"%@",[super class]);
}
return self;
}
都会输出:Son
通过查阅 objc 源码可得,
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
[self class] 最后得到的依然是实例对象的类对象,即 Son
而 [super class] 表示的是去调用父类的class 方法,而根据多态特性,super 和 self 指向的是同一个接受者。
- @synthesize 和 @dynamic 有什么区别?
@synthesize 为属性添加一个成员变量别名。同时会为该属性自动生成 setter/getter 方法。
@dynamic 如果某属性已经在某处实现了自己的 setter/getter ,可以使用 @dynamic来防止 @synthesize 自动生成新的 setter/getter 覆盖;声明手动实现 get 和 set 方法。
假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和 @getter 方法。编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃; - iOS 动态特性
- 动态加载
按需求加载所需要的资源。 - 动态类型
id 类型,运行时才能根据语境识别类型 - 动态绑定
在运行时动态的添加方法,添加属性。
- 分类(category)、扩展(extension)、继承的区别?
- 分类:在不改变原有代码基础上给类增加新的方法和属性。
- 扩展:一般把不需要暴露的属性都定义在类扩展中。
- 继承:面向对象语言的特性之一,子类可以使用父类的方法和属性。
- 为什么代理理要用weak?代理的delegate和dataSource有什么区别?block和代理理 的区别?
用 weak 修饰代理是为了防止循环引用。
delegate 偏重于交互,dataSource 偏重于数据的回调。
block 是一个结构体,delegate 更加适合用来设计接口。
详情:https://www.jianshu.com/p/ccfdabd0abef
代理是一个设计模式,作为客户端和目标对象之间的中介。
block 操作简单,容易造成内存泄露,delegate写法复杂,但是不用花太担心内存泄露问题。
block 注重结果,而 delegate 注重过程。 - id和NSObject*的区别
id 是动态类型,运行时才知道类型,NSObject *是静态类型,编译时就能调用方法 - 使⽤用系统的某些block api(如UIView的block版本写动画时),是否也考虑引⽤用 循环问题?
不用担心,因为系统 block 不是 uiview 持有的 - 用@property声明的NSString(或NSArray,NSDictionary)经常使⽤用copy关键 字,为什么?如果改⽤用strong关键字,可能造成什么问题?
copy 是浅拷贝,可变对象copy 一次得到是不可变对象,只要实现了协议方法。一般用来修饰不可变类型,用 strong 也可以,但是用 copy 更符合规范。 - static 有什么作用?
修饰局部变量:只初始化一次,延长局部变量的生命周期,程序销毁变量才销毁。
修改全局变量的作用范围只在当前文件下,定义在.h 文件中则作用域包括引入这个文件的类;而默认情况下全局变量作用域是在整个项目下的,同一个工程声明两个同名的全局变量会造成编译失败。
用 static 通过改变全局变量的作用域,可以避免重复定义全局变量。
详情:static extern const - main()执行之前的过程有哪些?
读取镜像文件,查找动态库,加载资源文件,注册类和分类。 - KVO 基本原理?
KVO 注册监听的类会生成一个派生类,并把这个对象指针指向这个派生类,当改变属性值时,会调用属性的 set 方法,这时会通知调用监听方法。 swift 模式匹配?
- 通配符模式(Wildcard Pattern)
如果你在 Swift 编码中使⽤用了了 _ 通配符,就可以表示你使⽤用了了通配符模式
例如,下面这段代码在闭区间 1...10 中迭代,每次迭代都忽略该区间的当前值:
for _ in 0...10 {
print("hello")
}
- 标识符模式(Identifier Pattern)
定义变量或者常量时候,可以认为变量名和常量名就是一个标识符模式,⽤用于接收 和匹配⼀一个特定类型的值:
let i = 1 // i 就是⼀一个标识符模式
- 值绑定模式(Value-Binding Pattern)
值绑定在 if 语句和 switch 语句中用的较多。
比如 if let 和 case let, 还有可能是 case var。 let 和 var 分别对应绑定为常量和 变量。
var str: String? = "nil"
if let v = str {
// 使⽤用v这个常量做什么事 (use v to do something)
print("hello")
}
- 元组模式(Tuple Pattern)
顾名思义,元组模式就是匹配元组中元素的模式:
let tuple = ("world", 21)
switch tuple {
case ("hello", let num):
print("第⼀个元素匹配,第二个元素可以直接使用\(num)")
case (_, 0...10):
print("元组的第⼀个元素可以是任何的字符串,第二个元素在0~10之间")
case ("world", _):
print("只要元组的第⼀个元素匹配, num可以是任何数")
default:
print("default")
}
但是在 for-in 语句中,由于每次循环都需要明确匹配到具体的值,所以下代码 是错误的:
let pts = [(0, 0), (0, 1), (0, 2), (1, 2)]
for (x, 0) in pts {
}
需要使⽤用一个另外一个值绑定模式,来达成以上的逻辑:
let pts = [(0, 0), (0, 1), (0, 2), (1, 2)]
for (x, y) in pts {
}
- 枚举⽤例模式(Enumeration Case Pattern)
枚举⽤例模式是功能最强⼤的⼀个模式,也是整个模式匹配当中,应⽤⾯最广的模 式,结合枚举中的关联值语法,可以做很多事情。先看一个简单的例子:
enum Direction {
case up case down case left case right
}
let direction = Direction.up switch direction {
case .up:
print("up")
case .down:
print("down")
case .left:
print("left")
case .right:
print("right")
}
- 可选模式
语句 case let x = y 模式允许你检查 y 是否能匹配 x。
而 if case let x = y { ... } 严格等同于 switch y { case let x: ... }:当你只想与一条 case 匹配时,这种更紧凑的语法尤其有。有多个 case 时更适合使用switch。
可选模式就是包含可选变量定义模式,在 if case、 for case、 switch-case 会⽤用 到。注意 if case let 和 if let的区别:
使⽤可选模式匹配
let a:Int? = nil
if case let x? = a {
print("\(x)") }
let arr:[Int?] = [nil, 1, nil, 3]
/// 只匹配非nil的元素
for case let element? in arr {
print("\(element)")
}
- 类型转换模式(Type-Casting Pattern)
使⽤ is 和 as 关键字的模式,就叫类型转换模式:
let tp : Any = 0
switch tp {
case let a as Int :
print("是Int类型")
case let a as String :
print("是String类型")
default:
print("其它类型")
}
switch tp {
case is Int:
print("是Int类型")
case is String:
print("是String类型")
default:
print("其它类型")
}
- 表达式模式
表达式模式只出现在 switch-case 中,Swift的模式匹配是基于~=操作符的,如果 表达式的~= 值返回true则匹配成功。可以自定义~=运算符,自定义类型的表达式匹配 行为:
⾃定义模式匹配: 注意必需要写两个参数, 不然会报错
struct Affine {
var a: Int
var b: Int }
func ~= (lhs: Affine, rhs: Int) -> Bool { return rhs % lhs.a != lhs.b
}
switch 5 {
case Affine(a: 2, b: 0):
print("Even number")
case Affine(a: 3, b: 1):
print("3x+1")
case Affine(a: 3, b: 2):
print("3x+2")
default:
print("Other")
}
- objc在向一个对象发送消息时,发⽣生了了什么?
先去obj对应的类中找缓存列表,找不到时去找方法列表;再找父类,以此向上传递;最后找不到则走消息转发流程。
#park 1. 第一次找不到时,会自动调用该方法,用来给程序动态添加一个新方法并执行
+(bool)resolveInstanceMethod:(sel){
}
#park 2. 当系统调用上一个方法后未能实现添加新方法,这是系统提供一个将SEL转给备用接受者的机会
-(id)forwardingTargetForselector:(sel)aselctor{
}
#park 3. 方法加签
-(NSMethodSigature *)methodSignatrueForseletor:(sel)aselector{
//当上一个方法返回nil或self时,进入该方法,返回一个方法签名,并由forwardInvocation:去执行
}
-(void)forwardInvocation:(NSInvocation *)anInvocation{
//若上面方法不返回nil,则在这里调用自己对象的其他方法,也可以调用其它函数,以及多个不同对象的多个方法
}
-
静态库的原理理是什什么?你有没有⾃自⼰己写过静态编译库,遇到了了哪些问题?
- runloop 有什么作用?runloop 和弦城有什么关系?
runloop:字面意思就是跑圈,其实就是一个循环,用来处理线程里面的事件和消息。
runloop 和线程之间的关系:每个线程如果不想被释放,就必须有一个 runloop 来不停的跑圈,以来处理线程里面的各个事件和消息。
主线程默认开启一个 runloop,也就是这个线程保证程序正常运行,子线程默认是没有开启 runloop 的。
runloop 有5种模式(mode),
- tracking 模式,界面跟踪模式,用于追踪 scrollview触摸滑动,保证界面滑动时不受其他 mode 影响。
- default 模式:app 默认的 mode,通常主线程就是在这个线程里面。
- common 模式,这只是一个占位的 mode,没有实际作用。
- initialization模式:app 启动时进入到第一个模式,启动完成之后就不再使用。
- eventreceive模式:接收系统时间得 mode。
- 不手动指定 autoreleasepool 情况下,一个autorelease 对象什么时候释放?
1.手动调用 autoreleasepool 去释放
2.autorelease 对象在出了作用域之后会被自动添加到最近创建的自动释放池中,并会在当前 runloop 迭代结束时释放。而他能够释放的原因是同在每个 runloop 迭代中都加入了自动释放池 push 和 pop;
- OC 完整消息转发机制
- resolveInstanceMethod:动态方法解析
- forwardingTargetForSelector:备用接受者
- forwardInvocation:方法加签
- scheduledTimer方式触发的 timer,在滑动页面列表是,timer 回暂停,为什么?怎么解决?
timer创建会自动添加到 mode 为 default 模式的 runloop,列表在滑动时 runloop 模式会切换为 tracking,所以不会响应。解决办法就是把 timer 添加到模式为 common 的 runloop。 - 如何手动触发一个 value 的 KVO?
KVO 原理是当为对象添加观察者时会重新生成一个派生类,并且把这个对象的 isa 指针指向这个派生类。
手动触发 KVO,willChangeValueForKey和 didChangeValueForKey. - 如何定位和分析项目中影响性能的地方?以及如何进行性能优化?
instruments -> timer profile,检测应用耗时操作
instruments -> leaks,检测内存泄露问题。
instruments -> Core Animation,检测离屏渲染
性能优化:
- 耗时操作尽量放在子线程中
- 控件颜色尽量不透明
- imageview 大小和实际尺寸大小一只
- 利用 tableview 重用机制
- 懒加载
- 尽量少用离屏渲染,离屏渲染是开辟一个新的帧缓冲区用于 GPU 渲染纹理,开辟这个缓冲区需要消耗性能,而且当这帧画面需要显示到屏幕上,也是需要环境上下文的切换这也是要付出代价的。
以下操作都会引起离屏渲染:
a。图层遮罩(layer.mask)
b。为图层设置圆角和阴影
c。使用CGContext在drawRect :方法中绘制大部分情况下会导致离屏渲染,甚至仅仅是一个空的实现
d。文本(任何种类,包括UILabel,CATextLayer,Core Text等) - tableview 缓存高度
- 创建全局nsdateformat对象
-
线程是什么?进程是什么?二者有什么区别?
线程是任务的执行单元,进程负责调度任务的执行。每个进程至少有一个线程,进程占用内存空间,多个线程可以访问进程的资源。 -
RunLoop 是什么?
RunLoop 是一种事件循环机制,在有事件需要处理的时候则唤醒处理,没有消息需要处理则休眠以节约资源。
线程和runloop 是一一对应的,主线程之外的线程在创建时并没有加入到 runloop 中,如果不主动添加到当前 runloop并运行,线程执行完就会销毁;所以,NSTimer 在主线程中会执行,在子线程中默认是不会执行的。在当前 runloop 只要是有 timer 和 source 消息需要处理就不会退出,一旦没有事件需要处理,runloop 就回退出。RunLoop 的创建是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。
runloop 是一一对应的,线程会被添加到全局的 hash 表中。
RunLoop能正常运行的条件就是,至少要包含一个Mode(RunLoop默认就包含DefaultMode),并且该Mode下需要有至少一个的事件源(Timer/Source),且运行在有事件源的Mode下。
经过NSRunLoop封装后,只可以往mode中添加两类事件源:NSPort(对应的是source1)和NSTimer。 -
NSURLSession与NSURLConnection区别
https://guiyongdong.github.io/2016/11/18/NSURLSession%E4%B8%8ENSURLConnection%E5%8C%BA%E5%88%AB/
网友评论