多态:不同对象以自己的方式响应相同的消息的能力叫做多态。 (自我理解:不同的类可以有相同的属性、方法)
跳出面向对象思想(一)继承
继承是罪恶,尽量不要继承。(避免:拔出萝卜带出泥)就我目前了解到的情况看,除了安居客的Pad App没有在框架级针对UIViewController有继承的设计以外,其它公司或多或少都针对UIViewController有继承,包括安居客iPhone app(那时候我已经对此无能为力,可见View的架构在一开始就设计好有多么重要)。甚至有的还对UITableView有继承,这是一件多么令人发指,多么惨绝人寰,多么丧心病狂的事情啊。虽然不可避免的是有些情况我们不得不从苹果原生对象中继承,比如UITableViewCell。但我还是建议尽量不要通过继承的方案来给原生对象添加功能,前面提到的Aspect方案和Category方案都可以使用。用Aspect+load来实现重载函数,用Category来实现添加函数,当然,耍点手段用Category来添加property也是没问题的。这些方案已经覆盖了继承的全部功能,而且非常好维护,对于业务方也更加透明,何乐而不为呢。
不用继承可能在思路上不会那么直观,但是对于不使用继承带来的好处是足够顶得上使用继承的坏处的。顺便在此我要给Category正一下名:业界对于Category的态度比较暧昧,在多种场合(讲座、资料文档)都宣扬过尽可能不要使用Category。它们说的都有一定道理,但我认为Category是苹果提供的最好的使用集合代替继承的方案,但针对Category的设计对架构师的要求也很高,请合理使用。而且苹果也在很多场合使用Category,来把一个原本可能很大的对象,根据不同场景拆分成不同的Category,从而提高可维护性。
不使用继承的好处我在这里已经说了,放到iOS应用架构来看,还能再多额外两个好处:1.在业务方做业务开发或者做Demo时,可以脱离App环境,或花更少的时间搭建环境。2.对业务方来说功能更加透明,也符合业务方在开发时的第一直觉。
objc 优点:
1) Cateogies
2) Posing
3) 动态识别
4) 指标计算
5 )弹性讯息传递
6) 不是一个过度复杂的 C 衍生语言
7) Objective-C 与 C++ 可混合编程
缺点 :
1) 不支援命名空间
2) 不支持运算符重载
3 )不支持多重继承
4 )使用动态运行时类型,所有的方法都是函数调用,所以很多编译时优化方法都用不到。(如内联函数等),性能低劣。
多重继承:指的是一个类可以同时继承多个父类的行为和特征功能。
1 、copy其实是建立了一个相同的对象,而retain不是;
2、copy是内容拷贝,retain是指针拷贝;
3、copy是内容的拷贝,对于像NSString,的确是这样,但是如果copy的是一个NSArray呢?这时只是copy了指向array中相对应元素的指针.这便是所谓的"浅复制".
4、copy的情况:NSString *newPt = [pt copy];
此时会在堆上重新开辟一段内存存放@"abc"比如0X1122内容为@"abc同时会在栈上为newPt分配空间比如地址:0Xaacc内容为0X1122因此retainCount增加1供newPt来管理0X1122 这段内存;
assign 与 retain :
1 、assign:简单赋值,不更改索引计数;
2、assign的情况:NSString *newPt = [pt assing];
此时newPt和pt完全相同地址都是0Xaaaa内容为0X1111即newPt只是pt的别名,对任何一个操作就等于对另一个操作,因此retainCount不需要增加;
3、assign就是直接赋值;
4、retain使用了引用计数,retain引起引用计数加1, release引起引用计数减1,当引用计数为0时,dealloc函数被调用,内存被回收;
5、retain的情况:NSString *newPt = [pt retain];
此时newPt的地址不再为0Xaaaa,可能为0Xaabb但是内容依然为0X1111。因此newPt和pt都可以管理"abc"所在的内存,因此retainCount需要增加1 ;
readonly :
属性是只读的,默认的标记是读写,如果你指定了只读,在@implementation中只需要一个读取器。或者如果你使用@synthesize关键字,也是有读取器方法被解析
readwrite :
说明属性会被当成读写的,这也是默认属性。设置器和读取器都需要在@implementation中实现。如果使用@synthesize关键字,读取器和设置器都会被解析;
不保证setter/getter的原子性,多线程情况下数据可能会有问题。nonatomic,非原子性访问,不加同步,多线程并发访问会提高性能。
先释放原先变量,再将新变量 retain然后赋值;
1 、 weak和strong属性只有在你打开ARC时才会被要求使用,这时你是不能使用retain release autorelease操作的,因为ARC会自动为你做好这些操作,但是你需要在对象属性上使用weak和strong,其中strong就相当于retain属性,而weak相当于assign。
2、只有一种情况你需要使用weak(默认是strong),就是为了避免retain cycles(就是父类中含有子类{父类retain了子类},子类中又调用了父类{子类又retain了父类},这样都无法release)
3、声明为weak的指针,指针指向的地址一旦被释放,这些指针都将被赋值为nil。这样的好处能有效的防止野指针。
1. 具体一点:IBOutlet可以为weak,NSString为copy,Delegate一般为weak,其他的看情况。
一般来说,类“内部”的属性设置为strong,类“外部”的属性设置为weak。说到底就是一个归属权的问题。小心出现循环引用导致内存无法释放。
2.不用ARC的话就会看到很多retian。
3.如果你写了@synthesize abc = _abc;的话,系统自动帮你声明了一个_abc的实例变量。
使用assign:对基础数据类型(NSInteger)和C数据类型(int, float, double, char,等)
使用copy:对NSString
使用retain:对其他NSObject和其子类
就是代码中自动加入了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。
该机能在iOS 5/ Mac OS X 10.7开始导入,利用Xcode4.2以后可以使用该特性。
简短的代码更易读: _property 的写法比 self.property 更短,也更简单。我认为这样写出来的代码更方便阅读。
执行速度更快,IPA体积更小
我之前从来没想到过这两者之间的速度和应用体积会有很大差别。不过一个同行(来自国外著名的社交网络公司)告诉我,他们公司发现二者还是有不小的差距,如果你们的应用需要做一些深度优化,可以考虑一下把 self.property 换成 _property 。但我觉得,大部分应用都应该是不需要做这种深度优化的。
循环引用问题
微博上的@王_晓磊在评论中提到:直接用私有变量有个需要特别注意的地方,在 block 里直接写 _property 相当于 self->_property ,虽然没写 self,但是暗含了对 self 的retain,容易造成循环引用。要记得用 weakSelf/strongSelf 大法。
答案:浅层复制:只复制指向对象的指针,而不复制引用对象本身。
深层复制:复制引用对象本身。
用网上一哥们通俗的话将就是:
浅复制好比你和你的影子,你完蛋,你的影子也完蛋
深复制好比你和你的克隆人,你完蛋,你的克隆人还活着。
__block 与 __weak的区别理解
2.__block对象在block中不会被block强引用一次,从而不会出现循环引用问题。
使用了__weak修饰符的对象,作用等同于定义为weak的property。自然不会导致循环引用问题,因为苹果文档已经说的很清楚,当原对象没有任何强引用的时候,弱引用指针也会被设置为nil。
因此,__block和__weak修饰符的区别其实是挺明显的:
1.__block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。
2.__weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。
3.__block对象可以在block中被重新赋值,__weak不可以。
PS:__unsafe_unretained修饰符可以被视为iOS SDK 4.3以前版本的__weak的替代品,不过不会被自动置空为nil。所以尽可能不要使用这个修饰符。
简而言之,线程就是把一个进程分为很多片,每一片都可以是一个独立的流程。这已经明显不同于多进程了,进程是一个拷贝的流程,而线程只是把一条河流截成很多条小溪。它没有拷贝这些额外的开销,但是仅仅是现存的一条河流,就被多线程技术几乎无开销地转成很多条小流程,它的伟大就在于它少之又少的系统开销。(当然伟大的后面又引发了重入性等种种问题,这个后面慢慢比较)。
管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
申请大小:
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。
对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间
弹出
分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
指针所指向的这块内存是在哪里分配的 , 在堆上称为堆上的指针 , 在栈上为栈上的指针 .
在堆上的指针 , 可以保存在全局数据结构中 , 供不同函数使用访问同一块内存 .
在栈上的指针 , 在函数退出后 , 该内存即不可访问 .
什么是指针的释放 ?
具体来说包括两个概念 .
1 释放该指针指向的内存 , 只有堆上的内存才需要我们手工释放 , 栈上不需要 .
2 将该指针重定向为 NULL.
数据结构中的指针 ?
其实就是指向一块内存的地址 , 通过指针传递 , 可实现复杂的内存访问 .
函数指针 ?
指向一块函数的入口地址 .
指针作为函数的参数 ?
比如指向一个复杂数据结构的指针作为函数变量
这种方法避免整个复杂数据类型内存的压栈出栈操作 , 提高效率 .
注意 : 指针本身不可变 , 但指针指向的数据结构可以改变 .
指针指向的变量是一个指针,即具体内容为一个指针的值,是一个地址.
此时指针指向的变量长度也是4位.
指针与地址的区别?
1指针意味着已经有一个指针变量存在,他的值是一个地址,指针变量本身也存放在一个长度为四个字节的地址当中,而地址概念本身并不代表有任何变量存在.
2指针的值,如果没有限制,通常是可以变化的,也可以指向另外一个地址.
地址表示内存空间的一个位置点,他是用来赋给指针的,地址本身是没有大小概念,指针指向变量的大小,取决于地址后面存放的变量类型.
指针与数组名的关系?
其值都是一个地址,但前者是可以移动的,后者是不可变的.
怎样防止指针的越界使用问题?
必须让指针指向一个有效的内存地址,
1防止数组越界
2防止向一块内存中拷贝过多的内容
3防止使用空指针
4防止改变const修改的指针
5防止改变指向静态存储区的内容
6防止两次释放一个指针
7防止使用野指针.
全局变量储存在静态数据库,
constructor
左右子树都是平衡二叉树且左右子树的深度差值的绝对值不大于1
(1)函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,
因此其值在下次调用时仍维持上次的值;
(2)在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
(3)在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
(4)在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
(5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。
const、static、extern、define简介
常量const:修饰的变量是不可变的,之前常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽成宏,推荐我们使用const常量。
静态变量 static:修饰的变量,是一个私有的全局变量。函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
2. 执行时刻:宏是预编译(编译之前处理),const是编译阶段。
3. 编译检查:宏不做检查,不会报编译错误,只是替换,const会编译检查,会报编译错误。
4. 宏的好处:宏能定义一些函数,方法。 const不能。
5. 宏的坏处:使用大量宏,容易造成编译时间久,每次都需要重新替换。
注意:很多Blog都说使用宏,会消耗很多内存,我这验证并不会生成很多内存,宏定义的是常量,常量都放在常量区,只会生成一份内存。
响应者链条是由多个响应者对象连接起来的链条,其中响应者对象是能处理事件的对象,所有的View和ViewController都是响应者对象,利用响应者链条能让多个控件处理同一个触摸事件.
事件响应链。包括点击事件,画面刷新事件等。在视图栈内从上至下,或者从下之上传播.可以说点事件的分发,传递以及处理。具体可以去看下touch事件这块。
简单的说, 在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接收并处理事件。这种有响应和处理事件的对象,我们称之为“响应者对象”。
UIApplication、UIViewController、UIView都继承自UIResponder,因此它们都是响应者对象,都能够接收并处理事件
响应者链的事件传递机制:
如果当前view不能处理当前事件,那么事件将会沿着响应者链(Responder Chain)进行传递,直到遇到能处理该事件的响应者(Responsder Object)。
响应者链的事件传递过程。
(1)如果view的控制器存在,就传递给控制器;如果控制器不存在,则将其传递给它的父视图
(2)在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,则其将事件或消息传递给window对象进行处理
(3)如果window对象也不处理,则其将事件或消息传递给UIApplication对象
(4)如果UIApplication也不能处理该事件或消息,则将其丢弃
frame: 该view在父view坐标系统中的位置和大小。(参照点是,父亲的坐标系统)
bounds:该view在本地坐标系统中的位置和大小。(参照点是,本地坐标系统,就相当于ViewB自己的坐标系统,以0,0点为起点)
center:该view的中心点在父view坐标系统中的位置和大小。(参照电是,父亲的坐标系统)
1. 单例模式的要点:要点有三个
一是: 某个类只能有一个实例;
二是: 它必须自行创建这个实例;
三是: 它必须自行向整个系统提供这个实例。
IOS 中的单例模式
在 oc 中要实现一个单例类,至少需要做以下四个步骤:
1 、为 单例对象创建 一个 静态实例, 并初始化,然后设置成 nil ,
2 、实现一个 实 例构造方法 ,检查声明的静态实例是否为 nil ,如果是则新建,并返回一个本类的实例,
3 、重写 allocWithZone 方法,用来 保证其他人 直接使用 alloc 和 init 试图获得 一个新实 例 的时候不产生一个新实例,
{
static Singleton *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[ self alloc] init];
});
return instance;
/**
“ 专门定义一个类来负责创建其他类的实例,被创建的实例通常具有共同的父类。 ”
实际上就是由一个工厂类,根据传入的参数,动态地决定创建出哪一个产品类的实例。
GOF 是这样描述工厂模式的:在基类中定义创建对象的一个接口,让子类决定实例化哪个类。工厂方法让一个类的实例化延迟到子类中进行。
工厂方法要解决的问题是对象的创建时机,它提供了一种扩展的策略,很好地符合了开放封闭原则。
工厂方法也叫做虚构造器( Virtual Constructor).
*/
// NSThread
dispatch_async( dispatch_get_main_queue(), ^{
// NSOperationQueue
[[ NSOperationQueue mainQueue] addOperationWithBlock:^{
1. OC是通过一种" retainCount"(引用计数)的方式来管理内存的,
3. autorelease和release没什么区别,只是 引用计数减一的时机不同而已,autorelease会在对象的使用真正结束的时候才做引用计数减一.
1. 发起请求的一方,从发送syn(递交名片)包开始第一次握手。
2. 收到请求的一方立马回应ack包,发现这是新建连接,然后也给出自己的syn包(递交名片)。(严重来讲,任何一方收到对方数据之后都会回应一个ack表示自己已收到)
3. 发起方收到响应后回应一个ack包。(整个过程注意sequence number的变化)
1. 客户端(发起方)向服务端(应答方)发起Http请求,如图中Packet 4 :C=GET URI=/higkoo。
2. 服务端收到请求后立马回应了ack,并告诉客户保持连接等待传输数据。
3. 紧接着服务端就向客户端返回了Http响应数据。
4. OK,通讯完成!
2. 服务端也真够快,立马就回应ack包:“好,我收到你要关闭的请求了”。
3. 服务端认为可以关闭了,然后也向客户端发起了关闭请求。(fin包)
4. 客户端立马也回应ack包:“嗯,我们关闭吧”。
5. OK,游戏结束!
NSXMLParser *xml=[[ NSXMLParser alloc] initWithData:data];
xml. delegate= self;
[xml parse];
2. 创建对私有方法的向前引用 。
- ( void)didFinishToDo:( ASIHTTPRequest *)request
{
NSArray * WBArr = [dic objectForKey: @"statuses"];
for ( int i = 0 ; i < WBArr. count ; i++ )
WBModel *Awm = [[ WBModel alloc] init]; //一条微博的信息
NSDictionary *dic = [WBArr objectAtIndex:i];
NSString *headImageStr = [[dic objectForKey: @"user"] objectForKey: @"profile_image_url"];
Awm. headImageURL = headImageStr;
Awm. name = [dic objectForKey:[[dic objectForKey: @"user"] objectForKey: @"name"]];
Awm. text = [dic objectForKey: @"text"];
Awm. createTime = [dic objectForKey: @"created_at"];
[ _wbModelArr addObject:Awm];
[Awm release];
}
[ _tableView reloadData]; //刷新 tableview
}
{
//显示用户位置、信息
map_view=[[ MKMapView alloc] initWithFrame: self. view. frame];
map_view. delegate= self;
map_view. mapType = MKMapTypeStandard;
map_view. showsUserLocation = YES;
[ self. view addSubview: map_view];
//创建四个坐标点
CLLocationCoordinate2D coord[ 4];
coord[ 0] = CLLocationCoordinate2DMake( 41.000512, - 109.050116);
coord[ 1] = CLLocationCoordinate2DMake( 41.002371, - 102.052066);
coord[ 2] = CLLocationCoordinate2DMake( 36.993076, - 102.041981);
coord[ 3] = CLLocationCoordinate2DMake( 36.99892, - 109.045267);
//1.画一个多边形
//2.画线
//3.画圆
MKPolygon *gon=[ MKPolygon polygonWithCoordinates:coord count: 4];
MKPolyline *line=[ MKPolyline polylineWithCoordinates:coord count: 4];
MKCircle *circle=[ MKCircle circleWithCenterCoordinate:coord[ 0] radius: 5000];
[ map_view addOverlay:circle];
}
//1.确定位置 按钮绑定:自定方法
-( void)mapBtn
{
//1.2创建一个大头针(标记)
MyAnn *ann=[[ MyAnn alloc] initWithTitle: @"大头针 " withSubtitle: @"具体地址 " withCoorinate: coord];
[ map_view addAnnotation:ann];
//map_view.centerCoordinate = map_view.userLocation.coordinate;
//3.注释表 代理方法 (pin: 大头针 ) MKPinAnnotationView *pin
- ( MKAnnotationView *)mapView:( MKMapView *)mapView viewForAnnotation:( id < MKAnnotation>)annotation
{
static NSString *identifier= @"AnnotationView";
MKPinAnnotationView *pin = ( MKPinAnnotationView *) [mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (pin== nil) {
pin= [[ MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
}
if ( map_view. userLocation==annotation) {
return nil; //如果位置中 :有大头针 返回空
}
UIButton *Btn=[ UIButton buttonWithType: UIButtonTypeDetailDisclosure]; //按钮详情
Btn. frame= CGRectMake( 0, 0, 80, 20);
[Btn setTitle: @" 按钮 " forState: UIControlStateNormal];
UIImageView *imageview=[[ UIImageView alloc] initWithFrame: CGRectMake( 0, 0, 32, 32)];
imageview. image=[ UIImage imageNamed: @"head.png"];
//3.1大头针的颜色
//3.2能够显示弹出气泡
//3.3右视图
//3.4左视图
pin. pinColor= MKPinAnnotationColorGreen;
pin. canShowCallout= YES;
pin. rightCalloutAccessoryView= Btn;
pin. leftCalloutAccessoryView= imageview;
return pin;
}
//4.轻轻点击 :气泡 代理方法
}
//4.渲染器 代理方法 (Renderer:渲染器 )
- ( MKOverlayRenderer *)mapView:( MKMapView *)mapView rendererForOverlay:( id < MKOverlay>)overlay
{
//判断覆盖物类型
if ([overlay isKindOfClass:[ MKPolygon class]]) {
//创建多边形的渲染器 //设置多边形的填充色
//边框,线的颜色 //线条的宽度
MKPolygonRenderer *PR=[[ MKPolygonRenderer alloc] initWithPolygon:( MKPolygon *)overlay];
PR. fillColor= [ UIColor cyanColor];
PR. strokeColor= [ UIColor magentaColor];
PR. lineWidth= 3;
return PR;
}