标签:
1.Objective-C语言特性
2.static __block const
3.Object-C的内存管理
4.RunLoop
5.iOS消息传递机制
6.iOS程序生命周期
7.MVC设计模式MVVM
8.UIView CALayer Frame 与bounds
9.根类 NSObject
10.多线程简介
11.数据持久化
12.JSON和XML HTML 自定义报文
13.网络编程 HTTP TCP/IP Socket ASI AFNetwork
14.AppStore上传 及远程推送
15.XMPP
16.Block
===========================================================
Objective-C是面向对象的语言
Objective-C和Java C++一样,有封装,继承,多态,重用。
但是它不像C++那样有重载操作法、模版和多继承,也没有Java的垃圾回收机制。但
Objective-C的优点
Objective-C语言有C++ Java等面向对象的特点。Objective-C的优点是它是动态的。动态能力有三种:
动态类-运行时确定类的对象
动态绑定-运行时确定要调用的方法
动态加载--运行时为程序加载新的模块
动态类型(Dynamic typing)
运行时决定对象类型,id类型
id obj = someInstance;
if ([obj isKindOfClass:someclass])
{
someclass*classInstance = (someclass *)obj
}
动态绑定
动态绑定基于动态类型,某个实例对象类型被确定后,将某些属性和响应的方法绑定到实例上。该队相对应的属性和响应者消息也被完全确定。
在OC中最常用的是消息传递机制。调用实例的方法,所做的是向该实例对象的指针发送消息,实例在收到消息后,
从自身实现中寻找响应这条消息的方法。
在Cocoa层,我们一般向一个NSObject对象发送 -respondsToSelector活着instanceRespondToSelector,在消息机制被触发前
+resolveClasssMethod 和+resolveInstanceMethod将会被调用,此时有机会动态的响雷或者实例添加新方法
动态加载
在Retina设备上加载@2x的图片
基本动态特性在常规的Cocoa开发中非常常用,由于Cocoac程序大量的使用Protocol-Delegate中的设计模式,因此大部分delegate指针类型必须是id
以满足运行时delegate的动态转换(Java里这种设计模式被称为Strategy?)
OC的动态性表现为三个方面:动态类型,动态绑定,动态加载。之所以叫动态,是因为必须到运行时(runtime )才会做一些事
面向对象的三个基本特征是:封装、继承、多态。
封装是面向对象的特征之一,是对象和类概念的主要特性。 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的 数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。隐藏对象的属性和实现细节,仅对外公开接口,提高代 码安全性,封转程度越高,独立性越强,使用越方便。 继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。 通过 继承创建的新类称为“子类”或“派生类”。 被继承的类称为“基类”、“父类”或“超类” 多态性:允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子 对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针
我们说的obc是动态运行时语言是什么意思? 多态。主要是将数据类型的确定由编译时,推迟到了运行时。这个问题其实浅涉及到两个概念,运行时和多态。简单来说, 运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。多态:不同对象以自己的方式响应 相同的消息的能力叫做多态。意思就是假设生物类(life)都用有一个相同的方法-eat;那人类属于生物,猪也属于生物,都 继承了life后,实现各自的eat,但是调用是我们只需调用各自的eat方法。也就是不同的对象以自己的方式响应了相同的消 息(响应了eat这个选择器)。因此也可以说,运行时机制是多态的基础
有时我们需要在一个已经定义好的类中增加一些方法,而不想去重写该类。比如,当工程已经很大,代码量比较多,或者类中已经包住很多方法,已经有其他代码调用了该类创建对象并使用该类的方法时,可以使用类别对该类扩充新的方法。
category extension protrol
分别描述类别(categories)和延展(extensions)是什么?以及两者的区别?继承和类别在实现中有何区
别?为什么Category只能为对象添加方法,却不能添加成员变量?
类别:在没有原类.m文件的基础上,给该类添加方法; 延展:一种特殊形式的类别,主要在一个类的.m文件里声明和实现延展的作用,就是给某类添加私有方法或是私有变量。 两个的区别:延展可以添加属性并且它添加的方法是必须要实现的。延展可以认为是一个私有的类目。 继承和类别在实现中的区别:类别可以在不获悉,不改变原来代码的情况下往里面添加新的方法,只能添加,不能删除修 改。并且如果类别和原来类中的方法产生名称冲突,则类别将覆盖原来的方法,因为类别具有更高的优先级。 Category只能为对象添加方法,却不能添加成员变量的原因:如果可以添加成员变量,添加的成员变量没有办法初始 化----这是语言规则
什么是Protocol?什么是代理?写一个委托的interface?委托的property声明用什么属性?为什么? Protocol:一个方法签名的列表,在其中可以定义若干个方法。根据配置,遵守协议的类,会实现这个协议中的若干个方 法。
Objective-C 2.0也是有垃圾回收机制的,但是只能在Mac OS X Leopard10.5 以上的版本使用。
IPhone iOS有没有垃圾回收?autorelease 和垃圾回收制(gc)有什么关系?
没有。autorelease只是延迟释放,gc是每隔一段时间询问程序,看是否有无指针指向的对象,若有,就将它回收。他们 两者没有什么关系。
What is lazy loading?
就是懒加载,只在用到的时候才去初始化。也可以理解成延时加载。 我觉得最好也最简单的一个列子就是tableView中图片的加载显示了。 一个延时载,避免内存过高,一个异步加载,避免线程堵塞。
Object-C有多继承吗?没有的话用什么代替?
没有,多继承在这里是用protocol 委托代理来实现的
cocoa 中所有的类都是NSObject 的子类,你不用去考虑繁琐的多继承 ,虚基类的概念.ood的多态特性 在 obj-c 中通过委托来实现.
ARC
iOS8 新特性
什么是动态链接库和静态链接库?调用一个类的静态方法需不需要release???程序的编译过程--链接--- 静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件;动态链接就是 把调用的函数所在文件模块 (DLL)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从DLL中寻找 相应函数代码,因此需要相应DLL文件的支持。 静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还 可以再包含其他的动态或静态链接库。
动态的是:运行时才加载到内存中,
静态:编译时就加载到内存中 静态方法也就是类方法,不需要release
Object-C有私有方法吗?私有变量呢?
objective-c 类里面的方法只有两种, 静态方法和实例方法. --就是类方法和对象方法,
这似乎就不是完整的面向对象了,按照OO的原则就是一个对象只暴露有用的东西. 如果没有了私有方法的话, 对于一些小范围的代码重用就不那么顺手了. 在类里面声名一个私有方法
@interface Controller : NSObject {
NSString *something;
}
+ (void)thisIsAStaticMethod;
– (void)thisIsAnInstanceMethod;
@end
@interface Controller (private)
- (void)thisIsAPrivateMethod;
@end
@private可以用来修饰私有变量
在Objective‐C中,所有实例变量默认都是私有的,所有实例方法默认都是公有的
Swfit与Objective-C相比
*从语法上来说,Swift更简单,更简洁
*开发难度上,Swfit比OC难得多,开源框架比较少,会CocOaTouch开发上手简单,如果之间上手Swfit,遇到问题一查资料都是OC的例子
*swift在arc的objc上架了类型导倒,泛型,运算符重载,没有异常捕捉 oc上也不怎么使用
swift中对象和struct的区别.
OC中的数字对象都有哪些,简述它们与基本数据类型的区别是什么
Objective-C中的数字对象NSNumber; Objective-C中的基本类型和C语言中的基本类型一样.主要有:int,long,float,double,char,void,bool等. 对于基本类型变量,不需要用指针,也不用手动回收,方法执行结束会自动回收.数字对象需要指针,也需要手动回收内存。
什么是动态识别,动态绑定?延展--程序的编译过程
编译时和运行时对象分别是什么数据类型
对于语句NSString * testObject = [[NSData alloc]init];testObject在编译时和运行时分别是什么数据类型? 编译时是nsstring, 运行时是nsdata的一个实例,没有类型之分。
编译时被当做NSString 的实例来处理,运行时其isa指向的是[NSData Class],故运行时该实例只能收到NSData的固有实例方法(或者自己写的Category),如果对该实例发送诸如 testObject stringByAppendingString:@"哈哈哈" ,编译时不会报错只会给出黄色警告,但是运行时会直接崩溃,错误信息可能是
-[_NSZeroData stringByAppendingString:]: unrecognized selector sent to instance 0x8946440
故可以看出NSData 在运行时的真实类型是_NSZeroData(这里用的NSData alloc init,这里是个空的NSData,所以是ZeroData,不是说所有的)
如果对该实例发送诸如 testObject
base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength:这类消息 编译器会直接给出红色错误,无法运行,但其实这个实例是有这个方法的。
这里如果改为 id testObject=[[NSData alloc]init];
那么发送NSString 的方法仍然会崩溃,发送NSData的实例方法则完全正常。并且两种情况编译器都不会再给出警告或错误。 这就是运行时语言的特点
//1 继承有传递性
//父类继承过来的变量可以直接使用.继承于nsobject 改成父类;
//super (关键字,类似consit static); self是一个对象
//子类特性 重写父类函数sayHi,优先执行子类的函数;
//2 初始化的时候,优先调用父类的初始化方法,初始化父类中的变量,然后再初始化自己独有的变量。
//每个类都清楚自己独有的变量因此初始化方法里仅仅初始化自己独有的变量,通过调用父类初始化方法,初始化继承过来的变量;
//最不科学的初始化方法:在子类里面,为每个变量赋值,因为,你需要自己查阅都继承了那些变量信息;
//指定初始化,通常选参数最多的作为指定初始化方法;
//初始化方法特别多的时候,指定必须执行的初始化方法;//方便调用时赋值,无论改什么变量,都用变量最多的那个初始化方法
什么是安全释放?
[_instance release],_instance = nil;
OC中异常exception 怎么捕获?不同的CPU结构上开销怎样?C中又什么类似的方法? 了解一下异常捕获
CPU的开销:关于Objective-C++中的异常处理,可以相互捕获到吗?不可以;
run loop,正如其名称所示,是线程进入和被线程用来响应事件以及调用事件处理函数的地方。需要在代码中使用控制语 句实现run loop的循环,也就是说,需要代码提供while 或者 for循环来驱动run loop。在这个循环中,使用一个 runloop对象[NSRunloop currentRunloop]执行接收消息,调用对应的处理函数。
谈谈对性能优化的看法,如何做? 控制好内存,不用的内存实时释放;冗余代码;用户体验度;耗时操作,开线程进行处理
简述值传递和引用传递的区别? 所谓值传递,就是说仅将对象的值传递给目标对象,就相当于copy;系统将为目标对象重新开辟一个完全相同的内存空间。 所谓引用传递,就是说将对象在内存中的地址传递给目标对象,就相当于使目标对象和原始对象对应同一个内存存储空间。此时,如 果对目标对象进行修改,内存中的数据也会改变。
NSArray和NSMutableArray的区别,多线程操作哪个更安全? NSArray更安全,当同时被访问时,NSArray是不可改变
以.mm为拓展名的文件里,可以包含的代码有哪些? .mm是oc和C++混编类型文件后缀,给编译器识别的。
说说如何进行后台运行程序?
答:判断是否支持多线程
UIDevice* device = [UIDevice currentDevice];
BOOL backgroundSupported = NO;
if ([device respondsToSelector:@selector(isMultitaskingSupported)])
backgroundSupported = device.multitaskingSupported;
声明你需要的后台任务Info.plist中添加UIBackgroundModes键值,它包含一个或多个string的值,包括audio:在后台提供声音 播放功能,包括音频流和播放视频时的声音 location:在后台可以保持用户的位置信息 voip:在后台使用VOIP功能 前面的每个value让系统知道你的应用程序应该在适当的时候被唤醒。例如,一个应用程序,开始播放音乐,然后移动到后台仍然需要 执行时间,以填补音频输出缓冲区。添加audio键用来告诉系统框架,需要继续播放音频,并且可以在合适的时间间隔下回调应用程 序;如果应用程序不包括此项,任何音频播放在移到后台后将停止运行。除了添加键值的方法,IOS还提供了两种途径使应用程序在后 台工作:
Task completion—应用程序可以向系统申请额外的时间去完成给定的任务
Local notifications—应用程序可以预先安排时间执行local notifications 传递实现长时间的后台任务:应用程序可以请求在后台 运行以实现特殊的服务。这些应用程序并不连续的运行,但是会被系统框架在合适的时间唤醒,以实现这些服务 91
你了解svn,cvs等版本控制工具么?
版本控制 svn,cvs 是两种版控制的器,需要配套相关的svn,cvs服务器。scm是xcode里配置版本控制的地方。版本控制 的原理就是a和b同时开发一个项目,a写完当天的代码之后把代码提交给服务器,b要做的时候先从服务器得到最新版本, 就可以接着做。 如果a和b都要提交给服务器,并且同时修改了同一个方法,就会产生代码冲突,如果a先提交,那么b提交 时,服务器可以提示冲突的代码,b可以清晰的看到,并做出相应的修改或融合后再提交到服务器。
目标-动作机制 目标是动作消息的接收者。一个控件,或者更为常见的是它的单元,以插座变量的形式保有其动作消息的目标。
动作是控件发送给目标的消息,或者从目标的角度看,它是目标为了响应动作而实现 的方法。 程序需要某些机制来进行事件和指令的 翻译。这个机制就是目标-动作机制。
参考target-action
深层复制(mutableCopy):复制引用对象本身---再创建一个对象 得到一个新的可变对象,可以看到它的地址和原来对象的地址是不同的,也就是新对象的retainCount从0-1。
copy得到的是一个不可变对象,这里分为两种情况:
1、如果原来的对象也是一个不可变的,那么他们的地址指向同一个地址,也就是说它们同一个对象,只是把retainCount加1了而已,只复制指向对象的指针,而不复制引用对象本身 浅层复制(copy)
2、原来的对象是一个可变对象,那么它会新生成一个不可变对象,地址不同,也是retainCount从0-1。(和mutableCopy一样的深拷贝)
什么时候用到深浅复制呢?那么我就把我所总结的一些分享给大家,希望能帮助你们更好的理解深浅复制!
那么先让我们来看一看下边数组类型的转换
1、不可变对象→可变对象的转换:
NSArray *array1= [NSArray arrayWithObjects:@"a",@"b",@"c",@"d",nil];
NSMutableArray *str2=[array1 mutableCopy];
2、可变对象→不可变对象的转换:
NSMutableArray *array2 = [NSMutableArray arrayWithObjects:@"aa",@"bb",@"cc",@"dd",nil];
NSArray *array1=[ array2 Copy];
3、可变对象→可变对象的转换(不同指针变量指向不同的内存地址):
NSMutableArray *array1= [NSMutableArray arrayWithObjects:@"a",@"b",@"c",@"d",nil];
NSMutableArray *str2=[array1 mutableCopy];
通过上边的两个例子,我们可轻松的将一个对象在可变和不可变之间转换,并且这里不用考虑内存使用原则(即引用计数的问题)。没错,这就是深拷贝的魅力了。
4、同类型对象之间的指针复制(不同指针变量指向同一块内存地址):
NSMutableString *str1=[NSMutableString stringWithString:@"two day"];
NSMutableString *str2=[str1 retain];
NSArray *array1= [NSArray arrayWithObjects:@"a",@"b",@"c",@"d",nil];
NSArray *str2=[array1 Copy];
通俗的讲,多个指针同时指向同一块内存区域,那么这些个指针同时拥有对该内存区的所有权。所有权的瓜分过程,这时候就要用到浅拷贝了。
什么时候用到深浅拷贝?
深拷贝是在要将一个对象从可变(不可变)转为不可变(可变)或者将一个对象内容克隆一份时用到;
OC中是所有对象间的交互是如何实现的? 通过指针实现的
objective-c中的类型转换分为哪几类? 可变与不可变之间的转化; 可变与可变之间的转化;不可变与不可变之间
编译语言和解释语言的区别 区别:C语言,OC语言属于编译语言;解释语言:也可以理解为脚本文件,不需要编译, 编译型语言写的程序执行之前,需要一个专门的编译过程,把程序编译成为机器语言的文件,比如exe文件,以后要运行的 话就不用重新翻译了,直接使用编译的结果就行了(exe文件),因为翻译只做了一次,运行时不需要翻译,所以编译型语 言的程序执行效率高,但也不能一概而论,部分解释型语言的解释器通过在运行时动态优化代码,甚至能够使解释型语言的 性能超过编译型语言。 解释则不同,解释性语言的程序不需要编译,省了道工序,解释性语言在运行程序的时候才翻译, 比如解释性basic语言,专门有一个解释器能够直接执行basic程序,每个语句都是执行的时候才翻译。这样解释性语言每 执行一次就要翻译一次,效率比较低。解释是一句一句的翻译。
===============================================================
static __block const id、nil null 预处理指令#define #import和#include @class
用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年问题):
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365ul)
1) #define 语法的基本知识(例如:不能以分号结束,括号的使用,等等)
2)懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少 秒而不是计算出实际的值,是更清晰而没有代价的。
3) 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。
4) 如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。
#import和#include的区别,@class代表什么?
@class一般用于头文件中需要声明该类的某个实例变量的时候用到,在m文件中还是需要使用#import
而#import比起#include的好处就是不会引起交叉编译。
这样会导致循环引入头文件需要使用对方的时候,有一个文件或者两个都改成@class b;在他们的.M文件引入头文件
#import 过多会影响编译时间 ,是编译时间过长,用@class可以缩短编译时间
将注册的通知标示符,写成宏在pch文件里,可以提高可读性
系统的通知,监测键盘弹出,监测视频播放完毕;
单例很强大,用于不相关的类直之间的通讯,appdelegate是系统的单例,
任何地方都用单例会降低性能 ;
uipappliction
uiWindow
UIview
cell
button
查找顺序自上而下,响应顺序,自下而上;
c和obj-c如何混用? 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。
举出5个以上你所熟悉的ios sdk库有哪些和第三方库有哪些?
ios-sdk:
Foundation.framework,CoreGraphics.framework,UIKit.framework, MediaPlayer.framework, CoreAudio.framework
第三方库: 1.json编码解码;2.ASIHTTPRequest等相关协议封装;3.EGORefreshTableHeaderView下拉刷新代 码;4.AsyncImageView 异步加载图片并缓存;5.SDWebImage——简化网络图片处理
如何将产品进行多语言发布?
程序国际化;
比如:本地化应用程序名称
(1、选中工程,Info—Localizations点击“+”添加要国际化的语言。 (2、在InfoPlist.strings右边会多出一个三角形,点击展开可看到InfoPlish.strings(english)和
InfoPlish.strings(chinese)两个版本的文件; (3、在InfoPlish.strings(english)文件中加入:
CFBundleDisplayName ="Program"; 其中“Program”为英文应用程序名称,同理在InfoPlish.strings(chinese)文件中加入: CFBundleDisplayName ="应用程序"; 其中“应用程序”为中文名称,注意:CFBundleDisplayName加不加双引号都行;
(编辑Info.plist,添加一个新的属性Application has localized display name, 设置其类型为boolean,并将其 value设置为YES即可。
在开发大型项目的时候,如何进行内存泄露检测的? 内存泄露怎么处理?
如何检测内存泄露:
可以通过xcode的自带工具run---start with performance tool里有instruments下有个leaks工具,启动此工具 后,运行项目,工具里可以显示内存泄露的情况,双击可找到源码位置,可以帮助进行内存泄露的处理。 如何处理:先定位到具体位置, 再解决之。
iphone应用程序的项目基本结构?
· Classes -> 源程序文件(.h、.m)
· Other Sources-> main.m 等,不需要程序员修改 -Prefix.pch
· Resources -> 界面文件(.xib)、配置文件-info.plist
· Frameworks -> 链接的库· Targets -> 项目的不同Target(资源、编译配置不同)
· Executables -> 项目中所有的可执行文件 -Prefix.pch:_Prefix为所有的项目程序文件预先配置运行环境的前缀标头,在程序运行之前,引入所需框架中的(.h)头文 件。这样可以减少每个头文件对程序编译做出相同的定义,在巨型的应用程序项目开发中节省大量的时间,例如,程序有 100个根文件需要定义abc.h,只需要在_Preix.pch文件下建立一个对象,所有的根文件便可以重复地对程序编译做出定 义。
编译语言和解释语言的区别 区别:C语言,OC语言属于编译语言;解释语言:也可以理解为脚本文件,不需要编译, 编译型语言写的程序执行之前,需要一个专门的编译过程,把程序编译成为机器语言的文件,比如exe文件,以后要运行的 话就不用重新翻译了,直接使用编译的结果就行了(exe文件),因为翻译只做了一次,运行时不需要翻译,所以编译型语 言的程序执行效率高,但也不能一概而论,部分解释型语言的解释器通过在运行时动态优化代码,甚至能够使解释型语言的 性能超过编译型语言。 解释则不同,解释性语言的程序不需要编译,省了道工序,解释性语言在运行程序的时候才翻译, 比如解释性basic语言,专门有一个解释器能够直接执行basic程序,每个语句都是执行的时候才翻译。这样解释性语言每 执行一次就要翻译一次,效率比较低。解释是一句一句的翻译。
OC中是所有对象间的交互是如何实现 通过指针实现的
objective-c中的类型转换分为哪几类? 可变与不可变之间的转化; 可变与可变之间的转化;不可变与不可变之间。
关键字const什么含义
const意味着”只读”,下面的声明都是什么意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。
结论:
•; 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果
你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清
理的。)
•; 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。
•; 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。
欲阻止一个变量被改变,可以使用 const 关键字。在定义该const 变量时,通常需要对它进行初
始化,因为以后就没有机会再去改变它了;
(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为 const,或二者同时指
定为const;
(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4)对于类的成员函数,若指定其为const 类型,则表明其是一个常函数,不能修改类的成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为const 类型,以使得其返回值不为“左值”。
关键字volatile有什么含义?并给出三个不同例子?
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到
这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
• 并行设备的硬件寄存器(如:状态寄存器)
• 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
• 多线程应用中被几个任务共享的变量
• 一个参数既可以是const还可以是volatile吗?解释为什么。
• 一个指针可以是volatile 吗?解释为什么。
下面是答案:
• 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
• 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
static作用?
函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,
因此其值在下次调用时仍维持上次的值;
(2)在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
(3)在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明
它的模块内;
(4)在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
(5)在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。
__block
id和void *并非完全一样。在上面的代码中,id是指向struct objc_object的一个指针,这个意思基本上是说,id是一个指向任何一个继承了Object(或者NSObject)类的对象。需要注意的是id是一个指针,所以你在使用id的时候不需要加星号。比如id foo=nil定义了一个nil指针,这个指针指向NSObject的一个任意子类。而id *foo=nil则定义了一个指针,这个指针指向另一个指针,被指向的这个指针指向NSObject的一个子类。
id代表任意对象类型 凡是继承于NSObject的都是对象#import是将一个文件全部内容拷到当前文件;id是一个指针,指向任何一个继承了Object(或者NSObject)类的对象,所以在OC中,任意一个object对象都可以是一个id,id不是类型吗instancetype也是指针,void相当于空指针;
isKindOfClass、isMemberOfClass、selector作用分别是什么 isKindOfClass,作用是,某个对象属于某个类型,包括继承的类型--- isMemberOfClass:某个对象确切属于某个类型,是不是具体的实例 selector:通过方法名,获取在内存中的函数的入口地址
请分别写出SEL、id、@的意思? SEL是“selector”的一个类型,表示一个方法的名字-------就是一个方法的入口地址 id是一个指向任何一个继承了Object(或者NSObject)类的对象。需要注意的是id是一个指针,所以在使用id 的时候不需要加*。
nil和C语言的NULL相同,在objc/objc.h中定义。nil表示一个Objctive-C对象,这个对象的指针指向空(没有东西就是空)
if (a.b. c)
上面的代码当a、b、c中任何一个为nil时,值为false,这样写完全ok,所以没必要写成下面那样:
if a
if b
if c
===============================================================
3.Object-C的内存管理
线程和进程的区别
进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
(1)管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生 memory leak。
(2)申请大小:能从栈获得的空间较小,堆是向高地址扩展的数据结构,是不连续的内存区域。堆的大小受限于计算机系统中 有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
(3)碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。 对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块 从栈中间弹出
(4)分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成 的,比如局部变量的分配。动态分配由 alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器 进行释放,无需我们手工实现。
(5)分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈 都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。
OC使用了一种叫做引用计数的机制来管理对象,如果对一个对象使用了alloc、 [Mutable]copy,retain,那么你必须使用相应的realease或者autorelease。也可以理解为 自己生成的对象,自己持有。非自己生成的对象,自己也能持有。不在需要自己持有的 对象时释放。非自己持有的对象无法释放。生成并持有对象<alloc,new,copy,mutableCopy 等>,持有对象<retain>,释放对象<release>,废弃对象<dealloc>
1.当你使用new,alloc和copy方法创建一个对象时,该对象的保留计数器值为1.当你不再使用该对象时,你要负责向该对象发送一条release或autorelease消息.这样,该对象将在使用寿命结束时被销毁.
2.当你通过任何其他方法获得一个对象时,则假设该对象的保留计数器值为1,而且已经被设置为自动释放,你不需要执行任何操作来确保该对象被清理.如果你打算在一段时间内拥有该对象,则需要保留它并确保在操作完成时释放它.
3.如果你保留了某个对象,你需要(最终)释放或自动释放该对象.必须保持retain方法和release方法的使用次数相等.
为什么很多内置的类,如TableViewController的delegate的属性是assign不是retain?
循环引用
所有的引用计数系统,都存在循环应用的问题。例如下面的引用关系:
• 对象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模式时,也要注意这点。
委托的interface:声明一个某协议的属性delagate用assgin属性,原因是,为了避免循环引用。
定义属性时,什么情况使用copy、assign、retain?
assign用于简单数据类型,如NSInteger,double,bool,
retain和copy用于对象,
copy用于当a指向一个对象,b也想指向同样的对象的时候,如果用assign,a如果释放,再调用b会crash,如果用copy 的方式,a和b各自有自己的内存,就可以解决这个问题。
retain 会使计数器加一,也可以解决assign的问题。
另外:atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。
加了atomic,setter函数会变成下面这样:
if (property != newValue) {
[property release];
property = [newValue retain];
}
对象是什么时候被release的?
引用计数为0时。
autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了当前的Autoreleasepool中,当该pool被释放时,该pool中的所有Object会被调用Release。对于每一个Runloop, 系统会隐式创建一个Autoreleasepool,这样所有的release pool会构成一个象CallStack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的Autoreleasepool会被销毁,这样这个pool里的每个Object(就是autorelease的对象)会被release。那什么是一个Runloop呢? 一个UI事件,Timercall, delegate call, 都会是一个新的Runloop
readwrite,readonly,assign,retain,copy,nonatomic 、strong、weak属性的作用?并区别
strong(强引用)、 weak(弱引用)?什么情况使用copy,assign,和retain?
readwrite 是可读可写特性;需要生成getter方法和setter方法时
readonly 是只读特性 只会生成getter方法 不会生成setter方法 ;不希望属性在类外改变
assign 是赋值特性,setter方法将传入参数赋值给实例变量;仅设置变量时;
retain 表示持有特性,setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1;
copy 表示赋值特性,setter方法将传入对象复制一份;需要完全一份新的变量时。
nonatomic 非原子操作,决定编译器生成的setter getter是否是原子操作,atomic表示多线程安全,一般使用 nonatomic
assign用于简单数据类型,如NSInteger,double,bool。
retain 和copy用户对象,copy用于当 a指向一个对象,b也想指向同样的对象的时候,如果用assign,a如果释放,再 调用b会crash,如果用copy 的方式,a和b各自有自己的内存,就可以解决这个问题。retain 会使计数器加1,也可以解 决assign的问题。另外:atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。 在多线程环境 下,原子操作是必要的,否则有可能引起错误的结果。
为什么很多内置类如UITableViewController的delegate属性都是assign而不是retain的? 会引起循环引用----若是retain,在alloc一次之后,若release一次,会导致内训泄漏,若release两次会导致两个 对象的dealloc嵌套执行,结果就是都没有执行成功,最后崩溃! 所有的引用计数系统,都存在循环应用的问题。例如下面的引用关系:
* 对象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模式时,也要注意这点。
ObjC中,与retain配对使用的方法是dealloc还是release,为什么?需要与alloc配对使用的方法是dealloc还
是release,为什么?
与retain配对使用的方法是release,因为retain使retainCount计数加1,release使retainCount计数减1;与 retain语义相反的是release。 与alloc配对使用的是release,因为:alloc是使retainCount计数加1,,使retainCount计数减1。与alloc语义相反
的是dealloc,因为:alloc是创建一个对象,,dealloc是销毁一个对象。
16、重写一个NSStrng类型的,retain方式声明name属性的setter和getter方法 { NSString * _name; } @property NSString *name;----加上这些是为了避免错误
setter方法:
-(void)setName:(NSString*)name
{
{[_name release];
_name=[name retain];
}
}
分别描述内存管理要点、autorelease、release、NSAutoreleasePool?并说明autorelease是什 么时候被release的?简述什么时候由你负责释放对象,什么时候不由你释放?
[NSAutoreleasePool release]
和[NSAutoreleasePool drain]有什么区别?
内存管理要点:
Objective-C 使用引用计数机制(retainCount)来管理内存。内存每被引用一次,该内存的引用计数+1,每被释放一次引 用计数-1。当引用计数 = 0 的时候,调用该对象的 dealloc 方法,来彻底从内存中删除该对象。 alloc,allocWithZone,new(带初始化)时:该对象引用计数 +1;
retain:手动为该对象引用计数 +1;
copy:对象引用计数 +1;
mutableCopy:生成一个新对象,新对象引用计数为 1;
release:手动为该对象引用计数 -1; autorelease:把该对象放入自动释放池,当自动释放池释放时,其内的对象引用计数 -1。
NSAutoreleasePool: NSAutoreleasePool是通过接收对象向它发送的autorelease消息,记录该对象的release消息,当自动释放池被销毁 时,会自动向池中的对象发送release消息。
autorelease 是在自动释放池被销毁,向池中的对象发送release
只能释放自己拥有的对象, 区别是:在引用计数环境下(在不使用ARC情况下),两者基本一样,在GC环境下,release 是一个no-op(无效操 作),所以无论是不是gc都使用drain
内存管理 Autorelease、retain、copy、assign的set方法和含义?
1,你初始化(alloc/init)的对象,你需要释放(release)它。例如:
NSMutableArray aArray = [[NSArray alloc] init];
后,需要
[aArray release];
2,你retain或copy的,你需要释放它。例如:
[aArray retain]
后,需要
[aArray release];
3,被传递(assign)的对象,你需要斟酌的retain和release。例如:
obj2 = [[obj1 someMethod] autorelease];
对象2接收对象1的一个自动释放的值,或传递一个基本数据类型(NSInteger,NSString)时: 你或希望将对象2进行retain,以防止它在被使用之前就被自动释放掉。但是在retain后,一定要在适当的时候进行释放。
关于索引计数(Reference Counting)的问题
retain值 = 索引计数(ReferenceCounting)
NSArray对象会retain(retain值加一)任何数组中的对象。当NSArray被卸载(dealloc)的时候,所有数组中的对象会被执行一次释放(retain值减一)。不仅仅是NSArray,任何收集类(CollectionClasses)都执行类似操作。例如NSDictionary,甚至UINavigationController。
Alloc/init建立的对象,索引计数为1。无需将其再次retain。
[NSArray array]和[NSDate date]等“方法”建立一个索引计数为1的对象,但是也是一个自动释放对象。所以是本地临时对象,那么无所谓了。如果是打算在全Class中使用的变量(iVar),则必须retain它。
缺省的类方法返回值都被执行了“自动释放”方法。(*如上中的NSArray)
在类中的卸载方法“dealloc”中,release所有未被平衡的NS对象。(*所有未被autorelease,而retain值为1的)
===============================================================
4.RunLoop
RunLoop就是一个消息传送机制,用于异步的或线程内部的通信,它提供了一套机制来处理系统的输入源(像 socekts,ports,files,keyboard,mouse,定时器等),你可以把它想象成是一个邮局,在等待信件和传递信件给收件人,每个 NSThread都有属于它自己的RunLoop。
RunLoop可以用来区分交互式App和命令行。命令行通过一些参数运行后,执行完他们的程序,这个程序结束了,就像“Hello world”, 只要打印出“Hello world”,这个程序也就结束了。但是交互式的App就会一直等待用户输入,然后做出反应,然后再等待用户输入,知道某一个触发条件发生,才会退出程 序。
所以从上面的讲述就可以知道,RunLoop的工作实际上就是在等待触发事件的发生。这些触发事件可以是外部的事件,比如用户的一些行为或者像网络 请求,又或者像App内部的信息,比如线程内部的通知,异步代码的执行,定时器等等,一旦一个触发事件发生,并且RunLoop接受到这个信息,它就会去 寻找相关的收信人,并把信息发送给这个收信人。
RunLoop的时间线
-
通知观察者RunLoop已经被启动
-
通知观察者一些定时器已经准备开始
-
通知观察者一些不是基于端口的输入源准备开始
-
启动那些已经准备好的不是基于端口的输入源
-
如果一个基于端口的输入源已经准备好,正等待被启动,那么就会马上启动这个输入源。进入第9步。
-
通知观察者这个线程准备休眠。
-
把这个线程变成休眠状态直到下面一个事件发生:
-
-
一个事件到达了一个基于端口的源
-
一个定时器启动
-
RunLoop设置的时间已经到时
-
该RunLoop被唤醒
-
通知观察者该线程被唤醒
-
处理等待事件
-
-
通知观察者RunLoop退出。
===============================================================
Foundation 框架 中的传递机制 delegation,block 和 target-action
Foundation 框架外的 (KVO 和 通知) KVC KVO
KVO(Key-Value-Observing)键-值-监看
KVC(Key-Value-Coding)键-值-编码
Key-Value Coding(KVC)实现分析
KVC运用了一个isa-swizzling技术。isa-swizzling就是类型混合指针机制。KVC主要通过isa-swizzling,来实现其内部查找定位的。isa指针,如其名称所指,(就是is a kind of的意思),指向维护分发表的对象的类。该分发表实际上包含了指向实现类中的方法的指针,和其它数据。
比如说如下的一行KVC的代码:
- [site setValue:@"sitename" forKey:@"name"];
就会被编译器处理成:
- SEL sel = sel_get_uid ("setValue:forKey:");
- IMP method = objc_msg_lookup (site-]] > isa,sel);
- method(site, sel, @"sitename", @"name");
首先介绍两个基本概念:
(1)SEL数据类型:它是编译器运行Objective-C里的方法的环境参数。
(2)IMP数据类型:他其实就是一个 编译器内部实现时候的函数指针。当Objective-C编译器去处理实现一个方法的时候,就会指向一个IMP对象,这个对象是C语言表述的类型(事实上,在Objective-C的编译器处理的时候,基本上都是C语言的)。
这下KVC内部的实现就很清楚的清楚了:一个对象在调用setValue的时候,(1)首先根据方法名找到运行方法的时候所需要的环境参数。(2)他会从自己isa指针结合环境参数,找到具体的方法实现的接口。(3)再直接查找得来的具体的方法实现。
Key-Value Observing(KVO)实现
在上面所介绍的KVC机制上加上KVO的自动观察消息通知机制就水到渠成了。
当观察者为一个对象的属性进行了注册,被观察对象的isa指针被修改的时候,isa指针就会指向一个中间类,而不是真实的类。所以isa指针其实不需要指向实例对象真实的类。所以我们的程序最好不要依赖于isa指针。在调用类的方法的时候,最好要明确对象实例的类名。
熟悉KVO的朋友都知道,只有当我们调用KVC去访问key值的时候KVO才会起作用。所以肯定确定的是,KVO是基于KVC实现的。其实看了上面我们的分析以后,关系KVO的架构的构思也就水到渠成了。
因为KVC的实现机制,可以很容易看到某个KVC操作的Key,而后也很容易的跟观察者注册表中的Key进行匹对。假如访问的Key是被观察的Key,那么我们在内部就可以很容易的到观察者注册表中去找到观察者对象,而后给他发送消息。
KVC和KVO的使用原则:
一、KVC(key-value-coding)
1、只针对类属性,设置键值对
2、设置setValue: forKey:,即forKey只能为类属性
3、取值valueForKey
二、KVO(key-value-observing)被观察者的属性发生改变时,通知观察者
1、利用KVC对类属性进行设置
2、注册observing对象addObserver:forKeyPath:options:context:
3、观察者类必须重写方法 observeValueForKeyPath:ofObject:change:context:
4、应用,MVC模型中,数据库(dataModal)发生变化时,引起view改变,用这种方式实现非常方便
KVC:
优点:
1.设置值,会查找属性的getter(valueForKey)和setter(setValueForKey)方法,然后查找其名字的实例变量。
2.支持指定键路径,就像文件系统的路径一样。比如:car.emgine.horespower
3.整体操作,比如:给一个汽车的轮子发送kvc消息,由于汽车有4个轮子,调用代码(valueForKeyPath:@“tires.pressure”)后会返回一个数组(包含4个数据)。
4.快速运算:可以引用一些运算符来进行一些计算。如:cars.@count,cars.@avg.mileage (cars.用来获取cars的属性,@count中@符号代表着后面将进行一些运算,@count用于通知kvc机制计算键路径左侧值的对象总数。同理,后者是计算所有对象的平均行驶距离)。 但是你还不能添加自己的运算符。
5.批处理:有两个调用可以为对象进行批量处理。第一个调用为:dictionaryWithValueForKey。第二个调用为:setValueForKeyWithDictinary
6.nil仍然可用:重写setNilValueForKey方法
7.处理未定义的键:重写valueForUndefinedKey方法
缺点:
KVC需要解析字符串来计算你需要做的事情,速度会比较慢,此外,编译器还无法对字符串进行错误检查,无法判断错误的键路径。因此,当你使用时,可能会出现运行时错误。
当通过KVC调用对象时,比如:[self valueForKey:@”someKey”]时,程序会自动试图通过几种不同的方式解析这个调用。首先查找对象是否带有 someKey 这个方法,
如果没找到,会继续查找对象是否带有someKey这个实例变量(iVar),
如果还没有找到,程序会继续试图调用 -(id)valueForUndefinedKey:这个方法。
如果这个方法还是没有被实现的话,程序会抛出一个NSUndefinedKeyException异常错误。
字典类其实装载的是键值关系,obc里面,NSObject有个function叫做hash,字典类在装载的时候,都会对每个对象取key值
(Key-Value Coding查找方法的时候,不仅仅会查找someKey这个方法,还会查找getsomeKey这个方法,前面加一个get,或者_someKey以及_getsomeKey这几种形式。同时,查找实例变量的时候也会不仅仅查找someKey这个变量,也会查找_someKey这个变量是否存在。)
设计valueForUndefinedKey:方法的主要目的是当你使用-(id)valueForKey方法从对象中请求值时,对象能够在错误发生前,有最后的机会响应这个请求。
简述NotificationCenter、KVC、KVO、Delegate?并说明它们之间的区别???
Notification 是观察者模式的实现,KVO是观察者模式的OB-C底层实现。
NOtification 通过 Notifydcation addobserver 和 remove observer 工作。
KVO是键值监听,键值观察机制,提供了观察某一属性变化的方法 KVC是键值编码,是一种间接访问对象的属性,使用字符串来标示属性(例如:setValue:forKey:) Delegate:把某个对象要做的事情委托给别的对象去做。那么别的对象就是这个对象的代理,代替它来打理要做的 事。反映到程序中,首先要明确一个对象的委托方是哪个对象,委托所做的内容是什么。
区别:
委托代理(delegate),顾名思义,把某个对象要做的事情委托给别的对象去做。那么别的对象就是这个对象的代理,代替它来打理要做的事。反映到程序中,首先要明确一个对象的委托方是哪个对象,委托所做的内容是什么。
委托机制是一种设计模式,在很多语言中都用到的,这只是个通用的思想,网上会有很多关于这方面的介绍。
那么在苹果开发过程中,用到委托的程序实现思想如下,我主要拿如何在视图之间传输信息做个例子。
譬如:在两个页面(UIIview视图对象)实现传值,用委托(delegate)可以很好做到!
方法:
类A
@interface A:UIView
id transparendValueDelegate;
@property(nomatic, retain) idtransparendValueDelegate;
@end
@implemtion A
@synthesize transparendValueDelegate
-(void)Function
{
NSString* value = @"hello";
//让代理对象执行transparendValue动作
[transparendValueDelegate transparendValue:value];
}
@end
类B
@interface B:UIView
NSString* value;
@end
@implemtion B
-(void)transparendValue:(NSString*)fromValue
{
value = fromValue;
NSLog(@"the value is %@",value);
}
@end
//下面的设置A代理委托对象为B
//在定义A和B类对象处:
A* a = [[A alloc] init];
B* b = [[B alloc] init];
a. transparendValueDelegate = b;//设置对象a代理为对象b
这样在视图A和B之间可以通过委托来传值!
下面这个例子委托有两类:
1、一个视图类对象的代理对象为父视图,子视图用代理实现让父视图显示别的子视图
2、同一父视图下的一个子视图为另一个子视图的代理对象,让另一个子视图改变自身背景色为给定的颜色
===============================================
规范格式如下:
@protocol TransparendValueDelegate;
@interface A:UIView
id< TransparendValueDelegate > m_dTransparendValueDelegate;
@property(nomatic, retain) id m_dTransparendValueDelegate;
@end
//代理协议的声明
@protocol TransparendValueDelegat<NSObject>
{
-(void)transparendValue:(NSString*)fromValue;
}
block的运用和传值
Block的内存:
block的三种类型:NSGlobalBlock,NSStackBlock,NSMallocBlock
GlobalBlock呢?因为它不需要运行时(Runtime)任何的状态来改变行为,不需要放在堆上或者栈上,直接编译后在代码段中即可,就像个c函数一样。这种类型的Block在ARC和non-ARC情况下没有差别。
StackBlock呢?这个Block访问了作用域外的变量d,在实现上就是这个block会多一个成员变量对应这个d,在赋值block时会将方法exmpale中的d变量值复制到成员变量中,从而实现访问。由于局部变量d和这个block的实现不在同一作用域,仅仅在调用过程中用到了值传递,所以不能直接修改。而需要加一个标识符__block;,这样对Block外的变量访问从值传递转变为引用,从而有了修改内容的能力。那么block就可以实现对这个局部变量的修改了。
MallocBlock呢?正常我们使用Block是在栈上生成的,离开了栈作用域便释放了,如果copy一个Block,那么会将这个Block copy到堆上分配,这样就不再受栈的限制,可以随意使用啦。
Block一开始是在栈上分配的,属于NSStackBlock,如果是non-ARC情况下return这个NSStackBlock,那么其实已经被销毁了,在函数中使用时就会crash。如果是ARC情况下,getBlock返回的block会自动copy到堆上,那么block的类型就是NSMallocBlock,可以在example()中继续使用。所以,如果是non-ARC时,要写明[block copy]。
Block的循环引用问题:
retain cycle问题的根源在于Block和obj可能会互相强引用,互相retain对方,这样就导致了retain cycle,最后这个Block和obj就变成了孤岛,谁也释放不了谁。
在Block中虽然没直接使用self,但使用了成员变量。在Block中使用成员变量,retain的不是这个变量,而会retain self。
注意:MRC中__block是不会引起retain;但在ARC中__block则会引起retain。ARC中应该使用__weak或__unsafe_unretained弱引用。__weak只能在iOS5以后使用。
开发者必须要注意对象和Block的生命周期。
__block不要乱用:
将Block作为参数传给dispatch_async时,系统会将Block拷贝到堆上,如果Block中使用了实例变量,还将retain self,因为dispatch_async并不知道self会在什么时候被释放,为了确保系统调度执行Block中的任务时self没有被意外释放掉,dispatch_async必须自己retain一次self,任务完成后再release self。但这里使用__block,使dispatch_async没有增加self的引用计数,这使得在系统在调度执行Block之前,self可能已被销毁,但系统并不知道这个情况,导致Block被调度执行时self已经被释放导致crash
BLOCK用处:
1.回调传值 2.作为方法的参数,在block里面定义任意的代码段。
5.单例,代理,观察者模式,mvc框架
单例:单例模式的意思就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
1.单例模式的要点:
单例设计模式确保这个类仅仅拥有一个实例,并且为这个实例提供一个全局的访问点。
apple用了很多的单例:[NSUserDefaults
standardUserDefaults],[UIApplication sharedApplication], [UIScreen mainScreen], [NSFileManager defaultManager]他们都是返回一个单例对象。
2.单例模式的优点:
1.实例控制:Singleton 会阻止其他对象实例化其自己的 Singleton 对象的副本,从而确保所有对象都访问唯一实例。
2.灵活性:因为类控制了实例化过程,所以类可以更加灵活修改实例化过程
IOS中的单例模式
在objective-c中要实现一个单例类,至少需要做以下3个步骤:
1:定义一个静态变量来保存你类的实例确保在你的类里面保持全局。
2:定义一个静态的dispatch_once_t变量来确保这个初始化存在一次。
3:用GCD来执行block初始化libraryAPI实例。这是单例设计模式的本质。这个初始化不在被调用这个类已经被初始化。并且是线程安全的。
代理:是一个代表或者协调另一个对象的行为机制。在Objective-c实现代理的设计模式。一个类可以定义可选或者必须的方法通过协议。这是一个重要的设计模式。苹果在UIKit类里面用到了很多。 UITableView,UITextView, UITextField, UIWebView, UIAlert, UIActionSheet, UICollectionView, UIPickerView,UIGestureRecognizer, UIScrollView.
完整的单例:
1、为单例对象实现一个静态实例,并初始化,然后设置成nil,
2、实现一个实例构造方法检查上面声明的静态实例是否为nil,如果是则新建并返回一个本类的实例,
3、重写allocWithZone方法,用来保证其他人直接使用alloc和init试图获得一个新实力的时候不产生一个新实例,
4、适当实现allocWitheZone,copyWithZone,release和autorelease。
观察者:在观察者设计模式里面,一个对象通知其他的对象一些状态的改变。涉及这些对象不需要知道另一个对象---因此鼓励解耦设计模式。这个设计模式经常被用来通知感兴趣的对象当一个属性被改变时候。
通常实现需要一个观察者注册另一个对象感兴趣的状态。当状态改变,所有的观察者对象被通知改变了。苹果的远程通知服务就是一个全球性的例子。
cocoa实现观察者有两个相似的方法:通知和键值观察:
通知:不要被本地通知和远程通知迷惑,通知是根据订阅和通知的模式允许一个对象(通知者)发送消息给另一些对象(订阅者也就是监听者)。这个通知者不需要知道订阅者的任何信息。
苹果公司大量的使用通知,例如当键盘隐藏时候系统发送一个UIKeyboardWillShowNotification/UIKeyboardWillHideNotification通知。当你的应用进入后台系统发送一个UIApplicationDidEnterBackgroundNotification 通知。
键值观察模式:在KVO,一个对象可以要求被通知当他的某个特殊的属性被改变了。
注意:记得要删除你的观察者们当他们收回,否则你的应用程序会崩溃当系统试图将消息发送到这些不存在的观察者!
Block的作用:
同 代理-协议、通知一样用于对象和对象之间的通信。
block的定义:匿名函数
是一个匿名的函数代码块,此代码块可以作为参数传递给其他对象。
block的应用场景:
对象与对象之间的通信。
block的语法:
block的声明:
第一种:
int (^ myBlock)(int);
第二种:
typedef int (^MyBlock) (int);
block的创建:
MyBlock myblock = ^(int a){
NSLog(@"参数:%d",a);
return 10;
};
block的调用:
int ret = myblock(20);
第三种:
block作为参数:
Myblock myblock = ^(int a)
{
NSLog(@"这是block代码块,a = %d",a);
return 10;
};
[self testBlock:myblock];
-(void) testBlock:(Myblock) myblock
{
//可能有一些逻辑判断
//block回调
myblock(50);
}
block引用局部变量:将局部变量看成是常量:
__block int number = 10;
Myblock myblock2 = ^(int a)
{
number = 20;
number++;
NSLog(@"%d",number);
return 10;
};
block的内存管理:
在block里面引用一个局部的objective-c对象的时候,该对象会被retain
如果局部变量使用_block修饰,则不会被retain。
//引用计数为1
NSObject *obj = [[NSObject alloc] init];
Myblock *myBlcok = ^(int a)
{
//引用计数为2
NSLog(@"引用计数:%d",obj.retainCount);
return 10;
};
//引用计数1,block引用该变量时不会将它retain
__block NSObject *obj = [[NSObject alloc] init];
在block里面引用一个实例变量时,该实例对象会被retain
NSLog(@"计数:%d",self.retainCount);
[self objectMethod:^{
//引用了全局变量number
int value = number;
//引用计数2
NSLog(@"计数:%d",self.retainCount);
return a +b;
}];
block本身可以就像对象一样copy和release。
block创建后内存是分配在栈上,调用copy方法,会将block从栈移到堆上。
当block声明为全局变量时,我们应该调用block的copy方法。
两种copy方式:
[_block copy];
Block_copy(_block);
两种release方式:
[_block release];
iphoneSDK中哪里用到的block? (UIView动画)
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations ;
===============================================================
6.iOS程序生命周期
iOS程序运行流程{
1. 系统调用app的main函数
2. main函数调用UIApplicationMain.
3. UIApplicationMain创建sharedapplication instance, UIApplication默认的instance.
4. UIApplicationMain读取Info.plist找到主nib文件, 加载nib,把shared applicationinstance 设为nib的owner.
5. 通过nib文件,创建app的独立UIWindows object.
6. 通过nib,实例化了程序的AppDelegate object.
7. app内部启动结束,application:didFinishLaunchingWith-Options: 被设定成 wAppDelegate instance.
8. AppDelegate向UIWindowinstance发makeKeyAndVisible消息, app界面展示给用户. app准备好接收用户的操作指令.
}
程序到底是怎么执行的?
1. 当你单击iPhone上的应用程序图标的时候,系统就会查找这个程序的入口地址,即main函数在内存代码区的首地址.然后执行main函数里的程序,这里main函数里仅有一句: return UIApplicationMain(argc, argv, nil, NSStringFromClass([ZHAppDelegate class]));
2. 上面的这句话有什么作用呢?他会创建一个单例的application对象,这在系统中表征这个程序的存在,生命周期;建立事件循环,如不断的检查程序的运行状态,是否被触摸,摇晃等;还有就是建立一个application的delegate,delegate的作用是根据应用程序的状态并作出相应的处理,比如程序启动了,进入活跃状态,进入非活跃状态,将要进入前台,将要进入后台,程序将要结束等消息.这是主程序传递过来的又delegate代理完成的,因为主程序的侧重点不在此(猜测),
3. 那么这个代理的具体都做些什么事呢?在程序加载的时候,主程序会根据不同的情况给代理发送相应的消息,处理上面代理完成的辅助功能外,最总要的时delegate会创建一个window,这个window像个画板一样把视图呈现给用户.如果我们使用mvc模式设计程序,那么我们会把window的管理权交给一个叫做controler(管理器),也就是mvc中的c来管理.controler的作用是用来往window上加载视图,这里我们可以猜测,controller原则上可以有很多个,哪个controler拥有了对window的管理权,谁就可以往window上加载视图.
4. Controller的都有什么作用或者controler是怎么工作的呢?加载视图的时候首先会初始化函数(或者实例化)一个controler类的对象,由它来调用自己所在类的方法管理视图,controller所在的类都有哪些方法呢?最典型的方法有loadView方法(加载视图). viewDidLoad(视图加载完成), didReceiveMemoryWarning(内存警告)等.
===========================================================
7.MVC设计模式MVVM
怎么理解MVC,在Cocoa中MVC是怎么实现的?
MVC设计模式考虑三种对象:模型对象、视图对象、和控制器对象。模型对象代表特别的知识和专业技能,它们负责保有应用程序的数据和定义操作数据的逻辑。视图对象知道如何显示应用程序的模型数据,而且可能允许用户对其进行编辑。控制器对象是应用程序的视图对象和模型对象之间的协调者。
谈谈你对MVC的理解?为什么要用MVC?在Cocoa中MVC是怎么实现的?你还熟悉其他的OC设计模式或别 的设计模式吗?
MVC就是Model-View-Controller的缩写,M指的是业务模型,V指的是用户页面,C指的是控制器。MVC是架构模式,是讲M和 V的代码分离,从而使同那个一个程序可以使用不同的表现形式。 M:表示数据和业务规则,V是用户看到的并与之交互的页面,C是接受用户的输入并调用M和V取完成用户需求的 单例,代理,观察者,工厂模式等 单例模式:说白了就是一个类不通过alloc方式创建对象,而是用一个静态方法返回这个类的对象。系统只需要拥有一个的 全局对象,这样有利于我们协调系统整体的行为; 代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用.比如一个工厂生产了产品,并不想 直接卖给用户,而是搞了很多代理商,用户可以直接找代理商买东西,代理商从工厂进货.常见的如QQ的自动回复就属于代 理拦截,代理模式在iphone中得到广泛应用.
观察者模式: 当一个物体发生变化时,会通知所有观察这个物体的观察者让其做出反应。实现起来无非就是把所有观察者的 对象给这个物体,当这个物体的发生改变,就会调用遍历所有观察者的对象调用观察者的方法从而达到通知观察者的目的
MVC(Model-View-Controller)应用程序结构被用来分析分布式应用程序的特征。这种抽象结构能有助于将应用程序分割成若干逻辑部件,使程序设计变得更加容易。
MVC结构提供了一种按功能对各种对象进行分割的方法(这些对象是用来维护和表现数据的),其目的是为了将各对象间的耦合程度减至最小。MVC结构本来是为了将传统的输入(input)、处理(processing)、输出(output)任务运用到图形化用户交互模型中而设计的。但是,将这些概念运用于基于Web的企业级多层应用领域也是很适合的。
在MVC结构中,模型(Model)代表应用程序的数据(data)和用于控制访问和修改这些数据的业务规则(business rule)。通常模型被用来作为对现实世界中一个处理过程的软件近似,当定义一个模型时,可以采用一般的简单的建模技术。
当模型发生改变时,它会通知视(View),并且为视提供查询模型相关状态的能力。同时,它也为控制器(Controller)提供访问封装在模型内部的应用程序功能的能力。
一个视(View)用来组织模型的内容。它从模型那里获得数据并指定这些数据如何表现。当模型变化时,视负责维持数据表现的一致性。视同时将用户要求告知控制器(Controller)。
控制器(Controller)定义了应用程序的行为;它负责对来自视的用户要求进行解释,并把这些要求映射成相应的行为,这些行为由模型负责实现。在独立运行的GUI客户端,用户要求可能是一些鼠标单击或是菜单选择操作。在一个Web应用程序中,它们的表现形式可能是一些来自客户端的GET或POST的HTTP请求。模型所实现的行为包括处理业务和修改模型的状态。根据用户要求和模型行为的结果,控制器选择一个视作为对用户请求的应答。通常一组相关功能集对应一个控制器。下图描述了一个MVC应用程序中模型、视、控制器三部分的关系
===========================================================
8.UIView CALayer frame与bounds
frame 和 bounds 的区别 ,bound的大小改变frame 改变吗?
frame:该view在父view坐标系统中的位置和大小。(参照点是,父亲的坐标系统)
bounds:该view在本地坐标系统中的位置和大小。(参照点是,本地坐标系统)
简述你对UIView、UIWindow和CALayer的理解 UIView:属于UIkit.framework框架,负责渲染矩形区域的内容,为矩形区域添加动画,响应区域的触摸事件,布局和管 理一个或多个子视图 UIWindow:属于UIKit.framework框架,是一种特殊的UIView,通常在一个程序中只会有一个UIWindow,但可以手 动创建多个UIWindow,同时加到程序里面。UIWindow在程序中主要起到三个作用:
1、作为容器,包含app所要显示的所有视图
2、传递触摸消息到程序中view和其他对象
3、与UIViewController协同工作,方便完成设备方向旋转的支持
CAlayer:属于QuartzCore.framework,是用来绘制内容的,对内容进行动画处理依赖与UIView来进行显示,不能处 理用户事件。UIView和CALayer是相互依赖的,UIView依赖CALayer提供内容,CALayer依赖UIView一共容器显示 绘制内容。
延伸: UIViewController:管理视图的几成熟,每个视图控制器都有一个自带的视图,并且负责这个视图相关的一切事务。方便 管理视图中的子视图,负责model与view的通信;检测设备旋转以及内存警告;是所有视图控制类的积累,定义了控制器 的基本功能。
drawRect和layoutSubviews的区别
两个方法都是异步执行的,layoutSubviews方便数据计算,drawRect方便视图重绘。 layoutSubviews对subviews重新布局
layoutSubviews方法调用先于drawRect
在 UIView 中有一个autoresizingMask的属性,它对应的是一个枚举的值(如下),属性的意思就是自动调整子控件与父控件中间的位置,宽高。
enum {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
UIViewAutoresizingNone就是不自动调整。
UIViewAutoresizingFlexibleLeftMargin 自动调整与superView左边的距离,保证与superView右边的距离不变。
UIViewAutoresizingFlexibleRightMargin 自动调整与superView的右边距离,保证与superView左边的距离不变。
UIViewAutoresizingFlexibleTopMargin 自动调整与superView顶部的距离,保证与superView底部的距离不变。
UIViewAutoresizingFlexibleBottomMargin 自动调整与superView底部的距离,也就是说,与superView顶部的距离不变。
UIViewAutoresizingFlexibleWidth 自动调整自己的宽度,保证与superView左边和右边的距离不变。
UIViewAutoresizingFlexibleHeight 自动调整自己的高度,保证与superView顶部和底部的距离不变。
UIViewAutoresizingFlexibleLeftMargin |UIViewAutoresizingFlexibleRightMargin 自动调整与superView左边的距离,保证与左边的距离和右边的距离和原来距左边和右边的距离的比例不变。比如原来距离为20,30,调整后的距离应为68,102,即68/20=102/30。
===========================================================
9.根类 NSObject
NSObject是大部分Objective-C类的根类,它没有父类。其它类继承NSObject,访问Objective-C运行时系统的基本接口,这样其他类的实例可以获得运行时的能力。
根类和根类协议
NSObject不但是个类名,NSObject也是个协议的名称,
参考NSObject协议 , NSObject协议指定了根类必须实现的接口。
根类的主要方法:
• 分配、初始化、和复制:
alloc和allocWithZone:方法用于从某内存区域中分配一个对象内存,并使对象指向其运行时的类定义。
init方法是对象初始化。
new是一个将简单的内存分配和初始化结合起来的方法。
copy和copyWithZone:
•对象的保持和清理:
retain方法增加对象的保持次数。
release方法减少对象的保持次数。
autorelease方法也是减少对象的保持次数,但是以推迟的方式。
retainCount方法返回对当前的保持次数。
dealloc方法由需要释放对象的实例变量以及释放动态分配的内存的类实现。
转码和解码
下面的方法和对象的编解码(作为归档过程的一部分)有关:
encodeWithCoder:和initWithCoder:是NSCoding协议仅有的方法。前者使对象可以对其实例变量进行编码,后者则使对象可以根据解码过的实例变量对自身进行初始化。
NSObject类中声明了一些于对象编码有关的方法:classForCoder:、replacementObjectForCoder:、和awakeAfterUsingCoder:。
•消息的转发
forwardInvocation:允许一个对象将消息转发给另一个对象。
•消息的派发
在performSelector开头的一些方法允许你延迟后派发指定消息,而且可以将消息(同步或异步的消息)从辅助线程派发到主线程。
。
===========================================================
10.多线程:
l 线程(线程)用于指代独立执行的代码段。
l 进程(process)用于指代一个正在运行的可执行程序,它可以包含多个线程。
l 任务(task)用于指代抽象的概念,表示需要执行工作。
我们可以这样理解:我们可以在电脑上开启多个QQ应用。每个QQ应用可以同时打开很多聊天的窗口。这里每个QQ是一个进程,然后每个QQ聊天窗口是一个QQ应用的一个线程。
这三种编程方式从上到下,抽象度层次是从低到高,抽象程度越高的使用越简单,也是Apple最推荐使用的。
三种方式的优缺点:
NSThread
优点:NSThread比其他两个轻量级
缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销。
Cocoa operation
优点:不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上。Cocoa operation相关的类是NSOperation , NSOperationQueue。 NSOperation是个抽象类,使用它必须使用它的子类,可以实现它或者使用它定义好的两个子类:NSInvocationOperation 和 NSBlockOperation 。 创建NSoperation子类的对象,把对象添加到NSoperationQueue队列里执行。
GCD
这是Apple开发的一个多核编程的解决办法。GCD是一个替代诸如NSThread , NSOperationQueue , NSinvocationOperation等技术的很高效和强大的技术。
1:首先多线程技术是耗费资源的比如系统的内存和cpu的占用
2:多线程可能造成两个线程同时访问一块内存来造成资源争夺
3:刚才也说了多线程耗费宝贵的资源,你应该保证你多线程里面执行的操作运行时间长而且有效率。我们还得中断那些空闲的线程。这样有能降低我们应用的内存占用,来腾出内存给其他的应用。
4:线程之间的通信,有时候辅助线程可能要通知主线程做的工作怎么样了。这时候就用到了线程通信的技术。
5:线程的退出,一个线程执行完他的任务自然退出是最好的选择。但是有时候你强制退出一个线程会造成一些内存泄漏等潜在的安全问题。
6:如果你的一个线程突然因为什么原因抛出一个异常。你需要捕获异常,因为同一个进程的线程不能捕获其他线程的异常信息。我们必须把异常情况一个NSException对象从一个线程传递到另一个线程,向这个线程报告发生了什么,这样引发异常的线程可以继续执行(如果有可能),或者干脆退出。
7:线程浪费宝贵的资源我们需要提高他的效率需要专门一个run loop来处理要执行的事件,有时候还需要一个自动释放池来管理创建的一些临时对象。
Grand Central Dispatch(
GCD):
系统管理线程,你不需要编写线程代码。只需定义想要执行的任务,然后添加到适当的 dispatch queue。 GCD会负责创建线程和调度你的任务。系统直接提供线程管理,比应用实现更加高效。
GCD,全称
Grand Central Dispath,是苹果开发的一种支持并行操作的机制。它的主要部件是一个FIFO队列和一个线程池,前者用来添加任务,后者用来执行任务。
GCD中的FIFO队列称为dispatch queue,它可以保证先进来的任务先得到执行(但不保证一定先执行结束)。
通过与线程池的配合,dispatch queue分为下面两种:
• Serial Dispatch Queue -- 线程池只提供一个线程用来执行任务,所以后一个任务必须等到前一个任务执行结束才能开始。
• Concurrent Dispatch Queue -- 线程池提供多个线程来执行任务,所以可以按序启动多个任务并发执行。
1. Basic Management
我们可以通过dispatch_queue_cretae来创建队列,然后用dispatch_release释放。比如下面两段代码分别创建串行队列和并行队列:
dispatch_queue_t serialQ = dispatch_queue_create("eg.gcd.SerialQueue", DISPATCH_QUEUE_SERIAL);
1.dispatch_async(serialQ, ^{
2. // Code here
3.});
4.dispatch_release(serialQ);
dispatch_queue_t concurrentQ = dispatch_queue_create("eg.gcd.ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
1.dispatch_async(concurrentQ, ^{
2. // Code here
3.});
4.dispatch_release(concurrentQ);
而系统默认就有一个串行队列main_queue和并行队列global_queue:
1.dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2.dispatch_queue_t mainQ = dispatch_get_main_queue();
2. Normal Control
•dispatch_once
它可以保证整个应用程序生命周期中某段代码只被执行一次!
•dispatch_after
有时候我们需要等个几秒钟然后做个动画或者给个提示,这时候可以用
dispatch_after这个函数
•dispatch_set_target_queue
通过
dispatch_set_target_queue函数可以设置一个dispatch queue的优先级,或者指定一个dispatch source相应的事件处理提交到哪个queue上。
dispatch_set_target_queue(serialQ, globalQ);
•dispatch_apply
执行某个代码片段若干次。
dispatch_apply(10, globalQ, ^(
size_t index) {}
•dispatch group
Dispatch Group机制允许我们监听一组任务是否完成。
•dispatch_barrier_async
通过
dispatch_barrier_async函数提交的任务会等它前面的任务执行结束才开始,然后它后面的任务必须等它执行完毕才能开始。
Operation Queue:
Objective-C对象,类似于 dispatch queue。你定义想要执行的任务,并添加任务到 operation queue,后者负责调度和执行这些任务。和 GCD一样, Operation Queue也管理了线程,更加高效
asynchronous functions:
系统的一些API接口给你提供了异步的功能自动支持并发功能。这些API也许用系统的机制和进程来创建自定义的线程来执行他们的任务并且返回结果给他们。一旦你设计应用,寻找提供异步功能的API接口来代替用同步放在自定义线程。
timers:你可以用timers在你的应用主线程来执行周期性的任务这时需要一个线程是太微不足道了。但是仍然要提供每隔一段时间的服务。
线程是进程的基本单位 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。 进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下 不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的 地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较 大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
简述多线程的作用以及什么地方会用到多线程?OC实现多线程的方法有哪些?
谈谈多线程安全问题的几种解决方案?何为线程同步,如何实现的?分线程回调主线程方法是什么,有什么作用?
(1)、多线程的作用:可以解决负载均衡问题,充分利用cpu资源 。为了提高CPU的使用率,采用多线程的方式去同时完 成几件事情而互不干扰,
(2)、大多情况下,要用到多线程的主要是需要处理大量的IO操作时或处理的情况需要花大量的时间等等,比如:读写文 件、视频图像的采集、处理、显示、保存等。 (3)、ios有三种主要方法:1、NSThread。2、NSOperation。3、GCD。 (4)解决方案:使用锁:锁是线程编程同步工具的基础。锁可以让你很容易保护代码中一大块区域以便你可以确保代码的正 确性。使用POSIX互斥锁;使用NSLock类;使用@synchronized指令等。
(5)回到主线程的方法: dispatch_async(dispatch_get_main_queue(), ^{ });
作用:主线程是显示UI界面,子线程多数是进行数据处理
==========================================================
11.数据持久化
OC中有哪些数据存储方式,各有什么区别?
四种存储方式: 1.NSUserDefaults,用于存储配置信息2.SQLite,用于存储查询需求较多的数据3.CoreData,用于规划应用中的对象4.使用基本对象类型定制的个性化缓存方案.
NSUserDefaults:对象中储存了系统中用户的配置信息,开发者可以通过这个实例对象对这些已有的信息进行修改,也 可以按照自己的需求创建新的配置项。
SQLite擅长处理的数据类型其实与NSUserDefaults差不多,也是基础类型的小数据,只是从组织形式上不同。开发者可 以以关系型数据库的方式组织数据,使用SQL DML来管理数据。一般来说应用中的格式化的文本类数据可以存放在数据库 中,尤其是类似聊天记录、Timeline等这些具有条件查询和排序需求的数据。
CoreData是一个管理方案,它的持久化可以通过SQLite、XML或二进制文件储存。它可以把整个应用中的对象建模并进 行自动化的管理。从归档文件还原模型时CoreData并不是一次性把整个模型中的所有数据都载入内存,而是根据运行时状 态,把被调用到的对象实例载入内存。框架会自动控制这个过程,从而达到控制内存消耗,避免浪费。 无论从设计原理还是使用方法上看,CoreData都比较复杂。因此,如果仅仅是考虑缓存数据这个需求,CoreData绝对不 是一个优选方案。CoreData的使用场景在于:整个应用使用CoreData规划,把应用内的数据通过CoreData建模,完全 基于CoreData架构应用。 使用基本对象类型定制的个性化缓存方案:从需求出发分析缓存数据有哪些要求:按Key查找,快速读取,写入不影响正常 操作,不浪费内存,支持归档。这些都是基本需求,那么再进一步或许还需要固定缓存项数量,支持队列缓存,缓存过期 等。
coredata和sqlite有无必然联系?coredata是一个关系型数据库吗? iOS中可以有四种持久化数据的方式: 属性列表、对象归档、SQLite3和Core Data coredata可以使你以图形界面的方式快速的定义app的数据模型,同时在你的代码中容易获取到它。coredata提供了基础结构去处 理常用的功能,例如保存,恢复,撤销和重做,允许你在app中继续创建新的任务。在使用coredata的时候,你不用安装额外的数据 库系统,因为coredata使用内置的sqlite数据库。coredata将你app的模型层放入到一组定义在内存中的数据对象。coredata会 追踪这些对象的改变,同时可以根据需要做相应的改变,例如用户执行撤销命令。当coredata在对你app数据的改变进行保存的时 候,core data会把这些数据归档,并永久性保存。
mac os x中sqlite库,它是一个轻量级功能强大的关系数据引擎,也很容易嵌入到应用程序。可以在多个平台使用,sqlite是一个轻 量级的嵌入式sql数据库编程。与coredata框架不同的是,sqlite是使用程序式的,sql的主要的API来直接操作数据表。
Core Data不是一个关系型数据库,也不是关系型数据库管理系统(RDBMS)。虽然Core Dta支持SQLite作为一种存储类型, 但它不能使用任意的SQLite数据库。Core Data在使用的过程种自己创建这个数据库。Core Data支持对一、对多的关系。
第一种:实用属性列表,第二种:对象归档,第三种:嵌入式数据库(SQLite3)
第四种:文件写入磁盘
首先是属性列表。nsuserdefaults位于属性列表的最顶层,沙盒写入技术,以及对象的归档,以及第三方的SQLlite,还有就是coredata。
首先让我们说说这个属性列表:它是存储相对较小数据量的简洁方式,并且耗费非常低的工作量。但是这个属性列表也是有限制的像刚才我说的存储数据量小的数据,还有就是一些自定义的对象是不能存储的,只能是支持序列化的对象,想字典,数据,字符串,对了 还是不能添加UI界面的东西。他们可以以两种不同的方式存储一个是XML,这个XML是一种可读性非常强的格式,另一个是内部专门有的2进制的格式,这种方式速度更快写,数据量更小些。正如我们所说的NSuerdefaults位于属性列表的顶层,因此我们可以轻松的读取并且存储数据。
NSUserDefaults类的使用和NSKeyedArchiver有很多类似之处,但是查看NSUserDefaults的定义可以看出,NSUserDefaults直接继承自NSObject而NSKeyedArchiver 继承自NSCoder。这意味着NSKeyedArchiver实际上是个归档持久化的类,也就可以使用NSCoder类的[encodeObject: (id)objv forKey:(NSString *)key]方法来对数据进行持久化存储
苹果的一个专门记忆模式的实现是归档。把一个对象转化为流便于保存和恢复不用向外部类暴露私有的属性。
声明专辑通过符合NSCoding协议是可以归档的。在专辑头文件写上符合NSCoding协议:@interface Album :
NSObject <NSCoding>
在实现文件里面,添加两个方法:
(void)encodeWithCoder:(
NSCoder *)aCoder
(id)initWithCoder:(
NSCoder *)aDecoder
Core Data是一种 稳定,功能全面的持久化工具,和之前的一些持久化 工具相比,他不需要对实体进行归档,也就是序列化,而是在数据 模型编辑器中创建一些实体
在代码中,你不再使用存取方法和修改方法,而是使用键值对编码来设置属性或者减缩他们的值
那么这些托管对象的活动区域在哪 ? 他们位于所谓的持久库中,默认情况下,Core Data应用程序将持久库实现为存储在应用程序文档目录的sqlite数据库。
虽然数据是通过sqlite存储的,但框架中的类将完成加载和保存数据的相关工作。不许要编写任何sql语句。
sqlite3:
数据库对象:sqlite3 数据库指针:sqlite3_stmt
sqlite3_open():打开数据库
sqlite3_prepare():负责编译sql语句
sqlite3_step():只能得到第一行的内容,继续调用该函数直到所有结果都记录完毕
sqlite3_column():将sqlite3_step运行的结果读出来
sqlite3_finalize():在sqlite3_stmt使用完毕后销毁sqlite3_stmt
sqlite3_close():关闭数据库
sqlite3_exec():执行sql语句
sqlite3_bind_xxx():根据希望使用的数据类型,选择不同的绑定语句。
什么是沙盒?沙盒包含哪些文件,描述每个文件的使用场景。如何获取这些文件的路径?如何获取应用程序包 中文件的路径?
沙盒是某个iphone工程进行文件操作有此工程对应的指定的位置,不能逾越。 包括:四个文件夹:documents,tmp,app,Library。 手动保存的文件在documents文件里。 Nsuserdefaults保存的文件在tmp文件夹里。访问沙箱之外的磁盘文件是私有API,APPStore 无法通过
Documents 目录:您应该将所有de应用程序数据文件写入到这个目录下。这个目录用于存储用户数据或其它应该定期备 份的信息。AppName.app 目录:这是应用程序的程序包目录,包含应用程序的本身。由于应用程序必须经过签名,所以 您在运行时不能对这个目录中的内容进行修改,否则可能会使应用程序无法启动。Library 目录:这个目录下有两个子目 录:Caches 和 PreferencesPreferences 目录包含应用程序的偏好设置文件。您不应该直接创建偏好设置文件,而是 应该使用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类方法用于返回一个代表应用程序包的对象。
===========================================================
分析json、xml的区别?json、xml解析方式的底层是如何处理的?
对于服务器的接口还是用JSON的比较多。XML的比较少的还是。
◆可读性
JSON(Java Object Notation) 和XML的可读性可谓不相上下,一边是建议的语法,一边是规范的标签形式,很难分出胜负。
◆可扩展性
都具有很好的扩展性
◆编码难度
XML有丰富的编码工具,比如Dom4j、JDom等,JSON也有json.org提供的工具,但是JSON的编码明显比XML容易许多,即使不借助工具也能写出JSON的代码,可是要写好XML就不太容易了。相对而言:JSON的编码比较容易
◆解码难度
XML的解析得考虑子节点父节点,而JSON的解析难度几乎为0
◆实例比较
XML和JSON都使用结构化方法来标记数据
与XML一样,JSON也是基于文本的,且它们都使用Unicode编码,同样具有可读性。XML比较适合于标记文档,而JSON却更适合于时行数据交换处理。
数据体积方面:json相对于xml来讲,数据体积小,传递的速度跟快些
数据交互方面:json与JavaScript的交互更加方面,更容易解析处理,更好的数据交互
数据描述方面:xml对数据描述性比较好
传输速度方面:json的速度远远快于xml
JSON底层原理:遍历字符串中的字符,最终根据格式规定的特殊字符,比如{}号,[]号, : 号 等进行区分,{}号是一个字典 的开始,[]号是一个数组的开始, : 号是字典的键和值的分水岭,最终乃是将json数据转化为字典,字典中值可能是字典,数 组,或字符串而已。
XML底层原理: XML解析常用的解析方法有两种:DOM解析和SAX解析。DOM 采用建立树形结构的方式访问 XML 文档,而 SAX 采用的事件模型。 。DOM 解析把 XML 文档转化为一个包含其内容的树,并可以对树进行遍历。使用 DOM 解析器的时候需 要处理整个 XML 文档,所以对性能和内存的要求比较高。SAX在解析 XML 文档的时候可以触发一系列的事件,当发现给定的tag 的时候,它可以激活一个回调方法,告诉该方法制定的标签已经找到。SAX 对内存的要求通常会比较低,因为它让开发人员自己来决 定所要处理的tag。特别是当开发人员只需要处理文档中所包含的部分数据时,SAX 这种扩展能力得到了更好的体现。 延伸:SAX与DOM的区别: 1、SAX处理的优点非常类似于流媒体的优点。分析能够立即开始,而不是等待所有的数据被处理。而且由于应用程序只是 在读取数据时检查数据,因此不需要将数据存储在内存中。这对于大型文档来说是个巨大的优点。事实上,应用程序甚至不 必解析整个文档;它可以在某个条件得到 满足时停止解析。一般来说,SAX 还比它的替代者 DOM 快许多。另一方面,由 于应用程序没有以任何方式存储数据,使用 SAX 来更改数据或在数据流中往后移是不可能的。
2、DOM 以及广义的基于树的处理具有几个优点。首先,由于树在内存中是持久的,因此可以修改它以便应用程序能对数 据和结构作出更改。它还可以在任何时候在树中上下 导航,而不是像 SAX 那样是一次性的处理。DOM 使用起来也要简单 得多。另一方面,在内存中构造这样的树涉及大量的开销。大型文件完全占用系统内存容量的情况并不鲜见。此外,创建一 棵 DOM 树可能是一个缓慢的过程。
3、选择 DOM 还是选择 SAX,这取决于下面几个因素:
应用程序的目的:如果打算对数据作出更改并将它输出为 XML,那么在大多数情况下,DOM 是适当的选择。并不是说使 用 SAX 就不能更改数据,但是该过程要复杂得多,因为您必须对数据的一份拷贝而不是对数据本身作出更改。
数据容量: 对于大型文件,SAX 是更好的选择。数据将如何使用:如果只有数据中的少量部分会被使用,那么使用 SAX 来将该部分数据提取到应用程序中可能更好。 另一方面,如果您知道自己以后会回头引用已处理过的大量信息,那么 SAX 也许不是恰当的选择。
对速度的需要:SAX 实现通常要比 DOM 实现更快。
SAX 和 DOM 不是相互排斥的,记住这点很重要。您可以使用 DOM 来创建 SAX 事件流,也可以使用 SAX 来创建 DOM 树。事实上,用于创建 DOM 树的大多数解析器实际上都使用 SAX 来完成这个任务!
解析 XML 通常有两种方式,DOM 和 SAX:
•DOM解析XML时,读入整个XML文档并构建一个驻留内存的树结构(节点树),通过遍历树结构可以检索任意XML节点,读取它的属性和值。而且通常情况下,可以借助XPath,直接查询XML节点。
•SAX解析XML,是基于事件通知的模式,一边读取XML文档一边处理,不必等整个文档加载完之后才采取操作,当在读取解析过程中遇到需要处理的对象,会发出通知对其进行处理。
一般在iOS平台下,比较常用的XML解析类库有如下几种:
•NSXMLParser,这是一个SAX方式解析XML的类库,默认包含在iOS SDK中,使用也比较简单。
•libxml2,是一套默认包含在iOS SDK中的开源类库,它是基于C语言的API,所以使用起来可能不如NSXML方便。这套类库同时支持DOM和SAX解析,libxml2的SAX解析方式还是非常酷的,因为它可以边读取边解析,尤其是在从网上下载一个很大的XML文件,就可以一边下载一边对已经下载好的内容进行解析,极大的提高解析效率。
GDataXML,这是一套Google开发的DOM方式XML解析类库,支持读取和修改XML文档,支持XPath方式查询。
•如果是读取很小的XML文档,性能基本上没有什么差别,不过从调用的方便性来说,建议使用TouchXML、KissXML或GDataXML
•如果是需要读取和修改XML文档,建议使用KissXML或GDataXML
•如果需要读取非常大的XML文档,则建议使用libxml2或TBXML
•如果你不想去调用第三方类库,那么使用NSXML也可以
===========================================================
13.网络编程 HTTP TCP/IP Socket ASI AFNetwork
从上往下1-7层
计算机网络中五层协议分别是(从下向上):
1) 物理层
2)数据链路层
3)网络层
4)传输层
5)应用层
其功能分别是:
1)物理层主要负责在物理线路上传输原始的二进制数据;
2)数据链路层主要负责在通信的实体间建立数据链路连接;
3)网络层主要负责创建逻辑链路,以及实现数据包的分片和重组,实现拥塞控制、网络互连等功能;
4)传输曾负责向用户提供端到端的通信服务,实现流量控制以及差错控制;
5)应用层为应用程序提供了网络服务。
一般来说,物理层和数据链路层是由计算机硬件(如网卡)实现的,网络层和传输层由操作系统软件实现,而应用层由应用程序或用户创建实现。
TCP (Transmission Control Protocol)和UDP(User Datagram Protocol)协议属于传输层协议。其中TCP提供IP环境下的数据可靠传输,它提供的服务包括数据流传送、可靠性、有效流控、全双工操作和多路复 用。通过面向连接、端到端和可靠的数据包发送。通俗说,它是事先为所发送的数据开辟出连接好的通道,然后再进行数据发送;而UDP则不为IP提供可靠性、 流控或差错恢复功能。一般来说,TCP对应的是可靠性要求高的应用,而UDP对应的则是可靠性要求低、传输经济的应用。TCP支持的应用协议主要 有:Telnet、FTP、SMTP等;UDP支持的应用层协议主要有:NFS(网络文件系统)、SNMP(简单网络管理协议)、DNS(主域名称系 统)、TFTP(通用文件传输协议)等.
TCP/IP协议与低层的数据链路层和物理层无关,这也是TCP/IP的重要特点
HTTP协议详解
HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。目前在WWW中使用的是HTTP/1.0的第六版,HTTP/1.1的规范化工作正在进行之中。
http(超文本传输协议)是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式,HTTP1.1版本中给出一种持续连接的机制,绝大多数的Web开发,都是构建在HTTP协议之上的Web应用。
HTTP协议的主要特点可概括如下:
1.支持客户/服务器模式。
2.简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
3.灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
4.无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
5.无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
URL
HTTP URL (URL统一资源定位符是一种特殊类型的URI是他的子类,包含了用于查找某个资源的足够的信息)的格式如下:
错误!超链接引用无效。]
http表示要通过HTTP协议来定位网络资源;host表示合法的Internet主机域名或者IP地址;port指定一个端口号,为空则使用缺省端口80;abs_path指定请求资源的URI;如果URL中没有给出abs_path,那么当它作为请求URI时,必须以“/”的形式给出,通常这个工作浏览器自动帮我们完成。
#########################################################
TCP/UDP区别联系
TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快
TCP(Transmission Control Protocol,传输控制协议)是基于连接的协议,也就是说,在正式收发数据前,必须和对方建立可靠的连接。一个TCP连接必须要经过三次“对话”才能建立起来,我们来看看这三次对话的简单过程:1.主机A向主机B发出连接请求数据包;2.主机B向主机A发送同意连接和要求同步(同步就是两台主机一个在发送,一个在接收,协调工作)的数据包;3.主机A再发出一个数据包确认主机B的要求同步:“我现在就发,你接着吧!”,这是第三次对话。三次“对话”的目的是使数据包的发送和接收同步,经过三次“对话”之后,主机A才向主机B正式发送数据。
UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发送过去! UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境。
socket连接和http连接的区别
简单说,你浏览的网页(网址以http://开头)都是http协议传输到你的浏览器的, 而http是基于socket之上的。socket是一套完成tcp,udp协议的接口。
HTTP协议:简单对象访问协议,对应于应用层 ,HTTP协议是基于TCP连接的
tcp协议: 对应于传输层
ip协议: 对应于网络层
TCP/IP是传输层协议,主要解决数据如何在网络中传输;而HTTP是应用层协议,主要解决如何包装数据。
Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。
http连接:http连接就是所谓的短连接,即客户端向服务器端发送一次请求,服务器端响应后连接即会断掉;
socket连接:socket连接就是所谓的长连接,理论上客户端和服务器端一旦建立起连接将不会主动断掉;但是由于各种环境因素可能会是连接断开,比如说:服务器端或客户端主机down了,网络故障,或者两者之间长时间没有数据传输,网络防火墙可能会断开该连接以释放网络资源。所以当一个socket连接中没有数据的传输,那么为了维持连接需要发送心跳消息~~具体心跳消息格式是开发者自己定义的
我们已经知道网络中的进程是通过socket来通信的,那什么是socket呢?socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –>读写write/read –> 关闭close”模式来操作。我的理解就是Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭),这些函数我们在后面进行介绍。
Socket连接与HTTP连接
我们在传输数据时,可以只使用(传输层)TCP/IP协议,但是那样的话,如果没有应用层,便无法识别数据内容,如果想要使传输的数据有意义,则必须使用到应用层协议,应用层协议有很多,比如HTTP、FTP、TELNET等,也可以自己定义应用层协议。WEB使用HTTP协议作应用层协议,以封装HTTP文本信息,然后使用TCP/IP做传输层协议将它发到网络上。
1)Socket是一个针对TCP和UDP编程的接口,你可以借助它建立TCP连接等等。而TCP和UDP协议属于传输层 。
而http是个应用层的协议,它实际上也建立在TCP协议之上。
(HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。)
2)Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口。
下面是一些的重要的概念,特在此做摘抄和总结。
一。什么是TCP连接的三次握手
第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次握手”(过程就不细写了,就是服务器和客户端交互,最终确定断开)
二。利用Socket建立网络连接的步骤
建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。
套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。
1。服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
2。客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
3。连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
三。HTTP链接的特点
HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。
HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。
四。TCP和UDP的区别
1。TCP是面向链接的,虽然说网络的不安全不稳定特性决定了多少次握手都不能保证连接的可靠性,但TCP的三次握手在最低限度上(实际上也很大程度上保证了)保证了连接的可靠性;而UDP不是面向连接的,UDP传送数据前并不与对方建立连接,对接收到的数据也不发送确认信号,发送端不知道数据是否会正确接收,当然也不用重发,所以说UDP是无连接的、不可靠的一种数据传输协议。
2。也正由于1所说的特点,使得UDP的开销更小数据传输速率更高,因为不必进行收发数据的确认,所以UDP的实时性更好。
tcp协议和udp协议的差别
是否连接面向连接面向非连接
传输可靠性可靠不可靠
应用场合传输大量数据少量数据
速度慢快
http和scoket通信的区别?socket连接相关库,TCP,UDP的连接方法,HTTP的几种常用方式? http和scoket通信的区别: http是客户端用http协议进行请求,发送请求时候需要封装http请求头,并绑定请求的数据,服务器一般有web服务器配 合(当然也非绝对)。 http请求方式为客户端主动发起请求,服务器才能给响应,一次请求完毕后则断开连接,以节省资 源。服务器不能主动给客户端响应(除非采取http长连接技术)。iphone主要使用类是NSUrlConnection。 scoket是客户端跟服务器直接使用socket“套接字”进行连接,并没有规定连接后断开,所以客户端和服务器可以保持连 接通道,双方都可以主动发送数据。一般在游戏开发或股票开发这种要求即时性很强并且保持发送数据量比较大的场合使 用。主要使用类是CFSocketRef。 UDP:是用户数据报协议:主要用在实时性要求高以及对质量相对较弱的地方,但面对现在高质量的线路不是容易丢包除非 是一些拥塞条件下 ,如流媒体 TCP:是传输控制协议:是面连接的,那么运行环境必然要求其可靠性不可丢包有良好的拥塞控制机制如http ftp telnet 等
你连接服务器用的是什么方法,如果请求过程中,网络出了问题这么办?
NSUrlConnection 连接后,有一系列委托方法来接受来自服务器的响应和数据,其中接受相应的方法回得到服务器要传 回的数据有多大,接受数据的方法会反复调用来不断接受服务器数据,如果网络出了问题了,会调用一个方法让你来做相关 处理。
===========================================================
14.AppStore上传 及远程推送
AppStrore 审核不通过
app的设置界面、按钮使用了类似iphone的操作方式以及icon的圆角设计 -> 重新设计?
app的年龄设置太低 -> 改了年龄?
app里有实物奖励 -> 免责声明,和苹果无关?
app描述里提了后续版本的功能的字样 -> 删除?
app有打分的功能 -> 有reject的,也有通过的?
app需要使用location,没有提示用户 -> 加了提示,允许用户拒绝?
app没提供测试账号 -> 提供?
app不支持64位
app里有私有api -> 修改? 应用内含有有某公司LOGO的图片,没有该公司授权文件,被拒-> 修改?
第三方静态库包含私有api的调用(联系第三方技术支持,更新静态库); 包含潜在的色情,暴力等内容(调整应用年龄限制等级,并加入举报功能) 做浏览器的,分级必须选17+
什么是push?远程推送?
第一步:UIApplication向APNS注册push notification服务
1、应用程序 要支持 推送服务(在网页里配置) (1)https://developer.
apple.com/devcenter/ios/index.action
(2)登录 苹果开发者账号(注意是收费账号,$99或$299) 3)下载push证书(主要是给程序签名,push服务只有收费开发者才具备。所以需要签名验证),如果没有 push证书,创建一个push证书(App ID->钥匙串程序生成request->push证书)注意事项:App ID的 Bundle ID必须和程序plist文件里的Bundle identifier一致。App ID一旦生成,将不可修改。 (4)把证书安装到钥匙串里(双击证书文件)
(5)生成 编译程序 用的描述文件(网页里进行) 2、向APNS注册push服务(UIApplication的registerForRemoteNotificationTypes:方法)
第二步 获取APNS分配的DeviceToken(64位16进制串)
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
第三步 把DeviceToken发送给自己的后台服务器,服务器记录每台设备的DeviceToken以便日后推送信息给客 户端。(需要有一个网络接口,让客户端发送DeviceToken)
第四步 服务器推送信息给客户端 1、服务器除了需要有客户端的DeviceToken之外,还要有push证书,对push的内容进行签名。(苹果为了防 止 恶意向客户端(比如DeviceToken泄露了)发送消息,每次推送消息,都需要证书进行签名,从而避免黑客恶 意攻击用户手机。) 2、如果你的服务器是java写的,可以直接使用钥匙串导出的p12文件(证书和密钥一起导出)。如果你的服务器 是php写的,因为php语言不支持p12文件类型,需要转换为pem文件。
3、将p12转换为pem文件:终端 先找到你p12所在的目录 openssl pkcs12 -in CertificateName.p12 - outCertificateName.pem -nodes
4、服务器发送信息给APNS,APNS自动将信息推送给客户端
第五步 客户端处理收到的信息
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
1、测试版的push证书仅仅用于开发期间测试,发布版的程序需要生成一个发布版的push证书。 2、测试版APNS的ssl地址和发布版的ssl地址不同 3、测试版的DeviceToken和发布版的DeviceToken也不同
4、php文件要喝pem文件放在同一目录。
5、除了alert sound和badge之外,json串里还可以包含自定义信息。
6、推送的信息最大255字节 7、推送的信息受网络影响较大,有可能造成延迟甚至丢失,重要信息的传递不应该使用push通知,应该有专门的 后台接口。
8、借助push推送,两个客户端可以实现即时通信,工程里面存放我们的p12文件,客户端自己组织json串,发 送请求到APNS。
===========================================================
15.XMPP简介
XMPP(Extensible Messaging and Presence Protocol,前称)是一种以XML为基础的开放式实时通信协议,是 经由互联网工程工作小组(IETF)通过的互联网标准。简单的说,XMPP就是一种协议,一种规定。就是说,在网络上传 东西,要建立连接,TCP/IP连接,建立后再传东西,而XMPP就是规定你传的东西的格式。XMPP是基于XML的协议。 优点
开放:
XMPP协议是自由、开放、公开的,并且易于了解。 而且在客户端 、 服务器 、 组件 、 源码库等方面,都已经各自有多种实现。 标准:
分散式:
XMPP网络的架构和电子邮件十分相像;XMPP核心协议通信方式是先创建一个stream,XMPP以TCP传递XML数据流,没有 中央主服务器。 任何人都可以运行自己的XMPP服务器,使个人及组织能够掌控他们的实时传讯体验。
安全:
任何XMPP协议的服务器可以独立于公众XMPP网络(例如在企业内部网络中),而使用SASL及TLS等技术的可靠安全性,已自 带于核心XMPP技术规格中。
可扩展:
XML 命名空间的威力可使任何人在核心协议的基础上建造定制化的功能;为了维持通透性,常见的扩展由XMPP标准基金会 。 弹性佳:
XMPP除了可用在实时通信的应用程序,还能用在网络管理、内容供稿、协同工具、文件共享、游戏、远程系统监控等。 多样性:
用XMPP协议来建造及布署实时应用程序及服务的公司及开放源代码计划分布在各种领域;用XMPP技术开发软件,资源及支持的 来源是多样的,使得使你不会陷于被“绑架”的困境。
缺点
数据负载太重:
随着通常超过70%的XMPP协议的服务器的数据流量的存在和近60%的被重复转发,XMPP协议目前拥有一个大型架空中存在的 数据提供给多个收件人。 新的议定书正在研究,以减轻这一问题。
没有二进制数据:
XMPP协议的方式被编码为一个单一的长的XML文件,因此无法提供修改二进制数据。 因此, 文件传输协议一样使用外部的 HTTP。 如果不可避免,XMPP协议还提供了带编码的文件传输的所有数据使用的Base64 。 至于其他二进制数据加密会话 (encrypted conversations)或图形图标(graphic icons)以嵌入式使用相同的方法。
===========================================================
函数 通过函数名调用;
block 匿名函数 C++中的闭包 兰木达
int (int x,int y); 没有名称额函数
a =(10,8)—>a=8, 逗号表达式取逗号后面的
匿名函数
函数指针
int (*)(int,int)=sum;
Typedef int (* NewType)(int x,int y)NewType;
int (^block)(int,int)=^int (int x,int y){
return x+y;
};
//类型 int(^)(int ,int);
//变量名block1
//初始值^int (int x,int y){ return x+y; } 函数的定义(实现),去掉函数名
局部变量
block是语法//block 购买
返回值(^变量名)(参数列表)=^返回值(参数列表){ //变量名改名 参数列表以,隔开 ,返回值可省;
实现代码;
};
申明
//—————————————————————————————————————————————
//给block赋值的特点,类型一致,以^托字符 插入箭头开头 函数的定义(实现),去掉函数名;后面是赋的值;无法调用;
// int x= block1(10,20); //接收一下
int y= block2(10,20);
NSLog(@"%d",y);
block2 =^int(int x,int y)//返回值int 可以删除,
{
return x-y;
};
//(BOOL)(^)(NSString *,NSString *) 类型
int(^blockSum)(NSString *str1,NSString *str2)//定义(^)加上函数实现
=^int (NSString *str1,NSString *str2){ //int 可以删除,但必须保证return后面的类型一致;
return [str1 intValue]+[str2 intValue];//intValue把字符串变成字符
};
int result= blockSum(@"100",@"150");
NSLog(@"%d",result);
int(^aa)()=^(){
return 10;
};
// ^后面的()可以删除,当block后面没有参数的时候,可以吧()也省略掉;
NSLog(@"%d",aa());
void(^bb)()=^{NSLog(@"hello lanou");}; //分号不能少;
bb();//调用^后面的
// NSString *str1=@"adsj";
// NSString *str2=@"sada";字符串比较
NSComparisonResult (^aaa)(NSString *str1,NSString*str2 )=^(NSString *str1,NSString *str2){
return [str1 compare:str2];//字符串比较函数campare 等号后面^后面()不能漏
};
NSLog(@"%ld",aaa(@"hao",@"jian"));
//block可以使用外部变量,但是不能修改;如果想改值用__block,两个下划线
__block int m=100;//static修饰符
int (^block)(int,int)=^(int x,int y){
a=100;//可以修改外部变量;
return x+y+(++m)-a;
};
NSLog(@"%d",block(20,30));
//__block相当于static,静态区,一直存在,直到程序结束.
1,block 在实现时就会对它引用到的它所在方法中定义的栈变量进行一次只读拷贝,然后在 block 块内使用该只读拷贝。
如下代码:
- - (void)testAccessVariable
- {
- NSInteger outsideVariable = 10;
- //__block NSInteger outsideVariable = 10;
- NSMutableArray * outsideArray = [[NSMutableArray alloc] init];
-
- void (^blockObject)(void) = ^(void){
- NSInteger insideVariable = 20;
- KSLog(@" > member variable = %d", self.memberVariable);
- KSLog(@" > outside variable = %d", outsideVariable);
- KSLog(@" > inside variable = %d", insideVariable);
-
- [outsideArray addObject:@"AddedInsideBlock"];
- };
-
- outsideVariable = 30;
- self.memberVariable = 30;
-
- blockObject();
-
- KSLog(@" > %d items in outsideArray", [outsideArray count]);
- }
输出结果为:
- > member variable = 30
- > outside variable = 10
- > inside variable = 20
- > 1 items in outsideArray
注意到没?outside 变量的输出值为10,虽然outside变量在定义 block 之后在定义 block 所在的方法 testAccessVariable 中被修改为 20 了。这里的规则就是:blockObject 在实现时会对 outside 变量进行只读拷贝,在 block 块内使用该只读拷贝。因此这里输出的是拷贝时的变量值 10。如果,我们想要让 blockObject 修改或同步使用 outside 变量就需要用 __block 来修饰 outside 变量。
__block NSInteger outsideVariable = 10;
注意:
a),在上面的 block 中,我们往 outsideArray 数组中添加了值,但并未修改 outsideArray 自身,这是允许的,因为拷贝的是 outsideArray 自身。
b),对于 static 变量,全局变量,在 block 中是有读写权限的,因为在 block 的内部实现中,拷贝的是指向这些变量的指针。
c), __block 变量的内部实现要复杂许多,__block 变量其实是一个结构体对象,拷贝的是指向该结构体对象的指针。
2,非内联(inline) block 不能直接访问 self,只能通过将 self 当作参数传递到 block 中才能使用,并且此时的 self 只能通过 setter 或 getter 方法访问其属性,不能使用句点式方法。但内联 block 不受此限制。
- typedef NSString* (^IntToStringConverter)(id self, NSInteger paramInteger);
- - (NSString *) convertIntToString:(NSInteger)paramInteger
- usingBlockObject:(IntToStringConverter)paramBlockObject
- {
- return paramBlockObject(self, paramInteger);
- }
-
- typedef NSString* (^IntToStringInlineConverter)(NSInteger paramInteger);
- - (NSString *) convertIntToStringInline:(NSInteger)paramInteger
- usingBlockObject:(IntToStringInlineConverter)paramBlockObject
- {
- return paramBlockObject(paramInteger);
- }
-
- IntToStringConverter independentBlockObject = ^(id self, NSInteger paramInteger) {
- KSLog(@" >> self %@, memberVariable %d", self, [self memberVariable]);
-
- NSString *result = [NSString stringWithFormat:@"%d", paramInteger];
- KSLog(@" >> independentBlockObject %@", result);
- return result;
- };
-
- - (void)testAccessSelf
- {
- // Independent
- //
- [self convertIntToString:20 usingBlockObject:independentBlockObject];
-
- // Inline
- //
- IntToStringInlineConverter inlineBlockObject = ^(NSInteger paramInteger) {
- KSLog(@" >> self %@, memberVariable %d", self, self.memberVariable);
-
- NSString *result = [NSString stringWithFormat:@"%d", paramInteger];
- KSLog(@" >> inlineBlockObject %@", result);
- return result;
- };
- [self convertIntToStringInline:20 usingBlockObject:inlineBlockObject];
- }
3,使用 weak–strong dance 技术来避免循环引用
在第二条中,我提到内联 block 可以直接引用 self,但是要非常小心地在 block 中引用 self。因为在一些内联 block 引用 self,可能会导致循环引用。如下例所示:
- @interface KSViewController ()
- {
- id _observer;
- }
-
- @end
-
- @implementation KSViewController
-
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- // Do any additional setup after loading the view, typically from a nib.
-
- KSTester * tester = [[KSTester alloc] init];
- [tester run];
-
- _observer = [[NSNotificationCenter defaultCenter]
- addObserverForName:@"TestNotificationKey"
- object:nil queue:nil usingBlock:^(NSNotification *n) {
- NSLog(@"%@", self);
- }];
- }
-
- - (void)dealloc
- {
- if (_observer) {
- [[NSNotificationCenter defaultCenter] removeObserver:_observer];
- }
- }
在上面代码中,我们添加向通知中心注册了一个观察者,然后在 dealloc 时解除该注册,一切看起来正常。但这里有两个问题:
a) 在消息通知 block 中引用到了 self,在这里 self 对象被 block retain,而 _observer 又 retain 该 block的一份拷贝,通知中心又持有 _observer。因此只要 _observer 对象还没有被解除注册,block 就会一直被通知中心持有,从而 self 就不会被释放,其 dealloc 就不会被调用。而我们却又期望在 dealloc 中通过 removeObserver 来解除注册以消除通知中心对 _observer/block 的 retain。
b) 同时,_observer 是在 self 所在类中定义赋值,因此是被 self retain 的,这样就形成了循环引用。
上面的过程 a) 值得深入分析一下:
苹果官方文档中对 addObserverForName:object:queue:usingBlock: 中的 block 变量说明如下:
The block is copied by the notification center and (the copy) held until the observer registration is removed.
因此,通知中心会拷贝 block 并持有该拷贝直到解除 _observer 的注册。在 ARC 中,在被拷贝的 block 中无论是直接引用 self 还是通过引用 self 的成员变量间接引用 self,该 block 都会 retain self。
这两个问题,可以用 weak–strong dance 技术来解决。该技术在 WWDC 中介绍过:2011 WWDC Session #322 (Objective-C Advancements in Depth)
- __weak KSViewController * wself = self;
- _observer = [[NSNotificationCenter defaultCenter]
- addObserverForName:@"TestNotificationKey"
- object:nil queue:nil usingBlock:^(NSNotification *n) {
- KSViewController * sself = wself;
- if (sself) {
- NSLog(@"%@", sself);
- }
- else {
- NSLog(@"<self> dealloc before we could run this code.");
- }
- }];
下面来分析为什么该手法能够起作用。
首先,在 block 之前定义对 self 的一个弱引用 wself,因为是弱引用,所以当 self 被释放时 wself 会变为 nil;然后在 block 中引用该弱应用,考虑到多线程情况,通过使用强引用 sself 来引用该弱引用,这时如果 self 不为 nil 就会 retain self,以防止在后面的使用过程中 self 被释放;然后在之后的 block 块中使用该强引用 sself,注意在使用前要对 sself 进行了 nil 检测,因为多线程环境下在用弱引用 wself 对强引用 sself 赋值时,弱引用 wself 可能已经为 nil 了。
通过这种手法,block 就不会持有 self 的引用,从而打破了循环引用。
扩展:其他还需要注意避免循环引用的地方
与此类似的情况还有 NSTimer。苹果官方文档中提到"Note in particular that run loops retain their timers, so you can release a timer after you have added it to a run loop.",同时在对接口
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats
的 target 说明文档中提到:
The object to which to send the message specified by aSelector when the timer fires. The target object is retained by the timer and released when the timer is invalidated.
结合这两处文档说明,我们就知道只要重复性 timer 还没有被 invalidated,target 对象就会被一直持有而不会被释放。因此当你使用 self 当作 target 时,你就不能期望在 dealloc 中 invalidate timer,因为在 timer 没有被invalidate 之前,dealloc 绝不会被调用。因此,需要找个合适的时机和地方来 invalidate timer,但绝不是在 dealloc 中。
4,block 内存管理分析
block 其实也是一个 NSObject 对象,并且在大多数情况下,block 是分配在栈上面的,只有当 block 被定义为全局变量或 block 块中没有引用任何 automatic 变量时,block 才分配在全局数据段上。 __block 变量也是分配在栈上面的。
在 ARC 下,编译器会自动检测为我们处理了 block 的大部分内存管理,但当将 block 当作方法参数时候,编译器不会自动检测,需要我们手动拷贝该 block 对象。幸运的是,Cocoa 库中的大部分名称中包含”usingBlock“的接口以及 GCD 接口在其接口内部已经进行了拷贝操作,不需要我们再手动处理了。但除此之外的情况,就需要我们手动干预了。
- - (id) getBlockArray
- {
- int val = 10;
- return [[NSArray alloc] initWithObjects:
- ^{ KSLog(@" > block 0:%d", val); }, // block on the stack
- ^{ KSLog(@" > block 1:%d", val); }, // block on the stack
- nil];
-
- // return [[NSArray alloc] initWithObjects:
- // [^{ KSLog(@" > block 0:%d", val); } copy], // block copy to heap
- // [^{ KSLog(@" > block 1:%d", val); } copy], // block copy to heap
- // nil];
- }
-
- - (void)testManageBlockMemory
- {
- id obj = [self getBlockArray];
- typedef void (^BlockType)(void);
- BlockType blockObject = (BlockType)[obj objectAtIndex:0];
- blockObject();
- }
执行上面的代码中,在调用 testManageBlockMemory 时,程序会 crash 掉。因为从 getBlockArray 返回的 block 是分配在 stack 上的,但超出了定义 block 所在的作用域,block 就不在了。正确的做法(被屏蔽的那段代码)是在将 block 添加到 NSArray 中时先 copy 到 heap 上,这样就可以在之后的使用中正常访问。
在 ARC 下,对 block 变量进行 copy 始终是安全的,无论它是在栈上,还是全局数据段,还是已经拷贝到堆上。对栈上的 block 进行 copy 是将它拷贝到堆上;对全局数据段中的 block 进行 copy 不会有任何作用;对堆上的 block 进行 copy 只是增加它的引用记数。
如果栈上的 block 中引用了__block 类型的变量,在将该 block 拷贝到堆上时也会将 __block 变量拷贝到堆上如果该 __block 变量在堆上还没有对应的拷贝的话,否则就增加堆上对应的拷贝的引用记数
Objective-C
标签:
原文地址:http://www.cnblogs.com/shenzheng/p/4484295.html