您的位置:首页 > 职场人生

100道ios面试题目的总结体会

2016-02-22 15:06 337 查看
常见问题

- 你昨天/这周学习了什么?

昨天到目前为止学习了图片,以及视频上传的压缩处理,还有就是instruments上面的一些用法,还有一个自动化测试工具,进行软件的测试处理

常见问题

- 你为什么热衷于软件开发?

你见过妹子不爱美妆爱code的吗,所以对软件开发是一种热爱,在心情不好或是很烦躁的时候看看code或是研究一下code就会心情比较好一些

- 你对哪一种控制系统比较熟悉?

- 是否参与过GitHub项目?

这个对于现在的我一个初级ios开发说,可能目前还是download一些别人的代码进行研究和借鉴

- 是否参与过GitHub或其他同类型网站的iOS开源项目?

- 请描述一下你的iOS应用开发流程。

首先是整个项目

- 是否熟知CocoaPods?它是什么?如何运行的?

熟悉使用过CocoaPods使用,之前 有一个项目中使用的是pods,具体见笔记

- 请概括一下你对软件授权的理解,及其对软件开发的影响。

首先Oauth授权的原理知识就不再赘述

以微信的第三方授权来说微信登录授权开发

到微信开发平台注册相关APP,现在是等待审核成功后才能获取到对应的key和secret;获取成功后需要单独申请开通登录和支付接口,如图

和QQ类似,需要填写Url Schemes,如demo中的wxd930ea5d5a258f4f ,然后引入相应framework;

在AppDelegate中注册和实现授权后的回调函数,代码如下:

//向微信注册

[WXApi registerApp:kWXAPP_ID withDescription:@”weixin”];

//授权后回调 WXApiDelegate

-(void)onResp:(BaseReq *)resp

{

/*

ErrCode ERR_OK = 0(用户同意)

ERR_AUTH_DENIED = -4(用户拒绝授权)

ERR_USER_CANCEL = -2(用户取消)

code 用户换取access_token的code,仅在ErrCode为0时有效

state 第三方程序发送时用来标识其请求的唯一性的标志,由第三方程序调用sendReq时传入,由微信终端回传,state字符串长度不能超过1K

lang 微信客户端当前语言

country 微信用户当前国家信息

*/

SendAuthResp aresp = (SendAuthResp )resp;

if (aresp.errCode== 0) {

NSString *code = aresp.code;

NSDictionary *dic = @{@”code”:code};

}
}
//和QQ,新浪并列回调句柄

- (BOOL)application:(UIApplication )application openURL:(NSURL )url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation

{

return [TencentOAuth HandleOpenURL:url] ||

[WeiboSDK handleOpenURL:url delegate:self] ||

[WXApi handleOpenURL:url delegate:self];;

}
(BOOL)application:(UIApplication )application handleOpenURL:(NSURL )url

{

return [TencentOAuth HandleOpenURL:url] ||

[WeiboSDK handleOpenURL:url delegate:self] ||

[WXApi handleOpenURL:url delegate:self];;

}
微信登录授权比较复杂,相比QQ,新浪多了几步,简单说就是需要三步,第一步,获取code,这个用来获取token,第二步,就是带上code获取token,第三步,根据第二步获取的token和openid来获取用户的相关信息;

下面用代码来实现:

第一步:code

(IBAction)weixinLogin:(id)sender

{

[self sendAuthRequest];

}
-(void)sendAuthRequest

{

SendAuthReq* req =[[SendAuthReq alloc ] init];

req.scope = @”snsapi_userinfo,snsapi_base”;

req.state = @”0744” ;

[WXApi sendReq:req];

}
这里获取后会调用之前在AppDelegate里面的对应oauthResp回调,获得得到的code。

第二步:token和openid

-(void)getAccess_token

{

//https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

NSString *url =[NSString stringWithFormat:@"https://api.weixin.qq.com/sns/oauth2/access_token?appid=%@&secret=%@&code=%@&grant_type=authorization_code",kWXAPP_ID,kWXAPP_SECRET,self.wxCode.text];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *zoneUrl = [NSURL URLWithString:url];
NSString *zoneStr = [NSString stringWithContentsOfURL:zoneUrl encoding:NSUTF8StringEncoding error:nil];
NSData *data = [zoneStr dataUsingEncoding:NSUTF8StringEncoding];
dispatch_async(dispatch_get_main_queue(), ^{
if (data) {
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
/*
{
"access_token" = "OezXcEiiBSKSxW0eoylIeJDUKD6z6dmr42JANLPjNN7Kaf3e4GZ2OncrCfiKnGWiusJMZwzQU8kXcnT1hNs_ykAFDfDEuNp6waj-bDdepEzooL_k1vb7EQzhP8plTbD0AgR8zCRi1It3eNS7yRyd5A";
"expires_in" = 7200;
openid = oyAaTjsDx7pl4Q42O3sDzDtA7gZs;
"refresh_token" = "OezXcEiiBSKSxW0eoylIeJDUKD6z6dmr42JANLPjNN7Kaf3e4GZ2OncrCfiKnGWi2ZzH_XfVVxZbmha9oSFnKAhFsS0iyARkXCa7zPu4MqVRdwyb8J16V8cWw7oNIff0l-5F-4-GJwD8MopmjHXKiA";
scope = "snsapi_userinfo,snsapi_base";
}
*/

self.access_token.text = [dic objectForKey:@"access_token"];
self.openid.text = [dic objectForKey:@"openid"];

}
});
});


}
利用GCD来获取对应的token和openID.

第三步:userinfo

-(void)getUserInfo

{

// https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID

NSString *url =[NSString stringWithFormat:@"https://api.weixin.qq.com/sns/userinfo?access_token=%@&openid=%@",self.access_token.text,self.openid.text];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *zoneUrl = [NSURL URLWithString:url];
NSString *zoneStr = [NSString stringWithContentsOfURL:zoneUrl encoding:NSUTF8StringEncoding error:nil];
NSData *data = [zoneStr dataUsingEncoding:NSUTF8StringEncoding];
dispatch_async(dispatch_get_main_queue(), ^{
if (data) {
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
/*
{
city = Haidian;
country = CN;
headimgurl = "http://wx.qlogo.cn/mmopen/FrdAUicrPIibcpGzxuD0kjfnvc2klwzQ62a1brlWq1sjNfWREia6W8Cf8kNCbErowsSUcGSIltXTqrhQgPEibYakpl5EokGMibMPU/0";
language = "zh_CN";
nickname = "xxx";
openid = oyAaTjsDx7pl4xxxxxxx;
privilege =    (
);
province = Beijing;
sex = 1;
unionid = oyAaTjsxxxxxxQ42O3xxxxxxs;
}
*/

self.nickname.text = [dic objectForKey:@"nickname"];
self.wxHeadImg.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[dic objectForKey:@"headimgurl"]]]];

}
});

});


}
执行到这一步就算完成了整个授权登录的功能,能把昵称和头像显示出来,剩下的就是及时刷新你的token

请概括一下你在构建iOS应用时的测试过程。iOS应用如何实现对其他语言、日期格式以及货币单位的支持?

在开发iOS程序时,有时候需要将时间格式调整成自己希望的格式,这个时候我们可以用NSDateFormatter类来处理。例如:

//实例化一个NSDateFormatter对象

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];

//设定时间格式,这里可以设置成自己需要的格式

[dateFormatter setDateFormat:@”yyyy-MM-dd HH:mm:ss”];

//用[NSDate date]可以获取系统当前时间

NSString *currentDateStr = [dateFormatter stringFromDate:[NSDate date]];

//输出格式为:2010-10-27 10:22:13

NSLog(@”%@”,currentDateStr);

//alloc后对不使用的对象别忘了release

[dateFormatter release];

一、字符说明

(:)

时间分隔符。在某些区域设置中,可以使用其他字符表示时间分隔符。时间分隔符在格式化时间值时分隔小时、分钟和秒。格式化输出中用作时间分隔符的实际字符由您的应用程序的当前区域性值确定。

(/)

日期分隔符。在某些区域设置中,可以使用其他字符表示日期分隔符。日期分隔符在格式化日期值时分隔日、月和年。格式化输出中用作日期分隔符的实际字符由您的应用程序的当前区域性确定。

(%)

用于表明不论尾随什么字母,随后字符都应该以单字母格式读取。也用于表明单字母格式应以用户定义格式读取。有关更多详细信息,请参见下面的内容。

d

将日显示为不带前导零的数字(如 1)。如果这是用户定义的数字格式中的唯一字符,请使用 %d。

dd

将日显示为带前导零的数字(如 01)。

EEE

将日显示为缩写形式(例如 Sun)。

EEEE

将日显示为全名(例如 Sunday)。

M

将月份显示为不带前导零的数字(如一月表示为 1)。如果这是用户定义的数字格式中的唯一字符,请使用 %M。

MM

将月份显示为带前导零的数字(例如 01/12/01)。

MMM

将月份显示为缩写形式(例如 Jan)。

MMMM

将月份显示为完整月份名(例如 January)。

gg

显示时代/纪元字符串(例如 A.D.)

h

使用 12 小时制将小时显示为不带前导零的数字(例如 1:15:15 PM)。如果这是用户定义的数字格式中的唯一字符,请使用 %h。

hh

使用 12 小时制将小时显示为带前导零的数字(例如 01:15:15 PM)。

H

使用 24 小时制将小时显示为不带前导零的数字(例如 1:15:15)。如果这是用户定义的数字格式中的唯一字符,请使用 %H。

HH

使用 24 小时制将小时显示为带前导零的数字(例如 01:15:15)。

m

将分钟显示为不带前导零的数字(例如 12:1:15)。如果这是用户定义的数字格式中的唯一字符,请使用 %m。

mm

将分钟显示为带前导零的数字(例如 12:01:15)。

s

将秒显示为不带前导零的数字(例如 12:15:5)。如果这是用户定义的数字格式中的唯一字符,请使用 %s。

ss

将秒显示为带前导零的数字(例如 12:15:05)。

f

显示秒的小数部分。例如,ff 将精确显示到百分之一秒,而 ffff 将精确显示到万分之一秒。用户定义格式中最多可使用七个 f 符号。如果这是用户定义的数字格式中的唯一字符,请使用 %f。

t

使用 12 小时制,并对中午之前的任一小时显示大写的 A,对中午到 11:59 P.M 之间的任一小时显示大写的 P。如果这是用户定义的数字格式中的唯一字符,请使用 %t。

tt

对于使用 12 小时制的区域设置,对中午之前任一小时显示大写的 AM,对中午到 11:59 P.M 之间的任一小时显示大写的 PM。

对于使用 24 小时制的区域设置,不显示任何字符。

y

将年份 (0-9) 显示为不带前导零的数字。如果这是用户定义的数字格式中的唯一字符,请使用 %y。

yy

以带前导零的两位数字格式显示年份(如果适用)。

yyy

以四位数字格式显示年份。

yyyy

以四位数字格式显示年份。

z

显示不带前导零的时区偏移量(如 -8)。如果这是用户定义的数字格式中的唯一字符,请使用 %z。

zz

显示带前导零的时区偏移量(例如 -08)

zzz

显示完整的时区偏移量(例如 -08:00)

格式显示

M/d/yy

12/7/58

d-MMM

7-Dec

d-MMMM-yy

7-December-58

d MMMM

7 December

MMMM yy

December 58

hh:mm tt

08:50 PM

h:mm:ss t

8:50:35 P

H:mm

20:50

H:mm:ss

20:50:35

M/d/yyyy H:mm

12/7/1958 20:50

二、NSDate日期操作总结

1 // 当前时间创建NSDate

NSDate *myDate = [NSDate date];

NSLog(@"myDate = %@",myDate);


2 //从现在开始的24小时

NSTimeInterval secondsPerDay = 24*60*60;

NSDate *tomorrow = [NSDate dateWithTimeIntervalSinceNow:secondsPerDay];

NSLog(@"myDate = %@",tomorrow);


3//根据已有日期创建日期

NSTimeInterval secondsPerDay1 = 24*60*60;

NSDate *now = [NSDate date];

NSDate *yesterDay = [now addTimeInterval:-secondsPerDay1];

NSLog(@"yesterDay = %@",yesterDay);


4//比较日期

BOOL sameDate = [now isEqualToDate:yesterDay];

NSLog(@"sameDate = %lu",sameDate);

4.1//获取较早的日期

NSDate *earlierDate = [yesterDay earlierDate:now];

NSLog(@"earlierDate  = %@",earlierDate);

4.2//较晚的日期

NSDate *laterDate = [yesterDay laterDate:now];

NSLog(@"laterDate  = %@",laterDate);

//两个日期之间相隔多少秒

NSTimeInterval secondsBetweenDates= [yesterDay timeIntervalSinceDate:now];

NSLog(@"secondsBetweenDates=  %lf",secondsBetweenDates);

//通过NSCALENDAR类来创建日期

NSDateComponents *comp = [[NSDateComponentsalloc]init];

[comp setMonth:06];

[comp setDay:01];

[comp setYear:2001];

NSCalendar *myCal = [[NSCalendaralloc]initWithCalendarIdentifier:NSGregorianCalendar];

NSDate *myDate1 = [myCal dateFromComponents:comp];

NSLog(@"myDate1 = %@",myDate1);

//从已有日期获取日期

unsigned units  = NSMonthCalendarUnit|NSDayCalendarUnit|NSYearCalendarUnit;

NSDateComponents *comp1 = [myCal components:units fromDate:now];

NSInteger month = [comp1 month];

NSInteger year = [comp1 year];

NSInteger day = [comp1 day];

//NSDateFormatter实现日期的输出

NSDateFormatter *formatter = [[NSDateFormatteralloc]init];

[formatter setDateStyle:NSDateFormatterFullStyle];//直接输出的话是机器码

//或者是手动设置样式[formatter setDateFormat:@"yyyy-mm-dd"];

NSString *string = [formatter stringFromDate:now];

NSLog(@"string = %@",string);

NSLog(@"formater = %@",formatter);


//获取日期格式对象

(NSDateFormatter *)dateFormatter {

if (dateFormatter == nil) {

dateFormatter = [[NSDateFormatter alloc] init];

[dateFormatter setDateStyle:NSDateFormatterMediumStyle];

[dateFormatter setTimeStyle:NSDateFormatterNoStyle];

}
return dateFormatter;

}
三、NSDate与NSDateFormatter的相关用法

1.NSDateFormatter配合NSDate与NSString之间的转化

NSDateFormatter有下面2个方法:

(NSString )stringFromDate:(NSDate )date;//NSDate转NSString

(NSDate )dateFromString:(NSString )string;//NSString转NSDate

e.g.

NSString *dateString=@”1900-01-01”;

NSDateFormatter *dateFormatter=[[NSDateFormatter alloc]init];

[dateFormatter setDateFormat:@”yyyy-MM-dd”];

NSDate *date=[dateFormatter dateFromString:dateString];

[dateFormatter release];

NSString转NSDate与上面类似

NSString *dateString=[dateFormatter dateFromString:[NSDate date]];



2.NSDateFormatter 的一些格式介绍

[dateFormatter setDateFormat:@”yyyy年MM月dd日#EEEE”];EEEE为星期几,EEE为周几

[dateFormatter setDateFormat:@”yyyy-MM-dd HH:mm:ss”];

[dateFormatter setDateFormat:@”yyyy年MMMMd日”];//MMMM 为xx月,一个d可以省去01日前的0



3.NSString转NSDate

用下面这种格式化方式

1)

[dateFormatter setDateFormat:@”yyyy-MM-dd HH:mm:ss”];

2)

NSCalendar *calendar = [NSCalendar currentCalendar];

NSDateComponents *components = [calendar components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:[NSDate date]];

NSDate *todayDate = [calendar dateFromComponents:components];



4.计算两个时间相差的年/月/日/时/分/秒

//假设目标的时间格式为:2016-11-18T12:15:00.000Z

NSString *yearStr = [_countdownTimeStr substringToIndex:4];

NSString *monthStr = [_countdownTimeStrsubstringWithRange:NSMakeRange(5,2)];

NSString *dayStr = [_countdownTimeStrsubstringWithRange:NSMakeRange(8, 2)];

NSString *hourStr = [_countdownTimeStrsubstringWithRange:NSMakeRange(11,2)];

NSString *minuteStr = [_countdownTimeStrsubstringWithRange:NSMakeRange(14, 2)];

NSString *secondStr = [_countdownTimeStrsubstringWithRange:NSMakeRange(17, 2)];

NSCalendar *cal = [NSCalendarcurrentCalendar];//定义一个NSCalendar对象

NSDateComponents *endDate = [[NSDateComponentsalloc] init];//初始化目标时间

[endDate setYear:[yearStr integerValue]];

[endDate setMonth:[monthStr integerValue]];

[endDate setDay:[dayStr integerValue]];

[endDate setHour:[hourStr integerValue]];

[endDate setMinute:[minuteStr integerValue]];

[endDate setSecond:[secondStr integerValue]];

//也可以使用字符串转date(NSDate *date=[dateFormatter dateFromString:dateString];)

NSDate *targetDate = [cal dateFromComponents:endDate];//把目标时间装载入date

NSDate *today = [NSDate date];//得到当前时间

//用来得到具体的时差

unsignedint unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit |NSSecondCalendarUnit;

NSDateComponents *d = [cal components:unitFlags fromDate:todaytoDate:targetDate options:0];

相差(返回类型NSInteger)

[d year] ——>年

[d month] —->月

[d day] —>日

[d hour] —>小时

[d minute] —>分钟

[d second] —>秒



5.世界标准时间UTC /GMT 转为当前系统时区对应的时间

(NSDate )getNowDateFromatAnDate:(NSDate )anyDate

{ //设置源日期时区

NSTimeZone* sourceTimeZone = [NSTimeZone timeZoneWithAbbreviation:@”UTC”

];//或GMT

//设置转换后的目标日期时区

NSTimeZone* destinationTimeZone = [NSTimeZone localTimeZone];

//得到源日期与世界标准时间的偏移量

NSInteger sourceGMTOffset = [sourceTimeZone secondsFromGMTForDate:anyDate];

//目标日期与本地时区的偏移量

NSInteger destinationGMTOffset = [destinationTimeZone secondsFromGMTForDate:anyDate];

//得到时间偏移量的差值

NSTimeInterval interval = destinationGMTOffset - sourceGMTOffset; ​//转为现在时间

NSDate* destinationDateNow = [[[NSDate alloc] initWithTimeInterval:interval sinceDate:anyDate] autorelease];

return

destinationDateNow;

}
例子演示:我的机器是北京时区东八区。

//2013-08-03T12:53:51+0800     UTC时间格式下的北京时间,可以看到北京时间= UTC + 8小时。

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];

[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZ"];

NSDate *localDate = [dateFormatter dateFromString:@"2013-08-03T04:56:52+0000"];      +0000 表示的是当前时间是个世界时间。

[dateFormatter release];

NSLog(@"now Time = %@",[self getNowDateFromatAnDate:localDate]);


结果:

2013-08-03 12:57:33.391 xxxx[2547:c07] now Time = 2013-08-03 12:56:52 +0000

以上注意一点,在转出来后带的时间是原参数anydate的时区,因此切不可再用NSDateFormatter 转换。否则会多增加一个时区的时间值。应该使用如下来提取字符串

NSString *str = [NSString stringWithFormat:@"%@",[self getNowDateFromatAnDate:localDate]];

NSLog(@"str = %@",str);


注NSDate对象存放的日期始终是UTC的标准时间,可以根据这个时间进行其它时间的转换。因此上面算出来的时间中时区为 +0000,如果此时再转为字符串

几个转换函数

//NSString 2 NSDate

(NSDate )stringToDate:(NSString )strdate

{

NSDateFormatter *dateFormatter = [[NSDateFormatteralloc] init];

[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];

NSDate *retdate = [dateFormatter dateFromString:strdate];

[dateFormatter release];

return retdate;


}
//NSDate 2 NSString

(NSString )dateToString:(NSDate )date

{

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];

[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];

NSString *strDate = [dateFormatter stringFromDate:date];

[dateFormatter release];

return strDate;


}
(NSString )getUTCFormatDate:(NSDate )localDate

{

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];

NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"];

[dateFormatter setTimeZone:timeZone];

[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss Z"];

NSString *dateString = [dateFormatter stringFromDate:localDate];

[dateFormatter release];

return dateString;


}
(NSDate )getLocalFromUTC:(NSString )utc

{

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];

NSTimeZone *timeZone = [NSTimeZone localTimeZone];

[dateFormatter setTimeZone:timeZone];

[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss Z"];

NSDate *ldate = [dateFormatter dateFromString:utc];

[dateFormatter release];

return ldate;


}
//以上注意字符串时的输入参数的格式,别外不要用%@来查看NSDate的值,因为本身存的就是UTC ,小心被误导。将日期转换成字符串来查看一下。



6.NSDateFormatter格式详细列表一览

NSDateFormatter是一个很常用的类,用于格式化NSDate对象,支持本地化的信息。与时间相关的功能还可能会用到NSDateComponents类和NSCalendar类等。

下面主要列出NSDateFormatter常见用法。

NSDate对象包含两个部分,日期(Date)和时间(Time)。格式化的时间字符串主要也是针对日期和时间的。[以下代码中开启了ARC,所以没有release。]

1)基础用法

1 NSDate* now = [NSDate date];

2 NSDateFormatter* fmt = [[NSDateFormatter alloc] init];

3 fmt.dateStyle = kCFDateFormatterShortStyle;

4 fmt.timeStyle = kCFDateFormatterShortStyle;

5 fmt.locale = [[NSLocale alloc] initWithLocaleIdentifier:@”en_US”];

6 NSString* dateString = [fmt stringFromDate:now];

7 NSLog(@”%@”, dateString);

打印输出:10/29/12, 2:27 PM

这使用的系统提供的格式化字符串,通过 fmt.dateStyle 和 fmt.timeStyle 进行的设置。实例中使用的参数是 kCFDateFormatterShortStyle,此外还有:

typedef CF_ENUM(CFIndex, CFDateFormatterStyle) { // date and time format styles

kCFDateFormatterNoStyle = 0,       // 无输出

kCFDateFormatterShortStyle = 1,    // 10/29/12, 2:27 PM

kCFDateFormatterMediumStyle = 2,   // Oct 29, 2012, 2:36:59 PM

kCFDateFormatterLongStyle = 3,     // October 29, 2012, 2:38:46 PM GMT+08:00

kCFDateFormatterFullStyle = 4      // Monday, October 29, 2012, 2:39:56 PM China Standard Time


};

2) 自定义区域语言如上实例中,我们使用的是区域语言是 en_US,指的是美国英语。如果我们换成简体中文,则代码是:

1 fmt.locale = [[NSLocale alloc] initWithLocaleIdentifier:@”zh_CN”];

则对应的输出为:

typedef CF_ENUM(CFIndex, CFDateFormatterStyle) { // date and time format styles

kCFDateFormatterNoStyle = 0,       // 无输出

kCFDateFormatterShortStyle = 1,    // 12-10-29 下午2:52

kCFDateFormatterMediumStyle = 2,   // 2012-10-29 下午2:51:43

kCFDateFormatterLongStyle = 3,     // 2012年10月29日 GMT+0800下午2时51分08秒

kCFDateFormatterFullStyle = 4      // 2012年10月29日星期一 中国标准时间下午2时46分49秒


};

世界通用的区域语言代码,详见 International Components for Unicode (ICU)

3.)自定义日期时间格式NSDateFormatter提供了自定义日期时间的方法,主要是通过设置属性 dateFormat,常见的设置如下:

1 NSDate* now = [NSDate date];

2 NSDateFormatter* fmt = [[NSDateFormatter alloc] init];

3 fmt.locale = [[NSLocale alloc] initWithLocaleIdentifier:@”zh_CN”];

4 fmt.dateFormat = @”yyyy-MM-dd’T’HH:mm:ss”;

5 NSString* dateString = [fmt stringFromDate:now];

6 NSLog(@”%@”, dateString);

打印输出:2012-10-29T16:08:40

结合设置Locale,还可以打印出本地化的字符串信息。

1 NSDate* now = [NSDate date];

2 NSDateFormatter* fmt = [[NSDateFormatter alloc] init];

3 fmt.locale = [[NSLocale alloc] initWithLocaleIdentifier:@”zh_CN”];

4 fmt.dateFormat = @”yyyy-MM-dd a HH:mm:ss EEEE”;

5 NSString* dateString = [fmt stringFromDate:now];

6 NSLog(@”\n%@”, dateString);

打印输出:2012-10-29 下午 16:25:27 星期一

注:

//快捷设置为手机当前的区域语言

NSDateFormatter *formatter = [[

NSDateFormatteralloc] init];

[formatter

setLocale:[NSLocalecurrentLocale]];

[formatter

setDateFormat

:

@”yyyy-MM-dd HH:mm:ss”];

//+ (

NSLocale

*)currentLocale;

// an object representing the user’s current locale

//+ (

NSLocale

*)systemLocale;

// the default generic root locale with little localization

4)自定义月份星期等字符NSDateFormatter中同样提供了相应的方式,去修改这些字符。一般情况下,使用相应区域语言下面的默认字符就OK了。但是你的确有这个需求,那么也是可以办到的。相应的方法非常多,如下:

Managing AM and PM Symbols

– AMSymbol

– setAMSymbol:

– PMSymbol

– setPMSymbol:

Managing Weekday Symbols

– weekdaySymbols

– setWeekdaySymbols:

– shortWeekdaySymbols

– setShortWeekdaySymbols:

– veryShortWeekdaySymbols

– setVeryShortWeekdaySymbols:

– standaloneWeekdaySymbols

– setStandaloneWeekdaySymbols:

– shortStandaloneWeekdaySymbols

– setShortStandaloneWeekdaySymbols:

– veryShortStandaloneWeekdaySymbols

– setVeryShortStandaloneWeekdaySymbols:

Managing Month Symbols

– monthSymbols

– setMonthSymbols:

– shortMonthSymbols

– setShortMonthSymbols:

– veryShortMonthSymbols

– setVeryShortMonthSymbols:

– standaloneMonthSymbols

– setStandaloneMonthSymbols:

– shortStandaloneMonthSymbols

– setShortStandaloneMonthSymbols:

– veryShortStandaloneMonthSymbols

– setVeryShortStandaloneMonthSymbols:

Managing Quarter Symbols

– quarterSymbols

– setQuarterSymbols:

– shortQuarterSymbols

– setShortQuarterSymbols:

– standaloneQuarterSymbols

– setStandaloneQuarterSymbols:

– shortStandaloneQuarterSymbols

– setShortStandaloneQuarterSymbols:

Managing Era Symbols

– eraSymbols

– setEraSymbols:

– longEraSymbols

– setLongEraSymbols:



7.NSLocale与国际化处理

本地化封装了关于语言,文化以及技术约定和规范的信息。用于提供于用户所处地域相关的定制化信息和首选项信息的设置。通过获取用户的本地化信息设置,我们可以为用户提供更加友好人性化的界面设置,包括更改应用程序的界面的语言,货币类型,数字,日期格式的格式化,提供正确的地理位置显示等等。IOS内置为应用程序的开发提供了很好的本地化机制,良好的本地化意味着应用程序可以为更多的用户提供服务。其中NSLocale类的的主要作用便是用来封装本地化相关的各种信息,下面简单列举下NSLocale的一些方法,但NSLocale更多是使用在对数字,时间日期本地化的处理的过程。

1.创建本地化对象

NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@”en_US”];

[NSLocale currentLocale]

2.获取系统本地化信息

[NSLocale availableLocaleIdentifiers] ;

[NSLocale ISOCountryCodes] ;

[NSLocale ISOCurrencyCodes] ;

[NSLocale ISOLanguageCodes] ;

3.获取当前系统设置语言的标识符

[[NSLocale currentLocale] localeIdentifier];

[[NSLocale currentLocale] objectForKey:NSLocaleIdentifier];

4.获取本地化对象的具体内容

NSLocale *local = [NSLocale currentLocale];

[local objectForKey:NSLocaleIdentifier];

[local objectForKey: NSLocaleLanguageCode];

key值参见NSLocale Calendar Keys

5.获取当前语言的排版方向和字符方向

[NSLocale lineDirectionForLanguage:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode];

[NSLocale characterDirectionForLanguage:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode] ;

6.获取用户的语言偏好设置列表,该列表对应于IOS中Setting>General>Language弹出的面板中的语言列表。

[NSLocale preferredLanguages]

第一个元素即为当前用户设置的语言

7.监听用户本地化设置的消息

[[NSNotificationCenter defaultCenter] addObserver:self

selector:@selector(localChangedHandler:)

name:NSCurrentLocaleDidChangeNotification object:nil];

8.以本地化方式获取国际化信息的显示名称

NSLocale *curLocal = [[NSLocale alloc]initWithLocaleIdentifier:@”zh-Hans”] ;

NSLog(@”%@”,[curLocal displayNameForKey:NSLocaleIdentifier value:@”fr_FR”] );

curLocal = [[NSLocale alloc]initWithLocaleIdentifier:@”zh-Hant”] ;

NSLog(@”%@”,[curLocal displayNameForKey:NSLocaleIdentifier value:@”fr_FR”] );



若你只开发中国区的应用,需要保证用户修改当前语言环境时应用的显示不发生变化。而像NSDateFormatter这样的类,会根据设备的设置,自动返回不同语言的数据。为了保证返回数据的语言一致,我们需要设置NSLocale。

1

2 NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@”zh”];

3 NSDateFormatter *secondDateFormatter = [[NSDateFormatter alloc] init];

4 [secondDateFormatter setDateFormat:@”cccc”];

5 secondDateFormatter.locale = locale;

6 NSDate *date = [NSDate date];

7 NSLog(@”%@”, [secondDateFormatter stringFromDate:date]);

当然,像上面的需求很罕见。

作为大家都不常用的一个类,NSLocale类是将与国家和语言相关的信息进行简单的组合,包括货币,文学方面的信息。

货币:货币的国际名称(人民币的国际货币名称是CNY);货币符号(人民币的国际货币符号是¥)

文学:标点符号,文字的书写顺序(左右顺序),引用的起止符号等等

若做金融一类的应用可能会用到NSLocale这个类。

这个类稍微了解即可。

- 请描述一下Instruments及其作用。

有一个instruments的记录笔记,请注意查看

关于iOS技术

- 请解释一下Handoff是什么,并简述它是如何实现iOS、Mac/网页应用互通的。

苹果iOS 8系统为我们带来了一个新功能——Handoff,该功能可以让 iPhone、iPad 和 Mac 电脑协同工作,即使运行的不是同一操作系统,苹果也可以借此巧妙地连接了用户的多个设备。当然,使用该功能之前,你的设备必须登录到同一个Apple ID,同时需要都处在蓝牙的可侦测范围内。满足上述条件后,Handoff 便自动就绪并默默工作,等待你随时使用它。

  也许,还有一些用户对于这项新功能存在疑问,比如同步传输数据的Handoff安全吗,我们就来通过以下5句话来了解一下。

  1.你的 Handoff 只属于你,Apple ID 是唯一身份标识

  你的 Apple ID(也同时是你的 iCloud 和 iTunes 账户)被用来确认身份。你必须在需要使用 Handoff 的设备上登录同一个 Apple ID,这样苹果就可以知道哪些设备是你的。这样一来,只有你的硬件才能有权限访问你的数据。

  你的 Handoff 只属于你,这也意味着,即使你身边的人也使用苹果设备工作,甚至他们也使用 Handoff,你也永远不必担心你的数据会和他们的数据混在一起。

  2. Handoff 只在你的手够得着的距离生效

  当两个或更多个 Apple 设备互相接近在一定的范围内时,设备之间将会建立了一个低功耗蓝牙连接供 Handoff 使用。这一个距离限制能够非常有效地保护你的隐私:因为这一距离很近,近到基本是你肢体能够够得着的地方,这同时也方便了你迅速切换设备工作。

  这样一来,你根本不必担心发生以下情况:当你在家使用 iPad 浏览网页时,公司里的 Mac 电脑上也会弹出相应内容;或当你在星巴克喝咖啡小憩使用 Mac 电脑时,你家里的 iPad 上不停弹出相应的内容。

  3. 可靠的加密密钥,保证 Handoff 安全

  当设备之间建立起 Handoff 连接时,它使用苹果推送通知(APN)服务。这意味着,Handoff 使用着相当安全的加密服务,就像 iMessage 那样,Handoff 将会为每一个使用 Handoff 的设备分配一个对称的 256 位 AES 密钥,这个密钥直接存储在每个设备的钥匙串中。苹果公司声称,这一方法极其安全,可以防止外界的恶意攻击。

  一旦设备之间连接就绪,Handoff 将使得周围的设备「知晓」源设备上正在进行的活动。举例来说,如果你正在使用 Safari 浏览网页,Handoff 将让其它设备知道你正在使用 Safari 浏览器;如果你使用 Keynote 编辑幻灯片,Handoff 将让这些设备知道你正在使用 Keynote 软件。

  一般说来,低功耗蓝牙的通信都使用了上述的加密方式来传输数据,而且 APN 也是使用同一种类型的加密类型。

  4. 你让它工作,Handoff 才会开始工作

  Handoff 在准备就绪后基本上不怎么耗费数据传输——它不会在你的设备间时刻推送你的源设备活动数据,它只是让其它设备都「知晓」你的设备活动可以在它们之上继续。但是 Handoff 的一切,只有你选择开始,它才开始。

  在你的 iOS 设备的锁屏上,向上滑动 Handoff 图标,或者调出应用程序切换器后滑动到最左边点击对应的 Handoff 卡片;在 Mac 电脑上,你可以点击 Dock 栏中的 Handoff 图标,或使用 Command-Tab 调出应用程序切换器来选择对应的 Handoff 图标。

  瞧,Handoff 为你主导,在任何情况下,只有当你需要切换设备了,它才开始全速工作。

  5. Handoff 的数据传输全程加密

  一旦触发 Handoff,Handoff 将使用低功耗蓝牙或 APN 加密方式处理这些数据的传输。这个数据可以是你正在浏览的网页地址,可以是你正在编辑的幻灯片网页,可以是你正在编辑的 iCloud 文档。

  Handoff 还可以在网页端和应用程序之间传输信息,但在建立连接之前,应用程序必须证明它自己控制着该网站的域名。换句话说,Facebook 的应用程序必须证明它控制着 Facebook.com 域名。

  那么如果需要传输大量数据呢?例如你在编写的邮件中附着一个很大的附件,Handoff 首先使用低功耗蓝牙配对,然后走点对点的 Wi-Fi 通道(类似于 AirDrop),这个过程也是全程加密的。

  结语

  如果你不想使用Handoff,不管是在 iOS 设备上还是在 Mac 电脑上,你都可以很容易地禁用它。否则,Handoff 将在满足条件下尽一切所能希望在多设备之间同步你的操作。再次说明,设备的距离限制控制着你的隐私,加密的传输过程保护着你的数据安全,Handoff 相当可靠。

- iCloud包含了哪些技术与服务?

iCloud

iCloud是苹果提供的云端服务,用户可以将通讯录、备忘录、邮件、照片、音乐、视频等备份到云服务器并在各个苹果设备间直接进行共享而无需关心数据同步问题,甚至即使你的设备丢失后在一台新的设备上也可以通过Apple ID登录同步。当然这些内容都是iOS内置的功能,那么对于开放者如何利用iCloud呢?苹果已经将云端存储功能开放给开发者,利用iCloud开发者可以存储两类数据:用户文档和应用数据、应用配置项。前者主要用于一些用户文档、文件的存储,后者更类似于日常开放中的偏好设置,只是这些配置信息会同步到云端。

要进行iCloud开发同样需要一些准备工作(下面的准备工作主要是针对真机的,模拟器省略Provisioning Profile配置过程):

1、2步骤仍然是创建App ID启用iCloud服务、生成对应的配置(Provisioning Profile),这个过程中Bundle ID可以使用通配符(Data Protection、iCloud、Inter-App Audio、Passbook服务在创建App ID时其中的Bundle ID是可以使用通配ID的)。

3.在Xcode中创建项目(假设项目名称为“kctest”)并在项目的Capabilities中找到iCloud并打开。这里需要注意的就是由于在此应用中要演示文档存储和首选项存储,因此在Service中勾选“Key-value storae”和“iCloud Documents”:

- iOS扩展是指?能否列举一些热门或常见的范例?

一、扩展概述

扩展(Extension)是iOS 8中引入的一个非常重要的新特性。扩展让app之间的数据交互成为可能。用户可以在app中使用其他应用提供的功能,而无需离开当前的应用。

在iOS 8系统之前,每一个app在物理上都是彼此独立的,app之间不能互访彼此的私有数据。

而在引入扩展之后,其他app可以与扩展进行数据交换。基于安全和性能的考虑,每一个扩展运行在一个单独的进程中,它拥有自己的bundle, bundle后缀名是.appex。扩展bundle必须包含在一个普通应用的bundle的内部。

iOS 8系统有6个支持扩展的系统区域,分别是Today、Share、Action、Photo Editing、Storage Provider、Custom keyboard。支持扩展的系统区域也被称为扩展点。

Today Widget

对于赛事比分,股票、天气、快递这类需要实时获取的信息,可以在通知中心的Today视图中创建一个Today扩展实现。Today扩展又称为Widget。

Share

在iOS 8之前,用户只有Facebook,Twitter等有限的几个分享选项可以选择。如果希望将内容分享到Pinterest,开发者则需要一些额外的努力。在iOS 8中,开发者可以创建自定义的分享选项。

Action

action在所有支持的扩展点中扩展性最强的一个。它可以实现转换另一个app上下文中的内容。苹果在WWDC大会上演示了一个Bing翻译动作扩展,它可以将在Safari中选中的文本翻译成不同的语言。

Photo Editing

在iOS 8之前,如果你想为你的照片添加一个特殊的滤镜,你需要进入第三方app中,这个过程是相当繁琐的。在iOS 8中,你可以直接在Photos中使用第三方app,如Instagram,VSCO cam、Aviary提供的Photo Editing扩展完成对图片的编辑,而无需离开当前的app。

Storage Provider

Storage Provider让跨多个文件存储服务之间的管理变得更简单。类似Dropbox、Google Drive等存储提供商通过在iOS 8中提供一个Storage Provider扩展,app直接可以使用这些扩展检索和存储文件而不再需要创建不必要的拷贝。

Custom Keyboard

苹果公司在2007年率先推出了触摸屏键盘,但一直没多大改进。在这一方面,Android则将键盘权限开放给了第三方开发者,所以出现了许多像Swype,SwiftKey等优秀的键盘输入法。在iOS 8中,苹果终于将键盘权限开发给了第三方开发者,自定义键盘输入法可以让用户在整个系统范围内使用。

二、创建扩展与发布扩展

在创建扩展之前,你需要创建一个用来包含扩展的常规的app项目。这个包含扩展的app被称为containing app。在创建好containg app之后,选择File->New->Target菜单,从弹出的对话框中选择一个适当的扩展目标模板。每一个扩展目标模板都包含了与扩展点相关的文件和设置。一个containing app可以包含多个不同类型的扩展。

每一个扩展目标模板包含一个头文件和实现文件,一个Info.plist文件,以及一个storyboard文件。Info.plist文件包含了对扩展的配置信息,其中最重要的键是NSExtension。下面列出了一个NSExtension可能包含的常用键值对。

NSExtension

NSExtensionAttributes

NSExtensionActivationRule

NSExtensionActivationSupportsImageWithMaxCount

10

NSExtensionActivationSupportsMovieWithMaxCount

1

NSExtensionJavaScriptPreprocessingFile

MyJavaScriptFile

NSExtensionPointVersion

1.0

NSExtensionMainStoryboard

MainInterface

NSExtensionPointIdentifier

com.apple.ui-services

NSExtensionPrincipalClass

ActionViewController

1) NSExtensionActivationRule定义了当前的扩展支持的数据类型及数据项个数,例如当前的设置只支持图片格式和视频格式的数据,并且最多不超过10张图片和1个视频。

2) NSExtensionJavaScriptPreprocessingFile用于配置与脚本交互的JS脚本文件的名字。

3) NSExtensionMainStoryboard配置扩展的Storyboard文件名。

4) NSExtensionPointIdentifier用于表示扩展点,每一个扩展点拥有一个唯一的名字。

5) NSExtensionPrincipalClass配置当扩展启动时,扩展点首先要实例化的类

为了将扩展提交苹果商店,你需要提交你的containg app。并且需要注意,除了扩展必须包含功能以外,同时containg app还需要提供一些功能,而针对OS X平台的扩展则无此限制。当用户安装了你的containg app,containg app中包含的扩展也会一同被安装。

三、理解扩展如何运作

在安装扩展之后,扩展并不会自动运行,用户必须执行特定的操作来启用扩展。如果是Today扩展,用户可以在通知中心的Today视图中编辑启用扩展。如果是自定义键盘扩展,用户需要在系统设置的通用选项下的键盘选项中启用自定义键盘扩展。而如果是Share扩展,用户只需点击系统提供的分享按钮,即可在分享列表中找到分享扩展。

一个扩展并不是一个app,它的生命周期和运行环境不同于普通app。在生命周期方面,扩展的生命周期从用户在另一个app中选择了扩展开始,一直到扩展完成了用户的请求生命周期结束。在运行环境方面,扩展的限制要比普通app更严格,扩展的可用内存上限以及可用的API都比普通app要少。严格限制扩展的内存是因为在同一时间可能会有多个扩展同时运行,如Widget扩展。如果API声明包含NS_EXTENSION_UNAVAILABLE宏,则此API在扩展中将不可用,常见的API如:

+ (UIApplication *)sharedApplication NS_EXTENSION_UNAVAILABLE_IOS(“Use view controller based solutions where appropriate instead.”);

调用扩展的应用称为host app,对于Widget扩展,host app就是Today。host app会在扩展的有效生命周期内定义一个扩展上下文。通过扩展上下文,host app可以和扩展互传数据。注意,扩展只和host app直接通信,扩展与containg app以及containing app与host app之间不存在通信关系,如果扩展需要打开containg app,则通过自定义URL scheme方式实现,而不是直接向containg app发送消息。三者的关系见下图:

扩展是一个单独的个体。扩展拥有独立的target,独立的bundle文件,独立的运行进程,独立的地址空间。这意味着即使你的containing app不在运行,系统也可以启动扩展。或者你的containing app处于挂起状态,同样不会影响扩展的运行。所以系统可以单独对扩展执行优化。扩展与containg app的关系:

四、设计扩展过程中常见的几个问题

1. containg app与扩展如何通过扩展上下文互传数据

在iOS 8中,UIViewController新增了一个扩展上下文属性extensionContext。来处理containing app与扩展之间的通信,上下文的类型是NSExtensionContext。假设你现在需要在host app中将一张图片传递给扩展做滤镜处理,host app中的代码如下:

UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[[self.imageView image]] applicationActivities:nil];

[self presentViewController:activityViewController animated:YES completion:nil];

当用户在弹出的Action列表中选择了扩展,扩展将被启动,然后在扩展的viewDidLoad方法中,通过extensionContext检索host app传回的数据项。扩展中的代码如下:

- (void)viewDidLoad {

[super viewDidLoad];

NSExtensionItem *imageItem = [self.extensionContext.inputItems firstObject];

if(!imageItem){

return;

}
NSItemProvider *imageItemProvider = [[imageItem attachments] firstObject];

if(!imageItemProvider){

return;

}
// 检查是否包含文本

if([imageItemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage]){

[imageItemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeImage options:nil completionHandler:^(UIImage *image, NSError *error) {

if(image){

dispatch_async(dispatch_get_main_queue(), ^{

self.imageView.image = image;

});

}
}];

}


}
上述代码中,extensionContext表示一个扩展到host app的连接。通过extionContent,你可以访问一个NSExtensionItem的数组,每一个NSExtensionItem项表示从host app传回的一个逻辑数据单元。你可以从NSExtensionItem项的attachments属性中获得附件数据,如音频,视频,图片等。每一个附件用NSItemProvider实例表示。上述代码中NSItemProvider的loadItemForTypeIdentifier实例方法的第一个参数是(NSString )kUTTypeImage,如果你需要处理的是文本类型,参数则为(NSString )kUTTypeText,相应的处理代码则变成:

if([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeText]){

[imageItemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeText options:nil completionHandler:^(NSAttributedString *string, NSError *error) {

if (string) {

// 在这里处理文本

}
}];

}
当扩展处理完host app传回的图片数据后,它需要将处理好的数据再传给host app。在扩展中的代码如下:

-(IBAction)done:(id)sender{

NSExtensionItem* extensionItem = [[NSExtensionItem alloc] init];

[extensionItem setAttachments:@[[[NSItemProvider alloc] initWithItem:[self.imageView image] typeIdentifier:(NSString*)kUTTypeImage]]];

[self.extensionContext completeRequestReturningItems:@[extensionItem] completionHandler:nil];


}
最后一步是host app接收来自扩展传回的数据,在host app中的代码如下:

[activityViewController setCompletionWithItemsHandler:^(NSString activityType, BOOL completed, NSArray *returnedItems, NSError error){

if([returnedItems count] > 0){

NSExtensionItem* extensionItem = [returnedItems firstObject];

NSItemProvider* imageItemProvider = [[extensionItem attachments] firstObject];

if([imageItemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage]){

[imageItemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeImage options:nil completionHandler:^(UIImage *item, NSError *error) {

if(item && !error){

dispatch_async(dispatch_get_main_queue(), ^{

[self.imageView setImage:item];

});

}
}];

        }}}];


上述代码主要是通过设置一个completionBlock处理数据回调。

注意,所有的扩展都是一个UIViewController。所以UIViewController的所有生命周期方法,如viewWillAppear:、viewWillDisappear:等在扩展中都是可以使用的。

2. 如何在扩展中打开containing app

在一般情况下,扩展和containing app不存在通信关系。但是有时候需要在扩展中打开containing app,如iOS 7中预置的日历Widget。在常规的app中,可以使用如下代码在A app中打开B app:

if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:customURL]]) {

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:customURL]];

}
但是之前有讲到,sharedApplication API在扩展中被禁止使用,所以为了实现同样的功能,NSExtensionContext定义了一个新的方法用来打开containing app:

- (void)openURL:(NSURL *)URL completionHandler:(void (^)(BOOL success))completionHandler;

在调用此方法之前,需要在containg app中定义一个自定义URL Scheme。定义方法可参见链接,最终的结果如下图:

在扩展中打开containing app的代码如下:

- (IBAction)openContainingApp:(id)sender {

NSURL *url = [NSURL URLWithString:@”ExtensionDemo://”];

[self.extensionContext openURL:url completionHandler:^(BOOL success) {

}];

}
3. 如何实现containing app与扩展共享数据

扩展和containing app各自拥有自己的数据容器,虽然扩展内嵌在containing app的内部,但是它们并不可以互访彼此的数据。为了实现containing app与扩展的数据共享,苹果在iOS 8中引入了一个新的概念——App Group。为了开启App Group,找到你的containing app目标,在右侧找到Capabilities标签,定位到App Groups分组,如下图所示。

然后选择你需要共享数据的扩展目标,重复执行一次操作,注意两次的App Group名要相同,不要添加新的条目。当开启App Group后,你可以使用NSUserDefaults方法访问共享区域,如下述代码,注意不是[NSUserDefaults standardUserDefaults]:

_sharedUserDefault= [[NSUserDefaults alloc] initWithSuiteName:@”group.com.aegeaon.ExtensionDemo”];

你也可以使用NSFileManager的containerURLForSecurityApplicationGroupIdentifier方法访问共享数据区:

- (BOOL)saveTextByNSFileManager {

NSError *err = nil;

NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@”group.wangzz”];

containerURL = [containerURL URLByAppendingPathComponent:@”Library/Caches/good”];

NSString *value = _textField.text;
BOOL result = [value writeToURL:containerURL atomically:YES encoding:NSUTF8StringEncoding error:&err];
if (!result) {
NSLog(@"%@",err);
} else {
NSLog(@"save value:%@ success.",value);
}
return result;


}
App Group区域在containing app与扩展之间所处的关系图:

你可能注意到了,在Xcode 6中iPhone模拟器的位置已经发生了变化。与此同时,在iOS 8 release Note中有提到,app的沙盒结构已经发生了改变,现在它被划分成了三个容器,Bundle容器、Data容器、iCloud容器。iOS 8 app沙盒目录结构如下图:

为了具体了解沙盒目录的布局,使用如下代码分别在containing app和扩展中打印出App Group目录,app bundle目录,以及Document目录:

- (void)logAppPath

{

//app group路径

NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@”group.com.aegeaon.ExtensionDemo”];

NSLog(@”app group:\n%@”,containerURL.path);

//打印可执行文件路径
NSLog(@"bundle:\n%@",[[NSBundle mainBundle] bundlePath]);

//打印documents
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
NSLog(@"documents:\n%@",path);


}
在containing app中执行logAppPath方法的结果如下:

app group:

/Users/aegaeon/Library/Developer/CoreSimulator/Devices/72CA5D31-9509-4076-BC94-BF4D29DC0151/data/Containers/Shared/AppGroup/5B4CFBD8-D95D-4F01-9268-D9F79792147D

bundle:

/Users/aegaeon/Library/Developer/CoreSimulator/Devices/72CA5D31-9509-4076-BC94-BF4D29DC0151/data/Containers/Bundle/Application/EED1F771-A8AD-4A97-97F3-2B0A57936C17/ExtensionDemo.app

documents:

/Users/aegaeon/Library/Developer/CoreSimulator/Devices/72CA5D31-9509-4076-BC94-BF4D29DC0151/data/Containers/Data/Application/95DBF43A-8B4B-426C-9A3A-C1745FCB3FA2/Documents

在扩展中执行logAppPath方法的结果如下:

app group:

/Users/aegaeon/Library/Developer/CoreSimulator/Devices/72CA5D31-9509-4076-BC94-BF4D29DC0151/data/Containers/Shared/AppGroup/5B4CFBD8-D95D-4F01-9268-D9F79792147D

bundle:

/Users/aegaeon/Library/Developer/CoreSimulator/Devices/72CA5D31-9509-4076-BC94-BF4D29DC0151/data/Containers/Bundle/Application/EED1F771-A8AD-4A97-97F3-2B0A57936C17/ExtensionDemo.app/PlugIns/ExpressExt.appex

documents:

~/Documents

其中标注为红色的意思是每次运行目录名都会发生变化。标注为绿色的表示文件名不会变化的,标准为橘色也验证了iOS 8中沙盒目录被划分的说法。其中也可以看出扩展扩展名为appex,它包含在containing app的PlugIns目录内。下图展示了扩展目录在Finder中的结构:

如何让扩展访问到网页内容

在WWDC上,苹果演示了在Safari for iOS中使用Bing Action扩展将当前页面翻译为其他语言。考虑一下,为了完成这个功能,扩展和浏览器之间一定要建立一个连接,浏览器负责将选中的文本发给扩展,扩展将翻译的结果发回浏览器。为了实现这个机制,这里需要借助一个Javascript脚本,使用JS脚本可以访问网页的DOM。脚本的内容很简单,只包含两个方法,脚本文件名为MyJavaScriptFile.js。代码如下:

var MyExtensionJavaScriptClass = function() {};

MyExtensionJavaScriptClass.prototype = {

run: function(arguments) {

arguments.completionFunction({“baseURI”: document.baseURI});

},

finalize: function(arguments) {
var newContent = arguments["content"];
document.write(newContent);
}


};

var ExtensionPreprocessingJS = new MyExtensionJavaScriptClass;

其中包含一个run()和finalize()方法。当Safari一加载好你的JS文件,就会立即调用run方法,当你在扩展中调用了completeRequestReturningItems:expirationHandler:completion:方法,Safari会调用finalize()方法。在run()方法中,Safari提供了一个arguments参数,使用它可以利用键值对的形式将数据传给扩展。在上述代码中,传给扩展的键值对是:@{@”baseURI” : document.baseURI}。在finalize()方法中,当你调用了completeRequestReturningItems:expirationHandler:completion:方法,方法第一个参数的值会传给finalize()方法的arguments形参。在上述代码中,finalize()接收到参数后,将内容写入了当前的文档。

为了Safari能够调用正确调用到JS文件,需要在扩展的Info.plist文件中添加如下配置:

NSExtensionAttributes

NSExtensionJavaScriptPreprocessingFile

MyJavaScriptFile

在你的扩展中,为了取得从JS脚本传回的键值对,你需要为NSItemProvider的方法loadItemForTypeIdentifier:options:completionHandler:指定kUTTypePropertyList数据类型,在取得返回的键值字典后,使用NSExtensionJavaScriptPreprocessingResultsKey键取值,代码如下:

NSExtensionContext *context = self.extensionContext;

NSExtensionItem *item = context.inputItems.firstObject;

NSItemProvider *provider = item.attachments.firstObject;

[provider loadItemForTypeIdentifier:(NSString *)kUTTypePropertyList
options:nil
completionHandler:^(id<NSSecureCoding> item, NSError *error) {
NSDictionary *results = (NSDictionary *)item;
NSString *baseURI = [[results objectForKey:NSExtensionJavaScriptPreprocessingResultsKey] objectForKey:@"baseURI"];
NSLog(@"%@", baseURI);
}];


为了在扩展中将处理后的结果传给脚本,你需要使用NSItemProvider的initWithItem:typeIdentifier:包装键值对。代码如下:

NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init];

extensionItem.attachments = @[[[NSItemProvider alloc] initWithItem: @{NSExtensionJavaScriptFinalizeArgumentKey: @{@”content”:@”Hello World”}} typeIdentifier:(NSString *)kUTTypePropertyList]];

[[self extensionContext] completeRequestReturningItems:@[extensionItem] expirationHandler:nil completion:nil];

5. 如何在containing app与扩展之间共享代码

iOS 8中,你可以内嵌一个framework文件在扩展和containing app之间共享代码。假设你希望在你的containing app与扩展之间共享图片处理的代码,此时你可以将代码打包成framework文件,内嵌到两个目标中。对于内嵌框架中的代码,确保不包含扩展不允许使用的API。

如何将代码打包成framework文件这里就不敖述了,感兴趣的同学可以参见:http://blog.sina.com.cn/s/blog_407fb5bc01013v6s.html。当你创建好.framework文件后,你可以直接将.framework文件同时拖入containing app和扩展中,如下图所示:

这里使用公司ILSLib目录下的的MagicalRecord21.framework文件作为素材,讲解如何在containing app和自定义键盘扩展之间实现共享Core Data数据库。在你的扩展和containing app中中配置好引用头文件。分别在containing app的AppDelegate文件的application: didFinishLaunchingWithOptions: launchOptions与自定义键盘扩展的UIInputViewController子类文件中viewDidLoad方法中添加如下代码:

[MagicalRecord setupCoreDataStackWithStoreNamed:@”demo.sqlite”];

上述代码分别对containing app和扩展执行Core Data栈初始化,其中包括数据模型、sqlite存储文件等配置。运行containing app,此时AppDelegate中的数据库配置代码会被执行,接着打开系统设置中的通用选项下的键盘选项,在这里启用自定义键盘。然后回到containing app,切换到自定义键盘扩展,此时自定义键盘扩展中viewDidLoad方法中的数据库配置代码执行,但是控制台出现错误提示:

CoreData: error: -addPersistentStoreWithType:SQLite configuration:(null) URL:~/Library/Application%20Support/CustomKeyboardExt/demo.sqlite – file:/// options:(null) … returned error Error Domain=NSCocoaErrorDomain Code=512 “The operation couldn’t be completed. (Cocoa error 512.)” UserInfo=0x7b48a720 {reason=Failed to create file; code = 2} with userInfo dictionary {

reason = “Failed to create file; code = 2”;

}
上述错误表示在扩展的~/Library/Application%20Support/CustomKeyboardExt/demo.sqlite目录创建.sqlite文件失败。翻阅MagicalRecord源代码(需要从github重新下载源代码,.framework看不到源代码),其中在创建.sqlite存储文件路径的代码中会发现:

+ (NSURL ) MR_urlForStoreName:(NSString )storeFileName {

NSArray *paths = [NSArray arrayWithObjects:[self MR_applicationDocumentsDirectory], [self MR_applicationStorageDirectory], nil];

NSFileManager *fm = [[NSFileManager alloc] init];

for (NSString *path in paths) {

NSString *filepath = [path stringByAppendingPathComponent:storeFileName];

if ([fm fileExistsAtPath:filepath]) {

return [NSURL fileURLWithPath:filepath];

}
}
//set default url
return [NSURL fileURLWithPath:[[self MR_applicationStorageDirectory] stringByAppendingPathComponent:storeFileName]];


}
其中MR_applicationStorageDirectory方法返回的是Application Support目录,而这个目录是处在Library目录内的。上文中已经讲过,扩展没有Documents目录,同样也是没有Library目录。所以文件创建会发生失败。为了实现扩展与containing app之间共享.sqlite文件,这里需要将.sqlite文件创建在App Group区域。问题是MagicalRecord21.framework文件只暴露了头文件,无法对其源文件中的MR_urlForStoreName:方法做修改。这里使用Objective-C的动态运行时技术——Method Swizzling,在运行时将MR_urlForStoreName:方法的实现使用新的实现进行替换。 (注:这里可以直接给setupCoreDataStackWithStoreNamed方法传递一个包含文件路径的URL类型参数实现修改.sqlite文件的存放位置,methodSwizzling只是另一种通用处理方法)

首先需要为自定义键盘扩展创建一个Category文件NSPersistentStore+Tracking.h/m,.m文件中的完整的代码如下:

import “NSPersistentStore+Tracking.h”

import

import

pragma mark - Method Swizzling

(NSURL ) ILS_urlForStoreName:(NSString )storeFileName {

NSURL *storeURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:kGroupName];

storeURL = [storeURL URLByAppendingPathComponent:[kContainingDirectory stringByAppendingString:storeFileName]];

return storeURL;

}
@end

在当前的代码一载入内存,load方法将被执行,它比AppDelegate的application: didFinishLaunchingWithOptions: launchOptions方法要先被执行,上述代码会将MR_urlForStoreName:的实现替换成ILS_urlForStoreName:,在ILS_urlForStoreName:方法中,使用NSFileManager的containerURLForSecurityApplicationGroupIdentifier方法设定App Group,最终的.sqlite文件将保存在App Group目录内的CoreDataStore目录下。同样需要为containing app中使用此方法,可以直接将NSPersistentStore+Tracking.h/m拖入containing app目标内。再次运行自定义键盘扩展,数据库文件已成功保存到App Group中。如下图:

同时被共享的代码框架MagicalRecord21.framework被containg app和扩展共享,双方共用一个框架文件,如下图:

如何在扩展中处理长时间任务

用户希望在扩展完成他们的任务之后能够立即返回到host app中。但是如果扩展执行的任务是一个长时间任务,比如下载。在这种情况下,需要使用NSURLSession来创建一个下载session,并初始化一个后台下载任务。当扩展初始化了上传下载任务后,就算是完成了host app的请求,扩展就可以被终止。这不会影响到任务的结果。如果当后台任务完成后,你的扩展不在运行,系统将在后台启动你的contaiing app并调用appdelegate的aplication:handleEventsForBackgroundURLSession:completionHandler:方法。为了在扩展中初始化一个后台的NSURLSession任务,你必须设置一个containing app和扩展都可以访问的共享容器。

相关代码如下:

NSURLSession *mySession = [self configureMySession];

NSURL *url = [NSURL URLWithString:@”http://www.example.com/LargeFile.zip“];

NSURLSessionTask *myTask = [mySession downloadTaskWithURL:url];

[myTask resume];

(NSURLSession *) configureMySession {

if (!mySession) {

NSURLSessionConfiguration* config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@“com.mycompany.myapp.backgroundsession”];

config.sharedContainerIdentifier = @”com.mycompany.myappgroupidentifier”;

mySession = [NSURLSession sessionWithConfiguration:config delegate:selfdelegateQueue:nil];

}
return mySession;

}

- HealthKit是什么?

- Apple Pay是什么?能否描述一下如何在应用中使用Apple Pay?

- 请解释一下iOS应用沙盒机制。

iOS沙盒机制,文件读取,归档与反归档

1.IOS中的沙盒机制

IOS中的沙盒机制是一种安全体系,它规定了应用程序只能在为该应用创建的文件夹内读取文件,不可以访问其他地方的内容。所有的非代码文件都保存在这个地方,比如图片、声音、属性列表和文本文件等。

特点:

1.每个应用程序都在自己的沙盒内

2.不能随意跨越自己的沙盒去访问别的应用程序沙盒的内容

3.应用程序向外请求或接收数据都需要经过权限认证

每个沙盒含有3个文件夹:Documents, Library 和 tmp。Library包含Caches、Preferences目录。如下图:

Documents:苹果建议将程序创建产生的文件以及应用浏览产生的文件数据保存在该目录下,iTunes备份和恢复的时候会包括此目录

Library:存储程序的默认设置或其它状态信息;

Caches:存放缓存文件,保存应用的持久化数据,用于应用升级或者应用关闭后的数据保存,不会被itunes同步,所以为了减少同步的时间,可以考虑将一些比较大的文件而又不需要备份的文件放到这个目录下。

Preferences:存储偏好设置,比如:应用程序是否是第一次启动,保存用户名和密码等

tmp:提供一个即时创建临时文件的地方,但不需要持久化,在应用关闭后,该目录下的数据将删除,也可能系统在程序不运行的时候清除。

2.获取上述文件夹的路径

①获取沙盒的路径

NSLog(@"%@",NSHomeDirectory());


② 获取Documents目录路径:

/*返回值是数组的原因:

该方法一开始用于mac -os开发,对于PC端来说,可以有多个用户,所以获取时,会获取到所有用户的文件夹路径,

但是该方法用于ios开发时,因为移动端只有一个用户,所以获取到的路径也只有一个. */

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);

NSString *documentsDirectory = [paths firstObject];

③ 获取Library目录路径:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask, YES);
NSString *libraryDirectory = [paths objectAtIndex:0];


④ 获取Cache目录路径:

NSArray *cacPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,

NSUserDomainMask, YES);

NSString *cachePath = [cacPath objectAtIndex:0];

⑤ 获取Tmp目录路径:

NSString *tmpDirectory = NSTemporaryDirectory();

⑥创建文件和文件

//创建文件夹
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsDirectory = [paths firstObject];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *testDirectory = [documentsDirectory stringByAppendingPathComponent:@"test"];
// 判断是否创建文件成功
BOOL res=[fileManager createDirectoryAtPath:testDirectory withIntermediateDirectories:YES attributes:nil error:nil];
//<span style="font-family:georgia,verdana,Arial,helvetica,sans-seriff;">创建文件</span>
NSString *testPath = [testDirectory stringByAppendingPathComponent:@"test.txt"];
BOOL res=[content writeToFile:testPath atomically:YES encoding:


NSUTF8StringEncoding error:nil];

⑦删除文件

NSFileManager *defaultManager = [NSFileManager defaultManager];
if ([defaultManager isDeletableFileAtPath:filePath]) {
[defaultManager removeItemAtPath:filePath error:nil];
}//文件是否存在
NSLog(%@, [fileManager isExecutableFileAtPath:filePath]?@"YES":@"NO");


⑧.NSUserDefaults

//</span>程序运行时,判断用户是否登录
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSString *str = [userDefaults objectForKey:@"username"];


⑨NSBundle

// NSBundle表示 app 中代码和资源的文件在文件系统里所在的位置,app编译后,资源文件直接就复制到了该目录下

//获得程序所在路径

NSString *boundlePath = [[NSBundle mainBundle] bundlePath];

NSLog(@”%@”,boundlePath);

//根据文件名和文件类型找到文件的绝对路径

NSString *file = [[NSBundle mainBundle] pathForResource:name ofType:nil];

3.文件写入和读取操作

文件读取支持类型,(字符串,数组,字典,二进制数据NAData)

1.写入文件

writeToFile:atomically: — 数组,字典,二进制数据,

writeToFile:atomically:encoding:error:—字符串

2.从文件读取数据

[类名 + 类名WithContentsOfFile:]

[NSArray + arrayWithContentsOfFile:]

3.对于数组,字典大容器来说,想要实现文件读写,必须保证容器中的元素必须是字符串,数组,字典,

二进制数据类型之一.

①获取文件路径

(NSString *)filePath {

//1.获取Documents文件夹路径

//如要传入的3个参数(1)查找的文件夹的路径 (2)在那个范围查找 (3)是否显示详细路径

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask , YES);

NSString *documentsDirectory = [paths firstObject];

//2.拼接上文件路径

return [documentsDirectory stringByAppendingPathComponent:@”myDocument.txt”];

}
②字符串的写入和读取操作.

//写入操作

NSString str = @”aad”;

BOOL isSuccess =[str writeToFile:[self filePath] atomically:YES

encoding:NSUTF8StringEncoding error:&error];

if (isSuccess) {

NSLog(@”成功”);

} else {

NSLog(@”失败”);

}
//字符串从文件中读取数据

NSString *text = [NSString stringWithContentsOfFile:[self filePath] encoding:NSUTF8StringEncoding error:nil];

self.tf2.text = text;

③数组的写入和读取操作

//写入
NSArray *dataArr = @[@"aa", @"nn"];
BOOL isSuccess = [dataArr writeToFile:[self filePath] atomically:YES];

//读取
NSArray *arr = [NSArray arrayWithContentsOfFile:[self filePath]];


④字典的写入和读取操作

//写入
NSDictionary *dic = @{@"name":@"zhang",@"hehe":@"dad"};
BOOL isSuccess = [dic writeToFile:[self filePath] atomically:YES];


//读取

NSDictionary *dic =[NSDictionary dictionaryWithContentsOfFile:[self filePath]] ;

⑤NSData写入和读取文件

//将字符串转为data型
NSData *data = [@"dasf" dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
//写入
[data writeToFile:[self filePath] atomically:YES];

//将data型转化为字符串
NSString * newStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
//读写
NSData *data1 =[NSData dataWithContentsOfFile:[self filePath]];


4.归档与反 归档

对象归档实例:Encoding

“归档”是值另一种形式的序列化,对模型对象进行归档的技术可以轻松将复杂的对象写入文件,然后再从中读取它们,只要在类中实现的每个属性都是基本数据类型(如int或float)或都是符合NSCoding协议的某个类的实例,你就可以对你的对象进行完整归档。

对,对象进行归档,服从协议

NSCoding协议声明了两个方法:(required)

-(void)encodeWithCoder:(NSCoder *)aCoder,是将对象写入到文件中。

-(id)initWithCoder:(NSCoder *)aDecoder,是将文件中数据读入到对象中。

实现这两个协议方法

1).创建Contact对象(Contact.h文件)

@interface Contact : NSObject

//如果要对一个对象进行归档操作,需要让该类服从NSCoding协议

@property (nonatomic, copy) NSString *name;

@property (nonatomic, copy) NSString *gender;

@property (nonatomic, assign) NSInteger age;

@property (nonatomic, copy) NSString *phone;

(instancetype)initWithName:(NSString )name gender:(NSString )gender age:(NSInteger)age phone:(NSString *)phone;

@end

2).(Contact.m文件)实现协议

//当对一个对象进行归档时,自动调用该方法,为该对象的实例变量进行归档操作.

- (void)encodeWithCoder:(NSCoder *)aCoder {

[aCoder encodeObject:_name forKey:@”name”];

[aCoder encodeObject:_gender forKey:@”gender”];

[aCoder encodeObject:_phone forKey:@”phone”];

[aCoder encodeObject:@(_age) forKey:@”age”];

}
//当对于一个对象进行反归档时,自动调用该方法,为该对象的实例变量进行反归档.

- (id)initWithCoder:(NSCoder *)aDecoder {

self = [super init];

if (self) {

self.name = [aDecoder decodeObjectForKey:@”name”];

self.gender = [aDecoder decodeObjectForKey:@”gender”];

self.age = [[aDecoder decodeObjectForKey:@”age”] integerValue];

self.phone = [aDecoder decodeObjectForKey:@”phone”];

}
return self;

}
//初始化

- (instancetype)initWithName:(NSString )name gender:(NSString )gender age:(NSInteger)age phone:(NSString *)phone {

self = [super init];

if (self) {

self.name = name;

self.gender = gender;

self.age = age;

self.phone = phone;

}
return self;

}
3).实现归档操作

Contact *contact = [[Contact alloc] initWithName:@”aa” gender:@”man” age:18 phone:@”138”];

NSMutableData *mData = [NSMutableData data];

//1.创建归档工具对象

NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:mData];

//2.开始归档

[archiver encodeObject:contact forKey:@”lock”];

//3.结束归档

[archiver finishEncoding];

[contact release];

[archiver release];

//4.写入文件

BOOL isSuccess = [mData writeToFile:[self getFilePath] atomically:YES];

if (isSuccess) {

NSLog(@”归档成功”);

} else {

NSLog(@”归档失败”);

}
//另一种方式

//[NSKeyedArchiver archiveRootObject:contact toFile:[self getFilePath]];

4).反归档操作

//1.从本地读取数据
NSData *data = [NSData  dataWithContentsOfFile:[self getFilePath]];
//2.创建反归档工具
NSKeyedUnarchiver *unArchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
//3.开始反归档操作
Contact *contact = [unArchiver decodeObjectForKey:@"lock"];

//结束反归档
[unArchiver finishDecoding];
[unArchiver release];


//另一种方式:

//Contact *contact = [NSKeyedUnarchiver unarchiveObjectWithFile:[self getFilePath]];

VoiceOver是什么?请举例解释一下iOS中的辅助功能(Accessibility)。开发者如何使用这些功能?

iOS应用是如何实现后台多任务处理(Multitasking)的?

Game Center针对iOS游戏有哪些功能?

iBeacons是什么?

Cocoa/Cocoa Touch是什么?

请概括一下Core Audio,Core Data以及Core Location各是什么。它们对iOS应用有何意义?

请描述SpriteKit和SceneKit的作用。

Metal是什么?

响应链(Responder Chain)是什么?它是如何发挥作用的?

按钮和其他控制方式对哪些操作做出回应?

AppDelegate扮演着什么样的角色?

请解释一下NSUserDefaults。就你而言,你会如何在磁盘中对数组对象进行序列化?

你会如何储存用户的认证信息?

请问何为Keychain服务?

为什么移动设备上的缓存和压缩是不可或缺的?

请解释一下~/Documents,~/Library和~/tmp。 iOS中的~属于什么目录?

AirPlay是如何运行的?换做是你,你会如何通过编程提高应用的实用性以及演示效果?

传感器,IO以及WiFi、拨号等连接方式如何在iOS平台上运作?它们有何利用价值?请扼要地谈谈你的观点。

iPad 2,iPad mini 1-3,iPad Retina,iPad Air 2,iPhone 5、5S、6以及6+在硬件性能方面有何差异?这对注重性能的应用有何限制?

关于编程

Cocoa Touch包含什么?不包含什么?

为什么Cocoa Touch的类名称是以两个大写字母开头的?

这是峰驼命名法的时候

Swift和Objective-C分别是什么?两者相比有何不同之处,又有何联系?

为什么Optional在Swift语言中非常重要?

请解释一下NSError。在Swift中,什么情况下能使用NSError ,什么情况下不能?

请说明如何使用Instancetype及其重要性。

在Swift中,什么时候该用let,什么时候该用var?

为什么map函数必不可少?该在什么情况下使用它?

你会选择什么工具来追踪Bug?

如果在Cocoa中发现一个Bug,你会如何处理?

如果应用的新版本出现了Regression的情况,该如何补救?如何防止用户在使用过程中遇到新的Bug?

Objective-C的类是怎么执行的?Objective-C Runtime是如何实现的?

iOS是如何提高安全性,保护用户隐私信息的?

应用可以下载并即刻显示数据。如何根据MVC来判断下载的最佳位置?

MVC对代码库(Codebase)的设计有何影响?

Controller Life-Cycle以及View Life-cycle分别有哪些调试方法?

iOS使用的是哪些设计模式(Design Patterns)?你的代码库使用的是哪些设计模式?

iOS提供哪些线程?如何充分利用这些线程?

请简要描述一下UIScrollView的执行过程。它是如何响应手势识别(Gesture Recognizer)、多点触控(Multi-Touch)和Run Loop的?

你认为iOS需要添加或改进哪些API?

关于界面

iPhone5、6、6+以及iPad Air 2的屏幕分辨率分别是多少?

分辨率的计算单位是什么?

请解释一下Interface Builder的作用以及NIB文件的概念。

iOS UI的图像储存类型是什么?

请描述一下Storyboard和标准NIB文件的差别。

设备状态栏(Device Status Bar)是什么?高度如何?是否透明?在手机通话或者导航状态下,它是如何显示的?

导航栏(Navigation Bar)是什么?能否拿出你的iPhone,指出你下载的哪些应用运用了导航栏?

选项卡(Tab Bar)和工具栏(Toolbar)分别是什么?两者之间有何共同点和不同点?

表视图(Table View)是什么?集合视图(Collection View)又是什么?

什么时候用“弹出(Popover)”属性最为合适?

Split-view Controller是什么?

选取器视图(Picker View)适合存放哪类内容?

应该在什么情况下使用标签、文本域和文本视图?

分段控件(Segmented Control)的作用是什么?

模态视图(Modal View)是什么?

iOS通知属于什么类型?

关于设计

iOS应用图标是指什么?请尽可能详细地描述一下。

最小尺寸和最大尺寸的应用图标分别是什么样子的?

应用图标能否包含透明的部分?

Newsstand的图标与常规应用有何不同?

请解释一下启动画面(Launch Images)。

自动布局(Auto Layout)的作用是什么?请概括一下它是如何运行的。

设计软件时为什么要加上动画?

请描述一下软件设计中的交互和Feedback有什么作用。

设计iPhone和iPad应用时,应分别考虑哪些因素?

请描述一下原型设计对于软件开发的意义。其作用是什么?

关于App Store

应用内购买(In-App Purchases)是怎么回事?IAP能够为用户带来哪些新体验?

你是否在App Store上发布过应用?能否概括一下过程?

iTunes Connect是什么?

Provisioning Profiles是指?

App ID是什么?

iOS的开发和发布签名证书有何异同?

如何使用TestFlight?通过Ad-hoc发布应用的话,该如何使用UUID?

应何时验证购买收据?

发布iAds(苹果平台广告)有哪些要求?

趣味问答

最近有没有开发什么好玩的东西?你最引以为豪的作品是什么?

谈一谈你常用的开发工具都有哪些优势?

你最敬佩的独立Mac或者iOS应用开发者是谁?

最喜欢什么项目?哪种类型的?

你觉得Xcode有哪些需要改进的地方?

iOS上你最喜欢哪些API?

是否有最中意的错误报告?

你最爱以哪种方式来检验一项新技术是否好用?

为什么词典被称作Dictionaries,而不是HashTable或HashMap?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: