转载

iOS面试常见内容解析:设计模式

iOS有哪些常见的设计模式?

单例模式/委托模式/观察者模式/MVC模式

单例模式

单例保证了应用程序的生命周期内仅有一个该类的实例对象,而且易于外界访问.

在ios sdk中,UIApplication, NSBundle, NSNotificationCenter, NSFileManager, NSUserDefault, NSURLCache等都是单例.

在实际开发中,单例一般会分为ARC和非ARC两种不同的写法,这些代码可以通过判断统一整理到PCH文件中,这样每次使用单例时就不用多次输入重复的代码.

单例的代码简单样例:

//.h
@interface Singleton:NSObject
+(Singleton *)sharedManager;
@property(nonatomic,strong)NSString *singletonData;
@end
//.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;
}
@end

委托模式

委托Delegate是协议的一种,通过@protocol方式实现. 顾名思义, 就是委托他人帮自己去做什么事. 也就是当自己不方便做什么事情的时候, 就可以建立一个委托, 这样就可以委托他人帮忙去做.

比如常见的UITableView, iOS SDK在写这个类的时候,肯定不知道开发者想要在点击某一行之后都要进行什么操作, 所以只好让程序员委托UIViewController去代理UITableViewDelegate来实现这个方法.

同样的,它也不知道开发者希望这个表有多少行,每个cell的内容都是什么样的,所以UITableView只好委托UIViewController去代理他的UITableViewDataSource来实现这些方法.

当然,代理方法一是可以传递事件, 二也可以用来传递值,

比如:tableview的datasource里的一个代理方法

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

委托者可以把行数通过indexpath传递给被委托者,被委托者取到行数之后,自定义如何表现这个cell,再把cell传回给委托者.

下面有用一个Man类的例子来详细解说一下.

Man类,可以初始化一个胖子或瘦子,然后完成他的早中晚的一天......然后吃饭工作睡觉之后都干什么他自己不知道.....只好告诉你他吃了什么,睡了多久 ,让你来帮他决定.....我瞎编的...你也许可以编一个好一点的....

//man.h
#import //*************** delegate ***********************
//创建代理.吃喝工作的结果让别人帮你完成,你只需要提供吃了啥,睡了多久就行了.
@protocol ManDelegate @required
//人可以吃不睡,但是一定要工作啊....所以required
-(void)work;
@optional
//返回一个睡觉的时间给被委托的对象,通过这个时间来处理相关事宜
-(void)sleepWithTime:(int)time;
//通过给被委托者一种食物,返回一个布尔值,来判断是否吃饱.
-(BOOL)eatWithFood:(NSString *)food;
@end
//*************** delegate ***********************
@interface Man : NSObject
//delegate属性
@property (nonatomic, weak) id delegate;
//是胖,是瘦呢,这个在实例化类的时候完成.
@property (nonatomic, assign) BOOL isFat;
//你这一天都分别干了啥,这个要你自己完成
-(void)morning;
-(void)afternoon;
-(void)everning;
@end
//man.m
#import "Man.h"
@implementation Man
//通过[self.delegate XXXX]确定代理出去的方法在自己的方法里的实现的位置;
//早上睡了7个小时.吃了点面包,没吃饱就不工作,吃饱了就接着工作..
-(void)morning{
    [self.delegate sleepWithTime:7];
    BOOL isFull = [self.delegate eatWithFood:@"面包"];
    //觉得面包吃饱了就工作,面包吃不饱就不工作.
    if(isFull == YES){
        NSLog(@"早饭吃饱了,工作");
        [self.delegate work];
    }
    else{
        NSLog(@"早饭吃饱了,不工作");
    }
}
//中午睡了1个小时,睡完接着工作.
-(void)afternoon{
    [self.delegate sleepWithTime:1];
    [self.delegate work];
}
//晚上睡了3个小时...
-(void)everning{
    [self.delegate sleepWithTime:3];
}
@end
//代理执行的位置
#import "ViewController.h"
#import "Man.h"
@interface ViewController ()@property (nonatomic,retain) Man *zhangsan;
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    //初始化一个张三实例,张三不胖.
    self.zhangsan = [[Man alloc] init];
    self.zhangsan.isFat = NO;
    
    //把张三工作/吃/睡之后要干嘛,交给self也就是ViewController来决定,就是实现代理方法
    self.zhangsan.delegate = self;
    //张三的一天.
    [self.zhangsan morning];
    [self.zhangsan afternoon];
    [self.zhangsan everning];
}
#pragma mark ----ManDelegate
//viewcontroller决定,让张三工作的时候打印一句话到控制台.
-(void)work{
    NSLog(@"Working");
}
//viewcontroller决定,根据张三提供来的休息时间,把他的休息情况打印到控制台上.
-(void)sleepWithTime:(int)time
{
    NSLog(@"Slept for %d hour",time);
    if(time>4){
        NSLog(@"长眠不起");
    }else if(time == 4){
        NSLog(@"不长不短");
    }else if(time < 4){
        NSLog(@"小眯一下");
    }
}
//viewcontroller决定,通过张三提供吃的食物的内容,来判断张三是不是吃饱了...并且返回给张三...让他自己通过判断自己饱了没饱来处理一些事情
-(BOOL)eatWithFood:(NSString *)food{
    if (self.zhangsan.isFat == YES && [food isEqualToString:@"面包"]) {
        return NO;
    }
    else if(self.zhangsan.isFat == NO && [food isEqualToString:@"面包"]){
        return YES;
    }
    else{
        return YES;
    }
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

问题1:为什么delegate作为属性需要是weak?

防止retain cycle循环引用...

比如tableview....UIViewCotroller通过self.view addsubview.已经指向tableview了......所以tableview.delegate = self 和tableview.datasource又指向了viewcontroller,如果此时delegate是strong,就会发生循环引用无法释放,所以这里应该是weak.....详细见上一章属详解.

观察者模式

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。 简而言之,就是A和B,A对B的变化感兴趣,就注册为B的观察者,当B发生变化时通知A,告知B发生了变化。这个也叫做经典观察者模式。

在iOS中,观察者模式的具体实现有两种: 通知机制(notification)和KVO机制(Key-value Observing)

1.通知机制

注册通知接收者的代码:

[[NSNotificationCenter DefaultCenter] addObserver:self
                                        selector:@selector(registerCompeletion:)
                                           name:@"RigsterCompletionNotification"
                                          object:nil];
-(void)registerCompletion:(NSNotification *)notification{
      NSDictionary *theData = [notification useInfo];
      NSString *username = [theData objectForKey:@"username"];
      NSLog(@"username = %@",username);
}

投送通知的代码

NSDictionary *dataDic = [NSDictionary dictionaryWithObject:self.text
                                                    forkey:@"username"];
[[NSNotificationCenter DefaultCenter] postNotificationName:@"RegisterCompeletionNotification"
                                                    object:nil
                                                  userInfo:dataDict];

问题1. object是干嘛的?是不是可以用来传值?

答:object是用来过滤Notification的,只接收指定的sender所发的Notification.....传值请用userInfo,而不是object

问题2. iOS中,广播通知(broadcast notification)/本地通知(local notification)/推送通知(push notification)有什么区别和不同?

出了名字相似以外,广播通知和其他两个通知是完全不一样的: 广播通知是cocoatouch中观察者模式的一种机制, 实现应用内部多个对象的通信...本地通知和推送通知中的"通知"是一种"提示"...通过警告框,发出声音,振动和在应用图标上显示数组等,在计划时间到达时,本地通知通过本地iOS发出,推送通知由第三方程序发送给苹果远程服务器,再由远程服务器推送给iOS特定应用.

2.KVO机制

对象的属性变化时,通知会直接发送到观察者对象.

可以用来实现 ,比如, UITableView滑动时导航自动变换颜色,或者,一个计算房贷的软件,当首付金额改变时,每月还款数目等数据相应自动改变等.

这里来观察UITableView一个实例的contentOffset属性.

//添加监听者
[self.tableView addObserver:self    //监听者
                forKeyPath:@"contentOffset"  //被观察对象的属性字符串
      options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld//设置.这里的设置表示把属性变换的新旧两个值都传递给观察者
                   context:nil];//上下文内容,c语言形式的任何指针类型
//监听属性变化时的回调
-(void)obserValueForKeyPath:(NSString *)keyPath   //被观察的属性
                   ofObject:(id)object   //被观察的对象
                     change:(NSDictionary *)change//字典类型包含了变化的内容,与options呼应
                    context:(void *)context//传递过来的上下文
{
             NSLog(@"%@-%@",keyPah, (NSString *)change[NSKeyValueChangeNewKey]);//通过change字典渠道变化的属性变化前或变化后的值.
}
正文到此结束
Loading...