美文网首页
iOS面试题(1-5)

iOS面试题(1-5)

作者: 西门吹灰 | 来源:发表于2016-07-18 08:19 被阅读0次
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


屏幕快照 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不像通知机制那样通过一个通知中心通知所有观察者对象,而是在对象属性变化时通知会被直接发送给观察者对象


屏幕快照 2016-07-16 下午3.43.19.png

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

@interface AppDelegate : UIResponder <UIApplicationDelegate>
@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是注册时传递的上下文

屏幕快照 2016-07-16 下午4.08.19.png

MVC模式

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

屏幕快照 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

屏幕快照 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.简述视图控制器的生命周期

20140929170950334.jpeg

loadView中的三个方法

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

viewDidLoad

loadData 加载数据,调用一些api

dealloc

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

didReceiveMemoryWarning

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

额外

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

相关文章

网友评论

      本文标题:iOS面试题(1-5)

      本文链接:https://www.haomeiwen.com/subject/pwlujttx.html