1.写一个标准宏,这个宏输入两个参数并返回较小的一个
(1)函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,
3.extern c 的作用有哪些
(1)被 extern "C"限定的函数或变量是 extern 类型的;
4.#import与#include的区别,@class呢
(1)#import和#include的区别
当我们在代码中使用两次#include的时候会报错:因为#include相当于拷贝头文件中的声明内容,所以会报重复定义的错误
但是使用两次#import的话,不会报错,所以他可以解决重复导入的问题,他会做一次判断,如果已经导入一次就不导入了
1)obj-c的编译器处理后缀为m的文件时,可以识别obj-c和c的代码, 处理mm文件可以识别obj-c,c,c++代码,但cpp文件必须只能用c/c++代码,而且cpp文件include的头文件中,也不能出现obj- c的代码,因为cpp只是cpp
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的东西就可以用了,cpp混用obj-c的关键是使用接口,而不能直接使用实现代码,实际上cpp混用的是 obj-c编译后的o文件,这个东西其实是无差别的,所以可以用。obj-c的编译器支持cpp.
6.比较http和socket通信的区别http是客户端用http协议进行请求,发送请求时候需要封装http请求头,并绑定请求的数据,服务器一般有web服务器配合(当然也非绝对)。 http请求方式为客户端主动发起请求,服务器才能给响应,一次请求完毕后则断开连接,以节省资源。服务器不能主动给客户端响应(除非采取http长连接技术)。iphone主要使用类是NSUrlConnection。
scoket是客户端跟服务器直接使用socket“套接字”进行连接,并没有规定连接后断开,所以客户端和服务器可以保持连接通道,双方都可以主动发送数据。一般在游戏开发或股票开发这种要求即时性很强并且保持发送数据量比较大的场合使用。主要使用类是CFSocketRef。
由于通常情况下Socket连接就是TCP连接,因此Socket连接一旦建立,通信双方即可开始相互发送数据内容,直到双方连接断开。但在实际网络应用中,客户端到服务器之间的通信往往需要穿越多个中间节点,例如路由器、网关、防火墙等,大部分防火墙默认会关闭长时间处于非活跃状态的连接而导致 Socket 连接断连,因此需要通过轮询告诉网络,该连接处于活跃状态。
而HTTP连接使用的是“请求—响应”的方式,不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据。
很多情况下,需要服务器端主动向客户端推送数据,保持客户端与服务器数据的实时与同步。此时若双方建立的是Socket连接,服务器就可以直接将数据传送给客户端;若双方建立的是HTTP连接,则服务器需要等到客户端发送一次请求后才能将数据传回给客户端,因此,客户端定时向服务器端发送连接请求,不仅可以保持在线,同时也是在“询问”服务器是否有新的数据,如果有就将数据传给客户端。
7.堆和栈的区别
管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
(1).申请大小:
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示 overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
(2).碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出
(3).分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由 alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
(4).分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。
8.sprintf,strcpy,memcpy使用上有什么要注意的地方
strcpy是一个字符串拷贝的函数,它的函数原型为strcpy(char *dst, const char *src);
将src开始的一段字符串拷贝到dst开始的内存中去,结束的标志符号为 '\0',由于拷贝的长度不是由我们自己控制的,
所以这个字符串拷贝很容易出错。具备字符串拷贝功能的函数有memcpy,这是一个内存拷贝函数,它的函数原型
为memcpy(char *dst, const char* src, unsigned int len);
将长度为len的一段内存,从src拷贝到dst中去,这个函数的长度可控。但是会有内存叠加的问题。
sprintf是格式化函数。将一段数据通过特定的格式,格式化到一个字符串缓冲区中去。sprintf格式化的函数的长度不可控,
有可能格式化后的字符串会超出缓冲区的大小,造成溢出。
9.什么是沙箱模型?哪些操作是属于私有api范畴? 某个iphone工程进行文件操作有此工程对应的指定的位置,不能逾越。
iphone沙箱模型的有四个文件夹,分别是什么,永久数据存储一般放在什么位置,得到模拟器的路径的简单方式是什么.
documents,tmp,app,Library。
(NSHomeDirectory()),
手动保存的文件在documents文件里
Nsuserdefaults保存的文件在tmp文件夹里
Documents 目录:您应该将所有de应用程序数据文件写入到这个目录下。这个目录用于存储用户数据或其它应该定期备份的信息。
AppName.app 目录:这是应用程序的程序包目录,包含应用程序的本身。由于应用程序必须经过签名,
所以您在运行时不能对这个目录中的内容进行修改,否则可能会使应用程序无法启动。
Library 目录:这个目录下有两个子目录:Caches 和 Preferences
Preferences 目录包含应用程序的偏好设置文件。您不应该直接创建偏好设置文件,而是应该使用NSUserDefaults类来取得和设置应用程序的偏好.
Caches 目录用于存放应用程序专用的支持文件,保存应用程序再次启动过程中需要的信息。
tmp 目录:这个目录用于存放临时文件,保存应用程序再次启动过程中不需要的信息。
获取这些目录路径的方法:
1,获取家目录路径的函数:
NSString *homeDir = NSHomeDirectory();
2,获取Documents目录路径的方法:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex:0];
3,获取Caches目录路径的方法:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachesDir = [paths objectAtIndex:0];
4,获取tmp目录路径的方法:
NSString *tmpDir = NSTemporaryDirectory();
5,获取应用程序程序包中资源文件路径的方法:
例如获取程序包中一个图片资源(apple.png)路径的方法:
NSString *imagePath = [[NSBundle mainBundle] pathForResource:@”apple” ofType:@”png”];
UIImage *appleImage = [[UIImage alloc] initWithContentsOfFile:imagePath];
代码中的mainBundle类方法用于返回一个代表应用程序包的对象。
文件IO写入
1,将数据写到Documents目录:
- (BOOL)writeApplicationData:(NSData *)data toFile:(NSString *)fileName {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex:0];
if (!docDir) {
NSLog(@”Documents directory not found!”); return NO;
}
NSString *filePath = [docDir stringByAppendingPathComponent:fileName];
return [data writeToFile:filePath atomically:YES];
}
2,从Documents目录读取数据:
- (NSData *)applicationDataFromFile:(NSString *)fileName {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex:0];
NSString *filePath = [docDir stringByAppendingPathComponent:fileName];
NSData *data = [[NSData alloc] initWithContentsOfFile:filePath]];
return data;}
NSSearchPathForDirectoriesInDomains这个主要就是返回一个绝对路径用来存放我们需要储存的文件。
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:@"shoppingCar.plist"];
}
NSFileManager* fm=[NSFileManager defaultManager];
if(![fm fileExistsAtPath:[self dataFilePath]]){
//下面是对该文件进行制定路径的保存
[fm createDirectoryAtPath:[self dataFilePath] withIntermediateDirectories:YES attributes:nil error:nil];
//取得一个目录下得所有文件名
NSArray *files = [fm subpathsAtPath: [self dataFilePath] ];
//读取某个文件
NSData *data = [fm contentsAtPath:[self dataFilePath]];
//或者
NSData *data = [NSData dataWithContentOfPath:[self dataFilePath]];
}
iphone常见私有api的应用(比如直接发送短信,访问沙箱之外的磁盘文件).
10.线程的常见方法有哪些,你是如何处理多线程的,多线程同步问题你了解么?
线程创建的几种方式,线程的加锁,休眠,唤醒,解锁,退出,
多线程要考虑同步问题,解决同步问题的方式就是对某一资源加锁,当一个线程操作本资源时,其他线程不能操作 。
系统自带线程池(NSOpertionQueue)的作用:
凡是需要启动多个线程的地方都可以使用NSOpertionQueue,加入到NSOpertionQueue中的对象都需要继承NSOpertion。 NSOpertionQueue会在系统内部启动一个独立线程去执行这个被加入对象的main方法。常用的地方是用nsoprationqueue 下载图片,文件。如果是自己创建一个线程池,无非就是启动多个线程的时候,
把这些线程对象放到一个大数组中,如果需要启动线程的时候,先从数组中找空闲线程来使用。
自己管理线程池最大的难题是不好处理当启动多个线程后,用户在多个界面的跳转的时候,对线程方法的回调管理。
而NSOpertionQueue可以很好的处理他。
11.你在开发大型项目的时候,如何进行内存泄露检测的?
可以通过xcode的自带工具run---start with performance tool里有instruments下有个leaks工具,
启动此工具后,运行项目,工具里可以显示内存泄露的情况,双击可找到源码位置,可以帮助进行内存泄露的处理。
会引起循环引用
所有的引用计数系统,都存在循环应用的问题。例如下面的引用关系:
* 对象a创建并引用到了对象b.
* 对象b创建并引用到了对象c.
* 对象c创建并引用到了对象b.
这时候b和c的引用计数分别是2和1。
当a不再使用b,调用release释放对b的所有权,因为c还引用了b,所以b的引用计数为1,b不会被释放。
b不释放,c的引用计数就是1,c也不会被释放。从此,b和c永远留在内存中。
这种情况,必须打断循环引用,通过其他规则来维护引用关系。我们常见的delegate往往是assign方式的属性而不是retain方式 的属性,
赋值不会增加引用计数,就是为了防止delegation两端产生不必要的循环引用。
如果一个UITableViewController 对象a通过retain获取了UITableView对象b的所有权,这个UITableView对象b的delegate又是a,
如果这个delegate是retain方式的,那基本上就没有机会释放这两个对象了。自己在设计使用delegate模式时,也要注意这点。
13.以下每行代码执行后,person对象的retain count分别是多少?
Person *person = [[Person alloc] init]; count 1
[person retain]; retain count 2
[person release];retain count 1
[person release];retain count = 0
14.在一个对象的方法里面:
self.name = “object”;和 name =”object”有什么不同吗?
答:self.name = "object"会调用对象的setName()方法,会使object引用计数加1,name = "object"会直接把object赋值给当前对象的name 属性,引用计数不增加。
15:下面一段代码的执行结果是
main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
}
答:2,5
*(a+1)就是a[1],*(ptr-1)就是a[4],执行结果是2,5
&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)
int *ptr=(int *)(&a+1);
则ptr实际是&(a[5]),也就是a+5
原因如下:
&a是数组指针,其类型为 int (*)[5];
而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同。
a是长度为5的int数组指针,所以要加 5*sizeof(int)
所以ptr实际是a[5]
但是prt与(&a+1)类型是不一样的(这点很重要)
所以prt-1只会减去sizeof(int*)
a,&a的地址是一样的,但意思不一样
a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,
a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5].
16。id
声明的对象有什么特性?
(1)没有 * 号
(2)动态数据类型
(3)可以指向任何类的对象(设置是nil),⽽而不关⼼心其具体类型
(4)在运⾏行时检查其具体类型
(5)可以对其发送任何(存在的)消息
答:线程创建有三种方法:使用NSThread创建、使用 GCD的dispatch、使用子类化的NSOperation,然后将其加入NSOperationQueue;在主线程执行代码,方法是 performSelectorOnMainThread,如果想延时执行代码可以用performSelector:onThread:withObject:waitUntilDone:
1)代理:也叫委托,当某个对象收到多个事件,并要求同一个对象来处理所有事件时。委托机制依赖于某个协议定义的方法来发送消息。
2)通知:当需要多个对象或两个无关对象处理同一个事件时。
3)Block:适用于回调只发生一次的简单任务。
19.在iOS开发中什么时候用delegate,什么时候用Notification?