iOS面试题(1-5)

0D6829F1A95EA9C1B42E3FD9413D5230.jpg

1.请描述你所了解的设计模式.举例并描述其使用场景

单例模式

iOS生命周期中,有时我们只需要某个类的一个实例.

应用案例

UIApplication,UIAccelerometer,NSUserDefaults,NSNotificationCenter,NSFileManager,NSBundle,NSWorkspace,NSApplication

实现代码

//
// Singleton.h
//
@interface Singleton : NSObject
(Singleton *)sharedManager;
@property (nonatomic, strong) NSString *singletonData;
@end
//
// Singleton.m
//
#import “Singleton.h”
@implementation Singleton
static Singleton *sharedManager = nil;
+ (Singleton *)sharedManager {
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        sharedManager = [[self alloc] init];
    });
    return sharedManager;
}

委托模式

委托是为了降低一个对象的复杂度和耦合度,使其能够更具通用性而将其中一些处理置于委托对象的编码方式.通用类因为通用性(与具体应用的无关性)而变为框架类,框架类保存委托对象的指针,并在特定时刻向委托对象发送消息.

应用案例

UITextFieldDelegate
textFieldShouldBeginEditing:
textFieldDidBeginEditing:
textFieldShouldEndEditing:
textFieldDidEndEditing:
委托消息的命名有一定的定性:使用Should时,应该返回一个布尔值;使用Will时,没有返回值,表示改变之前要做的事情;使用Did时,没有返回值,表示改变之后要做的事情.
UITableView:委托对象主要对控件对象的事件和状态变化做出响应,而数据源对象是为控件对象提供数据.委托中的方法在实现时是可选的,而数据源中的方法一般必须实现

观察者模式

一个对象状态改变也会连带影响其他很多对象的状态发生改变.观察者模式复用性强且对象之间匿名通信.

应用案例

通知机制和KVO机制

通知机制

通知机制与委托机制不同的是,前者是”一对多”的对象之间的通信,后者是”一对一”的对象之间的通信
使用Utility Application工程模板,这个模板有主界面控制器(MainViewController)和翻转界面控制器(FlipsideViewController),还有应用程序委托对象AppDelegate


iOS面试题(1-5)_第1张图片
屏幕快照 2016-07-16 下午2.05.45.png

在MainViewController和FlipsideViewController这两个视图控制器中,注册通知者

- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleTerminate:)
                                                 name:@“AppWillTerminateNotification”
                                               object:nil];
}

解除通知

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserve:self];
}

MainViewConrtroler和FilpsideViewController的处理通知方法

- (void)handleTerminate:(NSNotification *)notification {
    NSDictionary *theData = [notification userInfo];
    if (theData != nil) {
        NSDate *date = [theData objectForKey:@"TerminateDate"];
        NSLog(@"FlipsideViewController App Terminate Date: %@", date);
  }
}

如果我们想在应用终止时投送通知,需要重写AppDelegate中的方法

- (void)applicationWillTerminate:(UIApplication *)application {
    NSDate *date = [NSDate date];
    NSDictionary *dataDict = [NSDictionary dictionaryWithObject:date forKey:@"TermineDate"];
    [[NSNotificationCenter default] postNotificationName:@"AppWillTerminateNotification" object:self userInfo:DateDict];

// 除了上面代码中所示之外,还有另外一个重载方法:
    [[NSNotificationCenter defaultCenter] postNotificationName:@"AppWillTerminateNotification" object:self];
}

// 事实上,Cocoa Touch框架该通知,当应用事件发生时,由iOS系统自动投送
// so, 删除上面的代码并修改MainViewController和FlipsideViewController中注册接收通知者的代码
- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserve:self
                                            selector:@selector(handleTerminate:)
                                                name:UIApplicationWillTerminateNotification
                                              object:nil];
}

除了应用生命周期的不同阶段有不同的通知外,很多控件也会在某些事件发生时投送通知,例如UITextField控件.在编辑过程的不同阶段会分别发出如下通知:UITextFieldTextDidBeginEditingNotification, UITextFieldTextDidChangeNotification, UITextFieldTextDidEndEditingNotification

KVO机制

KVO不像通知机制那样通过一个通知中心通知所有观察者对象,而是在对象属性变化时通知会被直接发送给观察者对象


iOS面试题(1-5)_第2张图片
屏幕快照 2016-07-16 下午3.43.19.png

应用程序委托对象的AppDelegate.h

@interface AppDelegate : UIResponder 
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) NSString *appStatus;  // 记录应用程序状态的变化
@property (strong, nonatomic) AppStatusWatcher *watcher;  // 定义ppStatusWatcher类型的观察者对象属性

AppDelegate.m

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.watcher = [AppStatusWatcher new];
    
    // addObserver是要要被关注的对象
    // forKeyPath是要被关注对象的属性
    // options是为属性变化设置的选项,NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld意味着把新旧两个值都传递给观察者
    // context上下文内容,类型为(void*),如果传递"空",应是"NULL",而非"nil"
    [self addObserver:self.watcher forKeyPath:@"appStatus" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"Pass Context"];
    self.appStatus = @"launch";
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
    self.appStatus = @"inactive";
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    self.appStatus = @"background";
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    self.appStatus = @"inactive";
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    self.appStatus = @"active";
}

- (void)applicationWillTerminate:(UIApplication *)application {
    self.appStatus = @"terminate";
}

观察者AppStatusWatcher的代码

@interface AppStatusWatcher : NSObject
@end

@implementation AppStatusWatcher

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    NSLog(@"Property '%@' of object '%@' changed: %@ context: %@", keyPath, object, change, context);
}
@end

因为NSbject类实现了NSKeyValueObserving协议,所以只需声明AppStatusWatcher继承NSObject类,无需实现NSKeyValueObserving协议.
observeValueForKeyPath:ofObject:change:context:方法中的
observeValueForKeyPath参数是被关注的属性
ofObject是被关注的对象
change是字典类型,包含了属性变化的内容,这些内容与注册时属性设置的选项(options参数)有关
context是注册时传递的上下文

iOS面试题(1-5)_第3张图片
屏幕快照 2016-07-16 下午4.08.19.png

MVC模式

Model-ViewController,模型-视图-控制器 模式

iOS面试题(1-5)_第4张图片
屏幕快照 2016-07-16 下午4.10.29.png

模型:保存应用数据的状态,回应视图对状态的查询,处理应用业务逻辑,完成应用的功能,将状态的变化通知视图.
视图:为用户展示信息并提供接口,用户通过视图想控制器发出动作请求,然后再向模型发出查询状态的申请,而模型状态的变化会通知给视图
控制器:接收用户请求,根据请求更新模型.另外,控制器还会更新所选择的视图作为对用户请求的回应.控制器是视图和模型的媒介,可以降低视图和模型的耦合度,是视图和模型的权责更加清晰,从而提高开发效率.

2.不手动指定autoreleasepool的前提下,一个autorelease对象在什么时刻释放?

在MRR(Manual Retain Release, 手动保持释放), 也称为MRC(Manual Reference Counting, 手动引用计数)中,释放对象通过release或autorelease消息实现,其中release消息会立刻使引用计数减1,autorelease消息会使对象放入内存释放池中延迟释放,对象的引用计数并不变化,而是向内存释放池中添加一条记录,直到池被销毁前通知池中的所有对象全部发送release消息才真正将引用计数减少.
在iOS程序中,默认内存释放池的释放在程序结束

注意

int main(int argh, char *argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argh, argue, nil, NSStringFromClass([AppDelegate class]));
    }
}

代码被包裹在@autoreleasepool{...}之间,这是池的作用范围,默认是整个应用.如果产生大量对象,采用autorelease释放也会导致内存泄漏.

什么时候必须使用autorelease?

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"CellIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }

    NSUInteger row = [indexPath row];
    NSDictionary *rowDic = [self.listTeams objectAtIndex:row];
    cell.textLabel.text = [rowDict objectForKey:@"name"];

    NSString *imagePath = [rowDict objectForKey:@"image"];
    imagePath = [imagePath stringByAppendingString:@".png"];
    cell.imageView.image = [UIImage imageNamed:imagePath];

    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

    return cell;
}

1> 上述代码中,cell对象不能马上释放,我们需要使用它设置表视图界面.autorelease一般用在为其他调用者提供对象的方法中,对象在该方法不能马上释放,而需要延迟释放.
2> 此外,还有一种情况需要使用autorelease,即"类级构造方法":

NSString *message = [NSString stringWithFormat:@"您选择了%@队", rowValue];

该对象的所有权虽然不是当前调用者,但它是由iOS系统通过发送autorelease消息放入到池中的.当然,这一切对于开发者都是不可见的,我们也要注意减少使用这样的语句

3.分别描述Swift和OC中的访问限制有哪些?

Swift

OC

iOS面试题(1-5)_第5张图片
屏幕快照 2016-07-16 下午5.14.56.png

4.简述KVC和KVO

KVC(Key Value Coding)

键值编码是Cocoa的标准组件,允许开发者通过名字(键)访问属性,而无需调用显式的存取方法.由此,系统的某些部分就可以动态访问属性,即使在编译的时候不知道属性的键是什么.这种动态访问对于nib文件的加载和Core Data尤其重要.在Mac系统上,KVC是AppleScript接口的基础部分.
valueForKeyPath:会自动把数字类型(int,float等)转换成NSNumber对象,而其他的非对象类型(结构体,指针)会转换成NSValue对象.
KVC方法有key和keyPath两个版本.键和键路径的区别在于,后者可以包含嵌套关系,用句点分开.��

用KVC实现高阶消息传递

valueForKey:有很对有用的特例,比如说NSArray和NSSet这样的容器类都覆盖了这个方法.valueForKey:被传递给容器中的每个对象,而不是对容器本身进行操作.结果会被添加进返回的容器中.这样可以很方便的用一个容器对象创建另一个容器对象.

NSArray *array = @[@"foo", @"bar", @"baz"];
NSArray *capitals = [array valueForKey:@"capitalizedString"];

方法capitalizedString被传递给NSArray中的每一项,并返回一个包含结果的新NSArray,把消息(capitalizedString)作为参数传递称为高阶消息传递(Higher Order Messaging).多个消息可以用键路径传递:

NSArray *array = @[@"foo", @"bar", @"baz"];
NSArray *capitalLengths = [array valueForKey:@"capitalizedString.length"];

以上代码对array的每一个元素调用capitalizedString,然后调用length,再把返回值封装进NSNumber对象.结果被收集进名为capitalLengths的新数组

KVO(Key Value Observing)键值观察

键值观察是就对象对象属性变化透明地通知观察者的一种机制.
实现功能:无论对象什么时候发生变化,都可以让表格单元自动更新.
KVO用addObserver:forKeyPath:options:context:开始观察,用removeObserver:forKeyPath:context:停止观察.回调总是observeValueForKeyPath:ofObject:change:context:

// KVCTableViewCell.m

- (void)removeObservation {
  if (self.isReady) {
    [self.object removeObserver:self forKeyPath:self.property];
  }
}

- (void)addObservation {
  if (self.isReady) {
    [self.object addObserver:self forKeyPath:self.property options:0 context:(void *)self];
  }
}

- (void)observeValueForKeyPat:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change {
  if ((__bridge id)context == self) {
    [self update];
  }
  else {
    [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
  }
}

- (void)dealloc {
  if (__object && [_property length] > 0) {
    [_object removeObserver:self forKeyPath:_property context:(void *)self];
  }
}

- (void)setObject:(id)anObject {
  [self removeObservation];
  _object = anObject;
  [self addObservation];
  [self update];
}

- (void)setProperty:(NSString *)aProperty {
  [self removeObservation];
  _property = aProperty;
  [self addObservation];
  [self update];
}
// KVCTableViewController.m

#import "RNTimer.h"
@interface KVCTableViewController ()
@property (readwrite, retain) RNTimer *timer;
@property (readwrite, retain) NSDate *now;
@end

@implementation KVCTableViewController

- (void)updateNow {
  self.now = [NSDate date];
}

- (void)viewDidLoad {
  [self updateNow];

  __weak id weakSelf = self;
  self.timer = [RNTimer repeatingTimerWithInterval:1 block:^{
    [weakSelf updateNow];
  }];
}

- (void)viewDidUnload {
  self.timer = nil;
  self.now = nil;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  static NSString *CellIdentifier = @"KVCTableViewCell";

  id cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  if (cell == nil) {
    cell = [[[KVCTableView alloc] initWithReusableIdentifier:CellIdentifier] autorelease];
  [cell setProperty:@"now"];
  [cell setObject:self];
  }
  return cell;
}
@end

5.简述视图控制器的生命周期

iOS面试题(1-5)_第6张图片
20140929170950334.jpeg

loadView中的三个方法

createFields 接受参数,初始化变量
createViews 创建视图
createEvents 绑定事件.如按钮的点击,NotificationCenter,KVO

viewDidLoad

loadData 加载数据,调用一些api

dealloc

destroyEvents 取消事件绑定
destroyViews 释放,销毁视图
destroyFields 释放,销毁引用的变量

didReceiveMemoryWarning

cleanData 释放一些可以释放的资源

额外

enterForeground 进入前台时调用
enterBackground 进入后台时调用

你可能感兴趣的:(iOS面试题(1-5))