iOS 面试题(十二)

1  应用程序如何省电
    iOS的电能管理系统保持电能的方法是关闭当前未使用的硬件功能。应用程序可以通过以下组件达到省电的目的:CPU  WiFi和基带(EDGE,3G)无线信号  Core Location框架 加速计 磁盘  
   您的优化目标应该是以尽可能有效的方式完成大多数的工作。您应该总是采用Instruments和Shark工具对应用程序的算法进行优化。但是,很重要的一点,即使最优化的算法也可能对设备的电池寿命造成负面影响,因此,在写代码时应该考虑如下原则:
   避免需要轮询的工作,因为轮询会阻止CPU进入休眠状态。您可以通过NSRunLoop或者NSTimer类在规划需要做的工作,而不是使用轮询
   尽可能使共享的UIApplication对象的idleTimerDisabled属性保持NO。当设备不处于活动状态一段时间后,空闲定时器关闭设备的屏幕
   尽可能将任务合并在一起,以便使空闲时间最大化。间歇性执行任务会阻止系统更长时间无法关闭硬件
   避免过度访问磁盘 。举例来说,当你需要将状态信息保存在磁盘上,则当该状态信息发生变化时再进行保存,或者尽可能将状态变化合并保存,避免短时间频繁进行磁盘写入操作
   尽可能快的以群发方式传递数据包,而不是拉长数据传输的时间
   尽可能通过WiFi无线信号连接网络。
   如果您通过Core Location框架收集位置数据,则请尽可能快地禁止位置更新,以及位置过滤器和精度水平设置为恰当的值。

2   写一个递归方法:计算N的阶乘,然后将计算结果进行存储。以便应用退出后下次启动课直接获取该值。
    int f(int i)
   { 
       // 设置退出条件
         if(1 == i)
         {
             return 1;
         }
       //递归语句
        return i*f(i-1);
    }
   NSInteger result = f(N);
   // 创建文件管理器
   NSFileManager *fileManager = [NSFileManager defaultManager];
   NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
   NSString *documentsDirectory = [paths objectAtIndex:0];
  // 更改到待操作的目录下
   [fileManager changeCurrentDirectoryPath:[documentDirectory stringByExpandingTildeInPath]];
  // 创建文件fileName文件名称,contents文件的内容,如果开始没有内容可以设置为nil,attributes文件的属性,初始为nil
   NSString *fileName = @“f.text”;
   NSArray *array = [[NSArray alloc] initWithObject:result,nil];
   [fileManager createFileAtPath:fileName contents:array attributes:nil];

  3  NSArray和NSMutableArray的区别,多线程操作哪个更安全?
     NSMutableArray相对于NSArray可以改变数组中的元素,进行增删改
    当一个线程在修改数组,另一个数组在读取数组中的元素,会造成线程不安全,所以用NSArray更安全
   解决方案,可在对array进行操作时加锁
   NSLock *writeLock = [[NSLock alloc] init];
   [writeLock lock];
   // ……
   [writeLock unLock];

4   当前有一个数组,里面有若干重复的数据,如何去除重复的数据?(会几个写几个)
   因为涉及到重复数据项的问题,立马会想到集合(Set)这个数据结构,因为它是不容许存在重复数据项的数据结构。
  思路一 :将数组中的每一个元素插入到一个set中,利用set的自动剔除重复数据项的功能,将导致所有重复的数据没办法插入成功,也就是add方法返回false,然后调用toArray方法,返回这个集合所对应的数组。
  思路二 :随整个数组进行排序。然后遍历数组,将重复数据项,清除掉。假设下面这个算法的输入是一个已经排好序到的数组
for(int i = 0; i < array.length-1; i++){
        if(array[i] == array[i +1]){
           array[i] = -1;
        }
  然后遍历数组,删除所有为-1的数据项 
5   isKindOfClass、isMemberOfClass作用分别是什么?
  两者都是NSObject的比较Class的方法
但两者有很大的区别:
isKindOfClass来确定一个对象是否是一个类的成员,或者是这个类派生出来的类的成员
isMemberOfClass只能确认一个对象是否是当前类的成员
例如NSObject派生的类,用isMemberOfClass不能检测出任何类都是基于NSObject类的这一事实,而isKindOfClass可以

6   写出下面程序段的结果

NSDictionary *dict = [NSDictionary dictionaryWithObject:@"a string value" forKey:@"akey"]; 

NSLog(@"%@", [dict objectForKey:@"akey"]);

[dict release];

结果是: a  string value
考察NSDictionary取值的两个方法:objectForKey 和valueForKey   建议Dictionary只用objectForKey:来取值
objectForKey: 返回指定key的value 若没有这个key返回nil
valueForKey: 同样是返回指定key的value  但有一点,一般key可以是任意字符串的组合 ,如果key不是以@开头 这时候valueForKey:就等同于objectForKey;如果是以@开头,去掉key里的@然互剩下部分执行[super valueForKey:]
[dict valueForKey:@"@theKey"]; 会把 key 里的 @ 去掉,也就变成了 [dict valueForKey:@"theKey"];,而 dict 不存在 theKey 这样的 property,转而执行 [dict valueForUndefinedKey:@"theKey"];,抛出 NSUndefinedKeyException 异常后 crash 掉。

7   请写出以下代码的执行结果

NSString  * name = [ [ NSString alloc] init ];

name = @”Habb”;

[ name  release];

NSString是一个不可变的字符串,它在初始化后,你不能改变该变量所分配的内存中的值(仅可重新分配该变量所处的内存空间)用NSMutableString即可

8   请分别写出SEL、id的意思?
id 和void *并非完全一样。id是一个指向任何一个继承了Object(或者NSObject)类的对象。需要注意的是id是一个指针,所有你在使用的时候不需要加 * 。比如id foo = nil 定义了一个nil指针,这个指针指向NSObject的一个任意的子类 。而id * foo = nil 则定义了一个指针,被指向的这个指针指向NSObject的一个子类

SEL是“selector”的一个类型,表示一个方法的名字。比如我们定义一个Foo类,这个类带有一个-(int)blah方法,那么
NSLog(@“SEL = %s”,@selector(blah)); 会输出SEL = blah 说白了SEL就是返回方法名

9   iPhone上,不能被应用程序直接调用的系统程序是什么?
   时钟 视频 指南针 天气 计算器 备忘录 提醒事件 股市 (待定)


10    以.mm为拓展名的文件里,可以包含的代码有哪些?c和obj-c如何混用
      .mm文件可以识别obj-c c c++ 
 1  obj-c 的编译器处理后缀为m的文件时,可以识别obj-c和c的代码 
     处理mm文件可以识别obj-c c c++代码
    但是cpp文件必须只能用c/c++代码,而且在cpp文件include的头文件中,也不能出现obj-c的代码
 2  在mm文件中混有cpp直接使用即可,所以obj-c混cpp不是问题
 3  在cpp中混用obj-c其实就是使用obj-c编写的模块是我们想要的
    如果模块以类实现,那么要按照cpp class的标准写类的定义,头文件不能出现obj-c的东西,包括#import cocoa 
    实现文件中,即类的实现代码中可以使用obj-c的东西,可以import,只是后缀是是mm 
    如果模块以函数实现,那么头文件按照c的格式声明函数
    实现文件中,c++函数内部可以用obj-c,但后缀还是mm 或 m
   总结:只要cpp文件和cpp include的头文件中不包含obj-c的东西就行。obj-c的编译器支持cpp


11  说说如何进行后台运行程序
    程序在进入后台运行一小段时间,就会进入挂起状态,此时应用不会再执行任何代码。可以通过调用beginBackgroundTaskWithExpirationHandler:方法让系统给出额外的时间完成一些需要在后台长时间执行的任务
进入后台要处理的任务通过application delegate的方法
-(void)applicationDidEnterBackground:(UIApplication:)application{
 // 如处理界面的刷新和网络请求等
}
 当程序从后台将要进入前台要处理的任务可以写在-(void)applicationWillEnterForeground:(UIApplication *)application{
 // 恢复一些操作的处理
}
当然也可以注册系统通知来实现UIApplicationDidEnterBackgroundNotification和 UIApplicationWillEnterForegroundNotification
   
 12   sizeof和strlen的区别和联系
 一 sizeof
     sizeof(…)是运算,在头文件中typedef为unsigned int ,其值在编译时就计算好了,参数可以是数组  指针  类型   对象  函数等
     它的功能是:获得保证能容纳实现所建立的最大对象的字节大小
     由于在编译时计算,因此sizeof不能用来返回动态分配的内存空间的大小。实际上,用sizeof来返回类型以及静态分配的对象 结构体 或数组所占的空间,返回值跟参数里存储的内容没有关系。
     具体而言,但参数分别如下的时,sizeof返回值表示的含义如下:
     数组 — 编译时分配的数组的大小
     指针 — 存储该指针所用的空间大小(存储该指针的地址长度,是长整型 应该为4或者8)
     类型 — 该类型所占的空间大小
     对象 —  对象的实际占用空间大小
     函数 — 函数的返回类型所占的空间大小 。函数的返回类型不能是void

二  strlen
    strlen(…)是函数,要在运行时才能计算。参数必须是字符型指针(char *) 。当数组名作为参数传入时,实际上数组就退化成指针了。
   它的功能是:返回字符串的长度。该字符串可能是自己定义的,也可能是内存中随机的,该函数实际完成的功能是从代表该字符串的第一个地址开始遍历,直到遇到结束符NULL。返回的长度大小不包括NULL


三  举例 
    char arr[10] = “what?”;
    int len_strlen = strlen(arr);
    int len_sizeof = sizeof(arr);
    输出 5 and 10
   点评:sizeof返回定义arr数组时,编译器为其分配的数组空间大小,不关心里面存了多少数据。strlen只关心存储的数据内容,不关心空间的大小和类型
      char *parr = new char[10];
      int len_strlen = strlen(parr);
      int len_sizeof = sizeof(parr);
      int len_sizeofA = sozeof(*parr);
      输出 23   4   1 
  点评: 第一个输出结果23 实际上每次运行都不一样 ,这取决于parr里面存了什么(从parr[0]开始直到遇到第一个NULL结束);第二个结果实际上本意是想计算parr所指向的动态内存空间,但是事与愿违,sizeof认为parr是个字符指针,因此返回的是指该指针所占的空间(指针的存储用的是长整型,所以是4)第三个结果,由于*parr所代表的是parr所指向的地址空间存放的字符,所以长度是1
 

13   sprintf,strcpy,memcpy的功能?使用上要有哪些要注意的地方
不同拷贝函数的作用:
strcpy 
对字符串进行操作,完成从源字符串到目的字符串的拷贝,当源字符串大小大于目的字符串的最大存储空间后,执行该操作会出现段错误
spintf
函数操作的源对象不限于字符串:源对象可以是字符串 也可以是任意基本类型的数据。主要实现将字符串或基本数据类型转化为字符串。要额外指定格式符并且进行格式转化。
如果源对象是字符串,并且指定%s格式符,也可以实现字符串的拷贝功能
memcpy
实现内存的拷贝,实现将一块内存拷贝到另一块内存。该函数的操作对象不限于某一类数据类型,或者说适用于任意数据类型,只要能给出对象的起始地址和内存长度信息,并且对象具有可操作性即可。通常限于同种类型数据或对象之间的拷贝,包括字符串以及基本数据类型的拷贝
在软件升级中,当进行数据拷贝时,最好使用memcpy来进行数据拷贝。因为strcpy sprintf进行拷贝时,当检查到源字符串中有’\0’即ascll码为00 时,被当做数据结束符,就会停止拷贝

14  自己写函数,实现strlen功能
    strlen所做的仅仅是一个计数器的工作,它从内存某个位置开始扫描,直到碰到第一个结束符’\0’为止,然后返回计数器值
    int strlen(const char *str){
       assert(str != NULL);// 判断字符串地址非0
       int len;
       while((*str++) != ‘\0’){
        len++;
       }
       return len;
    }
 15   写一个代码片段输入一个字符串“20130322152832”,输出一个NSDate类型的对象,打印该对象输出2013-03-22 15:28:32
      NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
      // 设置格式
      [formatter setDateFormat:”yyyy-MM-dd HH : mm :  ss”];
      将字符串类型转化成 NSDate类型
      NSDate *Date = [formatter  dateFromString:“20130322152832”]
      NSLog(@“%@",Date);
 
16    找错误

a:void test1()

{

 char string[10];

 char* str1 = "0123456789”;  错误!!!// 加个’\0’表示结束符  strcpy只有遇到’\0’才停止    加上之后strcpy会造成string数组溢出

 strcpy( string, str1 );

扫描二维码关注公众号,回复: 3800694 查看本文章

}

b:void GetMemory( char **p, int num )

{

 *p = (char *) malloc( num );

}

void Test( void )

{

 char *str = NULL;

 GetMemory( &str, 100 );

 strcpy( str, "hello" ); 

 printf( str ); 

}


猜你喜欢

转载自blog.csdn.net/a_ss_a/article/details/38519161