离职找工做中,刷一刷网上的面试题。原文连接ios
由于OC能够在运行时能够改变其结构:新的函数能够被引进,已有的函数能够被删除等。因此,OC是一门动态语言。它具备至关多的动态特性,基本的,也是常常被提到和用到的有动态类型(Dynamic typing),动态绑定(Dynamic binding)和动态加载(Dynamic loading)面试
id obj = someInstance;
if ([obj isKindOfClass:someClass])
{
someClass *classSpecifiedInstance = (someClass *)obj;
// Do Something to classSpecifiedInstance which now is an instance of someClass
//...
}
复制代码
动态绑定(Dynamic binding)
便是在实例所属类肯定后,将某些属性和相应的方法绑定到实例上。例如class_addMethod
这个方法就能够动态的添加方法。编程
动态加载(Dynamic loading)
让程序在运行时添加代码模块以及其余资源。用户能够根据须要加载一些可执行代码和资源,而不是在启动时就加载全部组件。好比@2x,@3x资源,就是在运行的时候根据不一样的设备加载不一样的资源。安全
它们都是MVC的变种,结构划分为:bash
其中mvp和mvvm中的v是包含了ViewController的。服务器
看图说话。网络
viewDidLoad
、
viewWillAppear
这些view的生命周期都会在controller里面来管理。再加上controller还要负责代理、数据源、网络请求等,因而controller就变得愈来愈庞大,愈来愈混乱,很很差测试。
UIViewController
,它负责全部跟UI相关的东西,好比view的生命周期管理,布局。因此P的责任更加单一,只是经过数据和状态更新View。因为V和P的分离,咱们会写不少事件传递的代码来链接V和P。好比:
import UIKit
struct Person { // Model
let firstName: String
let lastName: String
}
protocol GreetingView: class {
func setGreeting(greeting: String)
}
protocol GreetingViewPresenter {
init(view: GreetingView, person: Person)
func showGreeting()
}
class GreetingPresenter : GreetingViewPresenter {
unowned let view: GreetingView
let person: Person
required init(view: GreetingView, person: Person) {
self.view = view
self.person = person
}
func showGreeting() {
let greeting = "Hello" + " " + self.person.firstName + " " + self.person.lastName
self.view.setGreeting(greeting)
}
}
class GreetingViewController : UIViewController, GreetingView {
var presenter: GreetingViewPresenter!
let showGreetingButton = UIButton()
let greetingLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
self.showGreetingButton.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
}
func didTapButton(button: UIButton) {
self.presenter.showGreeting()
}
func setGreeting(greeting: String) {
self.greetingLabel.text = greeting
}
// layout code goes here
}
// Assembling of MVP
let model = Person(firstName: "David", lastName: "Blaine")
let view = GreetingViewController()
let presenter = GreetingPresenter(view: view, person: model)
view.presenter = presenter
复制代码
MVP带给咱们更好的可测试性的同时又带来了大量的代码。像上面这个例子里,由于V和P的分离,在须要传递事件时,View中的一个方法只调用Presenter的一个方法的状况会时常发生多线程
func didTapButton(button: UIButton) {
self.presenter.showGreeting()
}
复制代码
MVVM和MVP的区别主要在于数据绑定这一块。经过响应式编程的框架好比ReactiveCocoa来把View和ViewModel绑定在一块儿,这样咱们就不用写不少刷新页面的代码了。架构
最后说一下VIPER这个框架,它不属于MV(X)架构,它更像是乐高积木同样搭建你的应用。VIPER对职责划分了5个模块。并发
实际上 VIPER 模块能够只是一个页面(screen),也能够是你应用里整个的用户使用流程(the whole user story)- 好比说「验证」这个功能,它能够只是一个页面,也能够是连续相关的一组页面。你的每一个「乐高积木」想要有多大,都是你本身来决定的。
这篇文章对几个模式分析得很好。值得好好读一读。文中的几个例子也是引自这篇文章。
避免循环引用。
@interface SubObj : NSObject
@property(nonatomic,strong) id delegate;
@end
@interface ViewController : UIViewController
@property(nonatomic,strong)SubObj * obj;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.obj = [[SubObj alloc] init];
self.obj.delegate = self;
}
@end
复制代码
上面的代码中就出现了ViewController
持有了SubObj
,同时由于Suobj
的delegate是强引用的,因此持有了ViewController
,出现了循环引用。
delegate主要是事件抛到给代理来作。dataSource主要是数据来源。
通常状况下,简单功能的回调用block,系列函数的回调选择delegate。
nonatomic
,atomic
readonly
,readwrite
getter
,setter
strong
,assign
,weak
,copy
,unsafe_unretained
nullable
,nonnull
,null_resettable
,null_unspecified
class
基本数据类型默认关键字是 atomic
,readwrite
,assign
其余类型默认关键字是 atomic
,readwrite
,strong
防止被修改。好比:
@interface ViewController : UIViewController
@property(nonatomic,strong)NSString * text;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *test = [NSMutableString stringWithFormat:@"123"];
self.text = test;
[test appendString:@"456"];
}
@end
复制代码
这个时候打印self.text
则变成了被修改后的值123456
。
实现NSCopying
协议就让对象有了拷贝功能。
简单的说,copy
会生成一个不可变的对象,mutableCopy
会生成一个可变对象
NSArray *array = @[@1,@2];
NSArray *array2 = [array copy];//不可变
NSMutableArray *array3 = [array mutableCopy];//可变
NSArray *array4 = [array3 copy];//不可变
NSMutableArray *array5 = [array3 mutableCopy];//可变
复制代码
集合里面的元素并无内容拷贝。仍是原来的对象。
NSArray<NSMutableString *> *array = @[[NSMutableString stringWithString:@"123"]];
NSArray<NSMutableString *> *array2 = [array copy];
NSMutableString *item = array2[0];
[item appendString:@"456"];
NSLog(@"%@",array);
复制代码
打印结果是
(
123456
)
复制代码
由于view
被添加到superView
上面后,就被superView
持有了。咱们通常在IB里面的拖的view
都是加在了根view或者它的子view上。而根view又被它的controller
持有,因此IBOutlet
能够用weak
。若是,在IB里面拖出来的view是一个单独的view
没有被加到任何其余view
上,则须要用strong
。
nonatomic
表示非原子性,不安全,效率高。atomic
表示原子性,效率低。
atomic
不是绝对的线程安全的。
@property (atomic, assign) int intA;
//thread A
for (int i = 0; i < 10000; i ++) {
self.intA = self.intA + 1;
NSLog(@"Thread A: %d\n", self.intA);
}
//thread B
for (int i = 0; i < 10000; i ++) {
self.intA = self.intA + 1;
NSLog(@"Thread B: %d\n", self.intA);
}
复制代码
即便我将intA声明为atomic,最后的结果也不必定会是20000。缘由就是由于self.intA = self.intA + 1
;不是原子操做,虽然intA的getter和setter是原子操做,但当咱们使用intA的时候,整个语句并非原子的,这行赋值的代码至少包含读取(load),+1(add),赋值(store)三步操做,当前线程store的时候可能其余线程已经执行了若干次store了,致使最后的值小于预期值。这种场景咱们也能够称之为多线程不安全。
@property (atomic, strong) NSString* stringA;
//thread A
for (int i = 0; i < 100000; i ++) {
if (i % 2 == 0) {
self.stringA = @"a very long string";
}
else {
self.stringA = @"string";
}
NSLog(@"Thread A: %@\n", self.stringA);
}
//thread B
for (int i = 0; i < 100000; i ++) {
if (self.stringA.length >= 10) {
NSString* subStr = [self.stringA substringWithRange:NSMakeRange(0, 10)];
}
NSLog(@"Thread B: %@\n", self.stringA);
}
复制代码
虽然stringA是atomic的property,并且在取substring的时候作了length判断,线程B仍是很容易crash,由于在前一刻读length的时候self.stringA = @"a very long string";,下一刻取substring的时候线程A已经将self.stringA = @"string";,当即出现out of bounds的Exception,crash,多线程不安全。
这段例子引用于这篇文章
继承UICollectionViewLayout
本身实现prepareLayout
,collectionViewContentSize
,layoutAttributesForElementsInRect:
这三个方法。
平日里开发,复用较多的模块用的xib来写。单独的模块,如设置界面用的storyboard来开发。
关于StoryBoard的讨论能够参看喵神的这篇文章
进程(process)
狭义的定义:进程就是一段程序的执行过程。
广义定义:进程是一个具备必定独立功能的程序关于某次数据集合的一次运行活动,它是操做系统分配资源的基本单元。
简单来说进程的概念主要有两点:第一,进程是一个实体。每个进程都有它本身的地址空间,通常状况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程当中调用的指令和本地变量。第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体,咱们称其为进程。
进程状态:进程有三个状态,就绪,运行和阻塞。就绪状态其实就是获取了除cpu外的全部资源,只要处理器分配资源立刻就能够运行。运行态就是获取了处理器分配的资源,程序开始执行,阻塞态,当程序条件不够时,须要等待条件知足时候才能执行,如等待I/O操做的时候,此刻的状态就叫阻塞态。
说说程序,程序是指令和数据的有序集合,其自己没有任何运动的含义,是一个静态的概念,而进程则是在处理机上的一次执行过程,它是一个动态的概念。进程是包含程序的,进程的执行离不开程序,进程中的文本区域就是代码区,也就是程序。
线程(thread)
一般在一个进程中能够包含若干个线程,固然一个进程中至少有一个线程,否则没有存在的意义。线程能够利用进程所拥有的资源,在引入线程的操做系统中,一般都是把进程做为分配资源的基本单位,而把线程做为独立运行和独立调度的基本单位,因为线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提升系统多个程序间并发执行的程度。
引用于这篇文章
同步是串行的顺序执行,异步是并行的同时执行
并发和并行的区别就是一个处理器同时处理多个任务和多个处理器或者是多核的处理器同时处理多个不一样的任务。
前者是逻辑上的同时发生(simultaneous),然后者是物理上的同时发生.
并发性(concurrency),又称共行性,是指能处理多个同时性活动的能力,并发事件之间不必定要同一时刻发生。
并行(parallelism)是指同时发生的两个并发事件,具备并发的含义,而并发则不必定并行
线程间通讯指的是:一、一个线程传递数据给另外一个线程,二、在一个线程中执行完特定任务后,转到另外一个线程继续执行任务。
在iOS中能够用这些方法来进行线程间通讯:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
dispatch_async(otherQueue, ^{
// dosth
});
复制代码
dispatch_group
:作完一组操做后再执行后续的代码
它有两种用法: 一种是dispatch_group_async
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"group one start");
dispatch_group_async(group, queue, ^{
//do something
});
dispatch_group_async(group, queue, ^{
//do something
});
dispatch_group_notify(group, queue, ^{
//do something
});
复制代码
第二种是dispatch_group_enter
和dispatch_group_leave
:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_enter(group);
dispatch_async(queue, ^{
//do something
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
//do something
dispatch_group_leave(group);
});
dispatch_group_notify(group, queue, ^{
//do something
});
复制代码
barrier
- (void)barrier {
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
// dosth1;
});
dispatch_async(queue, ^{
// dosth2;
});
dispatch_barrier_async(queue, ^{
// doBarrier;
});
dispatch_async(queue, ^{
// dosth4;
});
dispatch_async(queue, ^{
// dosth5;
});
}
复制代码
dispatch_semaphore
当咱们多个线程要访问同一个资源的时候,每每会设置一个信号量,当信号量大于0的时候,新的线程能够去操做这个资源,操做时信号量-1,操做完后信号量+1,当信号量等于0的时候,必须等待,因此经过控制信号量,咱们能够控制可以同时进行的并发数。
信号量有如下3个函数
dispatch_semaphore_create //建立一个信号量
dispatch_semaphore_signal //信号量+1
dispatch_semaphore_wait //等待,直到信号量大于0时,便可操做,同时将信号量-1
复制代码
-(void)dispatchSignal{
//crate的value表示,最多几个资源可访问
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任务1
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 1");
sleep(1);
NSLog(@"complete task 1");
dispatch_semaphore_signal(semaphore);
});
//任务2
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 2");
sleep(1);
NSLog(@"complete task 2");
dispatch_semaphore_signal(semaphore);
});
//任务3
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 3");
sleep(1);
NSLog(@"complete task 3");
dispatch_semaphore_signal(semaphore);
});
}
复制代码
执行结果为
run task 1
run task 2
complete task 1
complete task 2
run task 3
complete task 3
复制代码
因为设定的信号值为2,先执行两个线程,等执行完一个,才会继续执行下一个,保证同一时间执行的线程数不超过2。
若是咱们把信号量设置成1dispatch_semaphore_create(1)
,那么执行结果就会变成顺序执行
run task 1
complete task 1
run task 2
complete task 2
run task 3
complete task 3
复制代码
用锁,或者把资源的操做放到单一线程中。
// 当应用程序启动时(不包括已在后台的状况下转到前台),调用此回调。launchOptions是启动参数,假如用户经过点击push通知启动的应用,这个参数里会存储一些push通知的信息
– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions NS_AVAILABLE_IOS(3_0);
– (void)applicationDidBecomeActive:(UIApplication *)application;
//应用即将从前台状态转入后台
- (void)applicationWillResignActive:(UIApplication *)application;
– (void)applicationDidEnterBackground:(UIApplication *)application NS_AVAILABLE_IOS(4_0);
//从后台到前台调用了:
– (void)applicationWillEnterForeground:(UIApplication *)application NS_AVAILABLE_IOS(4_0);
– (void)applicationDidBecomeActive:(UIApplication *)application;
复制代码
NSLog(@"%@")
和[NSString stringWithFormat:@"%@"]
会转换成description
返回的字符串。
使用引用计数机制管理对象内存。