笔记2

 OC的动态性:会把编译和链接是需要执行的逻辑延迟到运行时,例如使用 id 所修饰的变量会在运行的时候才确定具体类型是什么,runtime 的方法交换等。


循环引用:当一个对象间接或直接地持有另一个对象,而这个对象又有强指针指向该对象,就会引起循环应用无法释放的问题。

1.block的使用 使用 weakSelf处理

2.delegate 作为属性用weak修饰,也可以防止野指针的出现

3.NSTimer 最为成员变量。解决方式

在程序中除了在 dealloc方法去明确使用 invalid 然后赋值为nil

如果无法明确知道什么时候才可以停止,可以添加了一个类别让原来的通过@selector执行任务的方式转换为block的方式。

+ (NSTimer *)xx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval

block:(void(^)())block

repeats:(BOOL)repeats

{

return [self scheduledTimerWithTimeInterval:interval

target:self

selector:@selector(xx_blockInvoke:)

userInfo:[block copy]

repeats:repeats];

}

+ (void)xx_blockInvoke:(NSTimer *)timer {

void (^block)() = timer.userinfo;

if(block) {

block();

}

}

然后当控制释放的时候,停止定时器就可以

-(void)dealloc{

NSLog(@"dealloc");

[self.timer invalidate];

self.timer = nil;

NSLog(@"timer:%@",self.timer);

}


属性修饰

assign:基本数据类型,结构体

weak:delegate(野指针,循环引用),IB引进来的UI控件(视图层级结构)

copy:不可变类型(指向不同对象),block(保证是放在堆上)

strong:修饰对象,强引用

retain:和strong类似,在修饰block的时候作用相当于assign


类别作用:

在保持原有类的情况下,增加其方法(UIScrollView,图片缓存裁剪处理)

讲一个庞大的类根据作用分解到不同的类别中,便于维护


static修饰的变量并不会改变作用范围,改变的只是内存分配方式(存放在静态区在整个运行期间一直存在)


数据持久化:原理都是数据保存到本地文件中

1.属性列表:一般使用NSUserDefaults操作(沙盒library/preference)

2.对象归档:将对象转化为NSData写入带本地文件,对象需要实现NSCoding协议

[NSKeyedArchiver archiveRootObject:self toFile:fileName];

3.SQLite3:FMDB封装库

4.Core Data:对SQLite基于面向对象的封装


栈区(stack)由编译器自动分配释放,存放方法(函数)的参数值,局部变量的值等,是一块连续的内存的区域。即栈顶的地址和栈的最大容量是系统预先规定好的。

堆区(heap)一般由程序员分配释放,是不连续的内存区域,从而堆获得的空间比较灵活。有我们去new/alloc来创建的对象,就是放在堆下面。


栈区(stack)由编译器自动分配释放,存放方法(函数)的参数值,局部变量的值等,是一块连续的内存的区域。即栈顶的地址和栈的最大容量是系统预先规定好的。

堆区(heap)一般由程序员分配释放,是不连续的内存区域,从而堆获得的空间比较灵活。有我们去new/alloc来创建的对象,就是放在堆下面。


结构体:

struct CGPoint {

CGFloat x;

CGFloat y;

};

typedef struct CGPoint CGPoint;


TableViewCell \ 页面优化:

1.尽量减少透明图层的使用来减少渲染次数,opaque = YES

2.减少对象的创建或者使用更为轻的对象,如当视图元素不需要响应用户的触摸事件使用CALayer代替UIView

3.当大量文字需要显示,cpu需要耗费比较多的资源去进行渲染和排版,所以这个时候可以选择CoreText 来处理

4.避免大图使用,程序解码比较耗时

5.复杂布局使用autoLayout会影响性能,更好的是选择手动布局

6.使用CoreGraphics进行绘制的时候,如果绘制比较耗时,可以异步处理后再返回主线程显示

7.TableViewCell的重用,避免在heightForCell方法进行较多的处理,将动态高度的缓存

7.离屏渲染(系统会在屏幕缓冲外开辟新的缓冲去处理),设置shadow时候不设置shadowPath,在设置圆角不同时使用 maskToBound和cornerRdius有时会导致,或者使用CAShapeLayer 和BezierPath结合的的方式绘制圆角。

7.如果不需要要显示透明图层,那么将试图的opaque设置为YES(默认),会让系统会将试图作为不透明去处理而加快渲染。

但如果需要使用到透明,则设置为NO。

8.减少视图的层级结构

4.可以根据需要是使用预加载或者延迟加载。

8.使用Instrument工具去检测


线程加锁 : 解决多线程同时访问同一资源导致的问题,一般通过加锁处理或者使用串行队列处理:

//串行队列

dispatch_queue_create("", DISPATCH_QUEUE_SERIAL), ^ {

}

1.GCD信号量

+(NSString*)getContacts{

//获取通讯录权限

ABAuthorizationStatus authStatus = ABAddressBookGetAuthorizationStatus();

if(authStatus != kABAuthorizationStatusAuthorized)

{

//是否有通讯录权限

__block BOOL accessGranted = NO;

ABAddressBookRef tmpAddressBook = ABAddressBookCreateWithOptions(NULL,NULL);

dispatch_semaphore_t sema=dispatch_semaphore_create(0);//创建信号量 为0

ABAddressBookRequestAccessWithCompletion(tmpAddressBook, ^(bool granted,CFErrorRef error){

accessGranted = granted;

dispatch_semaphore_signal(sema);//信号量 +1

});

//当信号量 > 0 才继续运行,并且信号量 - 1

dispatch_semaphore_wait( sema, DISPATCH_TIME_FOREVER);

if(accessGranted ==NO){

return @"[]";

}

}

//读取联系人----------此处省略联系人读取步骤-------------------}


2.NSLock *lock = [NSLock alloc]init];

[lock lock];

.....

[lock unlock];


3.条件锁(基于信号量)

NSConditionLock *conditionLock = nil;

BOOL canLock = [conditionLock tryLockWhenCondition:kCondition_A];

if (canLock) {

FDLog(@"线程A lock, 请等待");

[conditionLock unlock];

}else{

FDLog(@"线程A 条件不满足,未加lock");

}


4.NSRecursiveLock 递归锁,同一个线程可以多次加锁,但是不会引起死锁,如果是NSLock,则会导致崩溃

- (void)reverseDebug:(NSUInteger )num lock:(NSRecursiveLock *)lock

{

     [lock lock];

    if (num<=0) {

         FDLog(@"结束");

         return;

    }

    [self reverseDebug:num-1 lock:lock];//递归

    [lock unlock];//还是需要解锁

}


5.@synchronized(self) {        

      ...

}

6.atomic的线程安全只限于setter和getter方法。会在setter方法里面加锁。


当我们去启动 RunLoop,系统会自动在内部创建自动释放池。


数组遍历

当使用block的方式进行遍历,会阻塞线程,直到遍历结束。




UIView的 drawRect,layoutSubviews 方法调用时机:

两者都是在视图将要显示(viewWillAppear之后,viewDidAppear之前调用,layoutSubviews只要addSubviews就可以调用,但是drawRect方法必须设置了frame才能调用,否则就不会被调用)

layoutSubviews

1、init初始化不会触发layoutSubviews。

2、addSubview会触发layoutSubviews

3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化

4、滚动一个UIScrollView会触发layoutSubviews

5、旋转Screen会触发父UIView上的layoutSubviews事件

6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件

7、调用setNeedsLayout(延时到下个更新周期调用)


drawRect(在调用前会先调用sizeToFit方法,可以再这个方法计算坐标处理)

1.在UIView初始化时设置rect大小,drawRect 就会自动调用(在视图将要显示时候调用,只调用一次,viewDidLoad两方法之后掉用的),否则不被自动调用。

2.调用setNeedsDisplay(异步执行)


layoutSubviews:UIView 需要对子控件进行手动的布局,则可以重写此方法。只有在autoresizing和constraint-based behaviors of subviews不能提供我们想要的布局结果的时候,我们才应该重写此方法。

layoutSubviews方法调用先于drawRect



MVC优点:

体现在低藕性与重用性:

数据层与视图层的互相独立,可单独地对某一模块进行改变,而不会影响到其余模块,也可以根据实际需要灵活地重用各个模块。



NSOperation与GCD区别:

GCD是基于C语言的一套api;而NSOperaion底层是基于GCD开发,因此效率方面GCD更好。

而NSOperation更适用于一些复杂的任务:

1.可以对队列暂停,恢复,取消操作(NSOperationQueue cancelAllOperations setSuspended),不过这里暂停并不是暂停当前的任务而是下一个任务,恢复会从第一个没有执行的任务开始。

2.可以建立任务间的依赖关系(NSOperation setDependency)

3.NSBlockOperation NSInvocationOperation可以设置任务的优先级(setQueuePriority),

而GCD只可以设置队列的优先级.



TCP三次握手:

客户端向服务端发送请求 - 服务端返回数据包给客户端 - 客户端返回响应包给服务端,连接建立

TCP释放连接四次握手:

客户端向服务端发送断开请求 - 服务端接收到请求后先返回响应给客户端,然后连接断开后再返回一次数据包给客户端- 客户端收到服务端的返回,返回响应给服务端。



9.CoreData多线程安全处理

CoreData包含(实体对象,模型对象,上下文,持久化存储协调器)

线程注意点:

1.manageObject,manageObjectContext只能在自己的线程处理,不能跨线程

2.对于持久化存储协调器(NSPersistentStoreCoordinator)可以多线程共享

3.magageObject的线程间传递通过id,并且通过objectWithID来获取

解决方法:

共同使用一个 NSPersistentStoreCoordinator,以及两个独立的 Contexts,一个context 负责主线程与UI协作,一个context在后台负责耗时的处理,用Notifications的方式通知主线程的NSManagedObjectContext进行mergeChangesFromContextDidSaveNotification 对变化进行合并处理。

//这个通知是系统定义的通知

[[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:nil queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification *note) {

if (note.object == self.privateContext) {

dispatch_async(dispatch_get_main_queue(), ^{

//返回主线程合并处理,这里的note.object不能再主线程进行使用,需要合并处理

[self.mainContext performBlock:^{

[self.mainContext mergeChangesFromContextDidSaveNotification:note];

}];

});

}

}];




14.网络安全:

1.为了防止非法使用api,在每次向服务器请求的使用,将appKey和加密后的appSecret也发送给服务器,服务器根据appKey找到对应的appSecret与从客户端传递过来进行比对,一致的话就是合法请求。(appkey 和 secret key相当于当前账户的另外一套账号和密码机制)

2.使用https协议。



82.常见的crash情况

1.访问野指针,比如访问一个已经释放的对象的属性方法(解决方式是当一个指针所指向的对象如果已被释放,则将指针置nil)

2.访问数组类对象越界或插入了空对象

3.访问了不存在的方法

4.当某个对象会被多个线程修改(加锁处理)

5.NSNotification KVO的非对称添加删除(访问野指针)


数据库事务处理

当我们对数据库进行更新操作时,默认会将操作一条条地往数据库提交。而使用事务处理,则是将所有的操作一次性提交给数据库,当处理的过程中出现问题,则可以进行回滚操作不进行任何修改,保证数据的完整性。

所以当需要对大量的数据进行更新操作时,使用事务在效率和安全性都会大大提高。



16.断点续传

//使用HEAD方法,仅获取目标文件的信息,而不做实际的下载工作。

//[request setHTTPMethod:@"HEAD"];

/**

设置断点续传的思路:

HeaderField:头域(请求头部的字段)

可以通过指定range的范围逐步地下载指定范围内的数据,待下载完成后,再将这些数据拼接成一个文件。

1根据HEAD方法获取到要下载的文件的总大小、

2在磁盘上建立一个临时的缓冲文件,该文件的大小与目标文件大小一致


3缓冲文件中所有字节都是默认为0

4开启多线程,分别加载不同的range头指定的数据块,待数据块加载完成以后,将其分别写入对应的偏移地址。

5所有数据下载完成以后,表示文件下载完成,将临时文件名更改为目标文件。

开发的难点:

0在写入文件之前,首先要建立一个同等大小的文件。

1文件的读写问题,在oc里默认是覆盖,追加,如果要指定位置,需要用seek方法,移动文件指针。

2在多线程写入文件时,文件的锁定操作是一个问题。

*/

[request setValue:@"bytes=0-499" forKeyPath:@"range"];//表di示只读取数据的第0个字节到第499个字节。




线程间通信:

1.performSelectorOnMainThread,去主线程执行

   performSelectorInBackground ,后台线程执行

2.通过NSMachPort端口

将端口添加到当前线程的runloop里,通过这个端口与其他线程进行消息的发送接收(通过delegate 来进行消息接受的回调,通过回调方法传递过来的参数,获取对方的端口,通过端口就可以发送消息)。

NSPort *myPort = [NSMachPort port];

//2. 设置port的代理回调对象myPort.delegate=self;

//3. 把port加入runloop,接收port消息

[[NSRunLoop currentRunLoop] addPort:myPort forMode:NSDefaultRunLoopMode];

- (void)handlePortMessage:(NSMessagePort*)message{

//

NSPort*localPort = [message valueForKeyPath:@"localPort"];

NSPort*remotePort = [message valueForKeyPath:@"remotePort"];

//使用端口发送消息

[remotePort sendBeforeDate:[NSDatedate]

msgid:kMsg2

components:nilfrom:localPort

reserved:0];

}



APP通信方式:

1.URL Scheme:App1通过openURL的方法跳转到App2,并且在URL中带上想要的参数(需要在info.plist中配置好URL types),常用于 分享到微信朋友圈微博等功能。

2.Keychain:微信登陆,使用同一个账号平台,实现自动登录其他的平台。每个程序都有一个独立的keychain存储,只需要使用对应的登录SDK,通过共享keychain中的数据,那么就可以实现统一账户登录了。(比如密码、证书等等,就需要使用更为安全的keychain了。keychain里保存的信息不会因App被删除而丢失,在用户重新安装App后依然有效,数据还在。它是一个sqlite数据库,其保存的所有数据都是加密过的。)

3.UIPasteboard(剪切板功能):在淘宝app中将链接自定义成淘口令,引导用户进行复制,并去QQ好友对话中粘贴。然后QQ好友收到消息进行粘贴后后再打开自己的淘宝app,淘宝app每次从后台切到前台时,就会检查系统剪切板中是否有淘口令,如果有淘口令就进行解析并跳转到对于的商品页面。



绘制方式:

1.使用 drawInContext drawRect 方法,方法会自动创建画布,只需要获取上下文绘制。

2.手动创建画布

UIGraphicsBeginImageContext(CGSizeMake(400,400));

//获取上下文

CGContextRef context =UIGraphicsGetCurrentContext();

//使用CG来绘制

CGFloat lineWidth = 2.f;

CGRect allRect =CGRectMake(0, 0, 100, 100);

CGRect circleRect =CGRectInset(allRect, lineWidth/2.f, lineWidth/2.f);

CGPointcenter =CGPointMake(20, 20);

[[UIColor blueColor]setStroke];

[[UIColor grayColor]setFill];

CGContextSetLineWidth(context, lineWidth);

//绘制

CGContextStrokeEllipseInRect(context, circleRect);

//使用beizer绘制

CGFloat startAngle = - ((float)M_PI/ 2.f);

// Draw progress

UIBezierPath*processPath = [UIBezierPath bezierPath];

processPath.lineCapStyle=kCGLineCapButt;

processPath.lineWidth= lineWidth * 2.f;

CGFloatradius = 30;

CGFloatendAngle = 30 + startAngle;

[processPath addArcWithCenter:centerradius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];

[[UIColor blackColor]set];

[processPath stroke];

//获取绘制的图片

UIImage* im =UIGraphicsGetImageFromCurrentImageContext();

//关闭上下文

UIGraphicsEndImageContext();

//将绘制的图片显示处理

UIImageView*imageView = [[UIImageViewalloc]initWithFrame:CGRectMake(50, 50, 200, 200)];

[self.view addSubview:imageView];

[imageView setImage:im];


socket长连接设计(AsyncSocket):

使用单例模式维持一个长连接,并且定时地向服务器发送心跳包来判断是否在连接中,否则重新连接。客户端需要检测连接状态,通过delegate来对连接状态进行回调,包括:正在连接,连接中,连接断开,重新连接。并且提供对应的方法让用户去进行一些常用的操作,包括:开始连接,退出登录后的断开连接,消息的收发等


当const修饰一个指针变量的时候,可以指定指针不可修挂,或者指针指向的数据不可修改

const int *a(数据不可修改,指针可以)  int *const a(指针可修改,数据不可变)


UIView,UIViewController,UIApplication直接继承自UIResponder,而UIWindow继承自UIView,UIResponder 下面有对应的响应用户触摸的方法



watchdog超时机制:

当应用未能及时响应用户界面事件,如在主线程操作网络,文件操作等耗时操作导致程序卡主了,就会会杀死程序并生成watchdog超时日志。



38.观察者模式:

是一种订阅-发布模式。可以同时对一个对象进行观察,当对象的状态发生改变,观察者就会接收到对应的通知,他的优点在于观察者与消息发布者并不需要知道对方的细节,只需要做好自己的业务,并且观察者的数量并没有限制,是一种低藕的一对多的观察方式。

在ios里典型的就是NSNotification和KVO,前者常用语系统的事件通知,如键盘事件,前后台切换,或者模块之间的通信。KVO就是对对象属性的观察。



struct class 区别:

struct可以继承,可以多态,可以定义函数,和class最大区别在于变量class的默认访问控制是private,而struct默认是public;对于对象内存的管理有系统的回收机制处理,而 struct 定义的对象则是在使用完毕后自动清理分配的内存。

当需要有大量的逻辑处理使用类,如果只是CGPoint,CGSize这些轻量结构,使用struct。



枚举typedef NS_ENUM

使用情景:当需要列举的状态有限的,并且数量不多(3-4左右),使用枚举



沙盒机制(用来存放非代码文件(图片,音频,视频,属性列表(plist), sqlite数据库,文本文件,其他等等)):

Document文件夹用于保存程序运行中需要创建的文件,如sqlite文件

Library Preferences:存放应用的偏好设置(有系统维护,不能直接去修改,需要通过NSUserDefault来添加偏好设置)

Cache:缓存文件夹,在程序退出后不会删除(iTunes不会备份)

tmp:临时文件夹(在文件使用结束后删除),在手机重启目录文件会被删除;在内存底的情况下也可能会被删除,不会被iTunes备份

.app文件包,应用程序本身,不能去修改。

如果你做个记事本的app,那么用户写了东西,总要把东西存起来。那么这个文件则是用户自行生成的,就放在documents文件夹里面。

如果你有一个app,需要和服务器配合,经常从服务器下载东西,展示给用户看。那么这些下载下来的东西就放在library/cache


//获取沙盒主目录路径

NSString *homeDir = NSHomeDirectory();

//获取Documents目录路径

NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];

//获取Library的目录路径

NSString *libDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject];

//获取Caches目录路径

NSString *cachesDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];

//获取tmp目录路径

NSString *tmpDir =NSTemporaryDirectory();


进程

ios中app的运行是单进程的,进程是资源分配的基本单位,而线程是cpu调度的基本单位,是进程中的一个实体。线程没有独立的存储控件,进程下的线程共享该进程下的资源。

进程间通信方式:

1.管道

2.消息队列

3.socket

4.共享内存(常用)



objc_msgForward消息转发

当我们在对一个对象发送消息的时候,运行时调用objc_msgSend方法,根据累的分发表去寻找该方法,如果找不到就去父类找,如果依然找不到就会调用objc_msgForward进行消息转发,首先它会尝试去寻找在运行的时候是否动态地去实现了这个方法,如果没有就去寻找有没有其他的对象可以相应该方法,如果仍然没有,,则会调用forwardInvocation方法来来将消息转发给其他的对象,如果以上三步都没有处理掉就会跑出异常。

(_objc_msgForward是IMP类型,用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候)


BAD_ACCESS在什么情况下出现?

访问了野指针,比如访问已经释放的对象的成员变量或者向他发消息

所以当指针变量所指向的对象内存被释放掉后,需要对指针赋值为nil,防止产生“野指针”。



检测UIViewController内存泄漏的原理:

1、添加一个myDealloc方法并且在里面输出对应的信息,在类方法load 里实现默认的dealloc 与自定义定位方法进行方法交换,那么根据打印处理的信息,就可以判断一个VC有没有释放掉。

2、利用ARC中weak变量不持有对象,并且在对象释放时会自动置为nil的特性,在适当的时候来检测VC是否在内存驻留。


Left Join[左联结]

返回包括左表中的所有记录和右表中联结字段相等的记录

Right Join[右联结]

返回包括右表中的所有记录和右表中联结字段相等的记录

Inner Join[等值联结]

只返回两个表中字段相等的行


sqlite优化:

1.查询优化:索引建立(防止全文检索,防止索引建立太多)

2.大量的更新操作:事务(效率,安全)

3.表的建立(灵活,字段设置)




网络请求中如何提高性能

1.在于服务器交换的数据格式方面,gson要比xml效率高,如果使用xml那么在大量数据的情况下使用基于事件的解析方式要比dom树的方式效率高

2.对请求响应的数据进行压缩 gzip

3.对请求数据进行缓存,可以使用系统默认的NSURLCache 对GET请求进行缓存,只需要在请求时设置好对应的缓存策略。

NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@""] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:3];

connection= [[NSURLConnection alloc] initWithRequest:request delegate:self];

[connection start];


ios 循环引用

1.计时器NSTimer,当我使用计时器时候,内部会有一个引用指向VC,导致VC我发释放,dealloc不会被调用。

2.delegate

3.block



如果赋值没有通过setter方法或者KVC,而是直接修改属性对应的成员变量,例如:仅调用_name = @"newName",这时是不会触发kvo机制



KVO

typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) {

NSKeyValueObservingOptionNew = 0x01,//改变后的值

NSKeyValueObservingOptionOld = 0x02,//改变前的值

NSKeyValueObservingOptionInitial NS_ENUM_AVAILABLE(10_5, 2_0) = 0x04, //addobserving之后会马上调用observeValueForKeyPath,不会等到值改变

NSKeyValueObservingOptionPrior NS_ENUM_AVAILABLE(10_5, 2_0) = 0x08//分2次调用。在值改变之前和值改变之后

};

FOUNDATION_EXPORT NSString *const NSKeyValueChangeNewKey;

FOUNDATION_EXPORT NSString *const NSKeyValueChangeOldKey;

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

if (context == (__bridge void*)self) {

if ([keyPath isEqualToString:kKeyPathForNavigationItemRightBarButtonItems]) {

//取值

NSArray *rightBarButtonItems = [change objectForKey:NSKeyValueChangeNewKey];

}

}

}

当我们通过KVO对对象属性进行监听,如果注册的选项使用NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld,那么在接受消息出会将新和旧的值都会传递过来。如果加上NSKeyValueObservingOptionPrior选项那么在改变值得前后都会个发一次通知。

原来类似于手动触发KVO通知(因为默认下类别添加的属性因缺少setter getter方法而无法使用,通过runtime的关系属性来添加属性,如果需要对属性的改变通过KVO进行监听,因为系统这个时候是不会自动触发通知,所以就需要手动去触发):

[self willChangeValueForKey:@"mj_footer"]; // KVO

objc_setAssociatedObject(self, &MJRefreshFooterKey,//修改属性

mj_footer, OBJC_ASSOCIATION_ASSIGN);

[self didChangeValueForKey:@"mj_footer"];//KVO





72.MJRefresh基本原理

对于使用下拉刷新使用的header刷新控件和下拉加载的footer刷新控件都是将这些控件放在scrollview的可见区域外,而对于下拉加载的footer控件的添加则要判断scrollview的contentSize高度和frame的高度,如果内容是超出可见区域content高度大于frame高度,则将footer空间添加到contentSize高度之后,否则就加在frame的高度之后。

因为对于header还是footer刷新的动作都是通过KVO对scrollview的y轴偏移量变化来处理,而整个下拉上拉的状态可以在:默认状态,正在下拉,刷新中,返回默认状态这几个状态中,所以将他们的这些特性抽象出来他们的父类,在里面添加KVO的消息机制,添加KVO所触发的钩子方法,不做任何实现,具体实现在header,footer的类中根据偏移量的不同是处理不同的状态变化,重写状态属性的setter方法,当偏移量发生改变,在对应的响应方法这里只需要判断并且设置当前刷新状态,而在状态属性的stter方法里,根据状态的不同来设置不同的contentInset,视图的改变。

在这里为了table或者collection的方便添加Header或者footer,添加scrollview的类别,这样就需要在里面使用到下拉刷新的header属性和上啦加载的footer属性,这个时候就是用runtime机制的关系属性的方法来处理,并且在这两个setter方法添加KVO的手动触发通知willChangeValueForKey和didChangeValueForKey。





73.通过UIPanGestureRecognizer实现控件的拖拽效果

-(void)panAction:(UIPanGestureRecognizer *)pan

{

//以self.playerView的左上角为坐标原点

//CGPoint point=[pan locationInView:self.pointView];

//获取的点是以手指按下的点为原点的

CGPoint point1 = [pan translationInView:pan.view];

UIView *targetView = pan.view;

targetView.center = CGPointMake(targetView.center.x + point1.x, targetView.center.y + point1.y);

//清空位移数据,避免拖拽事件的位移叠加

[pan setTranslation:CGPointZero inView:pan.view];

}



数据传值方式

1.变量之间的数据传递

2.指针类型之间的地址传递

3.代理设置模式的数据传值

4.通过的block传递

5.系统通知传值(userInfo)



crash日志分析

在我们每次编译(需要将debug的选项改为dsym)或打包版本时候都会自动产生一个dsym文件,这是一个16进制的函数地址映射文件,首先检查.dsym和.crash文件的UUID是一致的,然后通过这个文件和崩溃日志.crash文件,使用XCODE自带的symbolicatecrash命令行工具,就可以导出一个.log文件,里面就可以让我们定位到程序哪里出问题。所以每次发布版本的时候就需要将.xcarchive文件夹保存下来,里面就有我们所需要的文件。这里还需要注意要。


Time Profiler

按照固定的时间间隔来跟踪每一个线程的堆栈信息,从而让我们方便地看到程序运行过程中各个方法正在消耗CPU时间



id声明的对象具有运行时的特性,知道运行时才回去决定他的类型,即可以指向任意继承自NSObject的对象



82.判断cell是否在屏幕内

1. 获取当前cell对于tableview的位置 rectForRowAtIndexPath

2.获取table y轴上的偏移量,将cell的frame的y坐标 - 偏移量,获取相对于内容视图的位置

3.可视区域为table 的frame,然后通过函数 CGRectIntersectsRect 判断两个位置是否存在重叠

4.这里如果是加入到导航栏中的table,还需要考虑到contentInset内编剧


CGFloat offsetY = self.tableView.contentOffset.y;

CGRect contentRect = self.tableView.frame;

CGRect rectCell = [self.tableView rectForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];

rectCell.origin.y = rectCell.origin.y - offsetY;

if(CGRectIntersectsRect(contentRect, rectCell)){

NSLog(@"再屏幕内");

}else{

NSLog(@"--不再屏幕内");

}



解决UITableView中Cell重用机制导致内容出错的方法总结

1.不使用重用机制

// UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; //改为以下的方法

UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; //根据indexPath准确地取出一行

每个cell指定不同的重用标识符

2.内容出错往往是加载过慢导致内容没有及时地更新,可以使用缩略图,显示默认图片,预加载进缓存中等方式处理。


哪些途径可以让ViewController瘦下来

1.将视图处理单独封装(如各种窗口,cell)

2.将业务逻辑的处理,如网络数据库处理等可以放到对应的model里进行



Objective-C如何对【已有的方法】,添加自己的功能代码以实现类似记录日志这样的功能?

1.创建一个方法,在里面调用原有的方法,并且加上记录功能

2.使用runtime 的方法交换,对原有方法和自定义的方法进行交换。


CADisplayLink保持着和屏幕刷新率形同的频率进行回调

//创建对象

self.progressObjectDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateProgressFromProgressObject)];

//注册到runloop中

[_progressObjectDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];//停止

[self.displayLink invalidate];

self.displayLink = nil;

对于一些需要高频率刷新以致到达流畅效果的,如动画,绘制,视频画面等,一般使用TA。



当我们使用readonly去修饰属性,意味着不会生成setter方法,那么通过访问方式如obj.name self.name这些实际就是访问setter方法,但是因为没有所以这样调用会报错,所以修改属性的方法就是在类内部使用_name这种方式去修改。当然可以用KVC的方式去修改(setter方法->accessInstanceVariablesDirectly yes->成员变量_name _isName name isName->setValues forUndefineKey该方法默认抛异常,可以去重写)

查找:

get<key>,<key>,is<key> 的顺序方法查找getter方法,如果没找到回去找类似于<key>AtIndex类似的数组方法。

+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默认行为),那么和先前的设值一样,会按_key,_isKey,key,isKey 的顺序搜索成员变量名,这里不推荐这么做,因为这样直接访问实例变量破坏了封装性,使代码更脆弱。如果重写了类方法+(BOOL)accessInstanceVariablesDirectly返回NO的话,那么会直接调用valueForUndefinedKey:

还没有找到的话,调用valueForUndefinedKey:


什么时候用@autoreleasepool

根据Apple的文档,使用场景如下:

1.写循环,循环里面包含了大量临时创建的对象。(本文的例子,相册,本地文件的遍历就会产生比较多开销较大的对象,使用block来遍历,在遍历的时候会在内部创建自动释放池来对对象及时释放)

2.长时间在后台运行的任务。(如长时间运行在后台的网络服务)



97.github + xcode结合使用

特点:

分布式:可以将代码提交到本地代码库,在确定后再更新到服务器上。

流程:

一般在开发中,多人合作开发的时候,版本控制非常重要,所以一定要有稳定的主分支Master,开发功能的分支developer,预发布的分支release这三个重要分支。每次有新功能和需求的时候每个开发人员就从developer分支分别拉取项目开发,最后合并入developer,功能完成后就并入release,修改bug时在release分支操作,修复完成后分别并入Master和developer分支,最后从Master分支拉取最终的代码打包上传APP Store

常用命令:

commit 将代码提交到本地代码库

push 将本地代码库提交到服务器

pull 将服务器代码更新到本地上

merge 分之合并


viewController的生命周期

初始化:

1.initWithNibName(通过代码调用,如present,pushNavigation)

2.initWithCoder(如果使用 storyboard 调用VC,VC这个时候是放在storyboard中),然后调用 awakeFromNib

如果view为 nil  

loadView (系统通过代码方式创建一个空的view),如果自己覆盖,则需要同样按照系统的方法去写

UIView *view = [[UIView alloc]initWithFrame:[UIScreen mainScreen].bounds];

self.view = view;

不要使用 [super loadView]


viewDidLoad:view加载完毕

viewWillAppear:控制器的view将要显示

viewWillLayoutSubviews:控制器的view将要布局子控件

viewDidLayoutSubviews:控制器的view布局子控件完成

这期间系统可能会多次调用viewWillLayoutSubviews 、    viewDidLayoutSubviews 俩个方法

viewDidAppear:控制器的view完全显示

viewWillDisappear:控制器的view即将消失的时候

viewDidDisappear:控制器的view完全消失的时候


loadView viewDidLoad

loadView方法在控制器的view为Nil的时候会调用,若控制器有关联的 Xib 文件,该方法会从 Xib 文件中加载 view;如果没有,则创建空白 UIView 对象。

如果用storyboard初始化控制器,就不用调用loadview方法了。如果重写这个方法给控制器创建view则这个view必须是一个单例,而且不能被其他的控制器使用.并且不可以调用super。

不建议使用loadview,可以根据自己的需要在storyboard或者viewdidload中创建自己需要的view给控制器,如果使用 Interface Builder 创建 view,则务必不要重写该方法。

viewDidlLoad :view 被加载到内存后调用,不管什么情况都会被调用,用于视图的初始化。


集合遍历方法

1.对于数据量比较大,或者在便利过程中会产生一些消耗较大的临时,使用block的形式遍历更好,因为使用多线程的方式,并且内部会自动创建一个autoreleasepool,对临时创建的对象及时释放。

2.一般的遍历,如果不需要使用到下标,那么使用for in的方式更直观效率也高。

3.对于block的数组方式,除了NSEnumerationConcurrent这种会在子线程内遍历,其余都是在主线程遍历。NSEnumerationConcurrent使用了GCD的group原理,当遍历完成,返回主线程


常量

define 宏:只是在预处理器里进行文本替换,不能声明类型,会多次地分配内存。除了定义常量,还可以定义函数宏

#define MIN(A,B) A < B ? A : B

const 常量:只分配一次内存

修饰局部变量

static const (内存分配在静态区,在运行周期中保持一份)

声明全局常量:

extern NSString *const kName 

UIKIT_EXTERN

FOUNDATION_EXPORT


autorelease 对象释放

1.autorelease 本质上就是延迟调用 release,autorelease pool,其实这个pool本质上是一个stack,扔到pool中的对象等价于入栈

2.默认下,对象会在当前runloop迭代结束的时候释放。

viewDidAppear 调用之前,NSAutoreleasePool会在当前runLoop迭代结束的时候被销毁,向池中的对象发送release消息,并且pop弹出栈中。

3.手动指定 @autoreleasepool {},当出了@autoreleasepool {}的作用域时,当前autoreleasepool被drain,其中的autoreleased对象被release

4.对于每一个Runloop,系统会隐式创建一个Autorelease pool(自然会有多个Autorelease pool),这样所有的release pool会构成一个象CallStack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的Autorelease pool会被销毁,这样这个pool里的每个Object会被release。

- (void)viewDidLoad {

[superviewDidLoad];

//    @autoreleasepool {

//        NSString *string = [NSString stringWithFormat:@"leichunfeng"];//对象创建时计数器+1,有变量指向它,+1,计数器为= 2

//        string_weak_ = string;

//    }

//出了池的作用域,池被释放,对象计数器 - 1,局部变量为nil,计数器-1,这时候对象被释放


//    NSString *string = nil;

//    @autoreleasepool {

//        string = [NSString stringWithFormat:@"leichunfeng"];

//        string_weak_ = string;

//    }

出了池的作用域,池被释放,对象计数器 - 1,局部变量这个时候还存,所以只有在viewdidload结束的时候,才会置nil,计数器-1,这时候对象被释放,所以对象在viewdidload内还存在,在viewwillappear才会是nil

NSLog(@"string: %@", string_weak_);

}



block对象就是一个结构体,里面有isa指针指向自己的类(global malloc stack),有desc结构体描述block的信息,引用到的__block变量,最重要的block结构体有一个函数指针,指向block代码块。


KVO,NSNotification Delegate都是同步发送消息的


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容

  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,107评论 29 470
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,050评论 25 707
  • iOS网络架构讨论梳理整理中。。。 其实如果没有APIManager这一层是没法使用delegate的,毕竟多个单...
    yhtang阅读 5,144评论 1 23
  • 望着这一张不成样子的照片,我心窃喜。 校园渐渐变得安静了,十点一过,该是学生熄灯休息的时间点了。 ...
    一个南瓜先生阅读 791评论 0 3
  • 马甲线 ——懒惰是劣质生活的导火索!SO关注健康,认真生活从现在开始! 关注健康 ▼ 规律饮食 早餐──7:00(...
    马甲线app阅读 298评论 0 0