iOS知识点总结

# 运行时

```

1、程序中任何代码都会被转化成runtime的C代码执行,如[target doSomeThing]会转化成objc_msgSend(tagert,@selector(doSomeThing))

2、OC中一切都是对象,实例,类本质也是对象,在runtime中结构体表示。

3、相关定义

* typedef struct objc_method *Method; 方法

* typedef struct objc_ivar *Ivar;实例变量

* typedef struct objc_category *Category;实例Category

* typedef struct objc_property *objc_property_t;类中声明的属性

4、基于以上,可以实时获取当前类中所有属性,方法,变量

* 获取属性:objc_property_t *propertyList = class_copyPropertyList([self class], &count);

* 获取方法:Method *methodList = class_copyMethodList([self class], &count);

* 获取变量:Ivar *ivarList = class_copyIvarList([self class], &count);

* 获取协议:__unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);

5、方法调用

* 如果用实例对象调用实例方法,会到实例的isa指针指向的对象(类对象)中操作;

* 如果调用的是类方法,会到类对象的isa指针指向的对象(元类对象)中操作。

* 寻找过程:

* 首先在操作对象中的缓存方法列表中寻找

* 若无,在操作对象中的方法列表中寻找

* 若无,去父类指针指向对象中执行1,2

* 依次遍历,若遍历到根类还没有,转向拦截调用(若没有实现,程序报错)

* 拦截调用:

* + (BOOL)resolveClassMethod:(SEL)sel;当调用一个不存在的类方法的时候,会调用此方法,默认返回NO

* + (BOOL)resolveInstanceMethod:(SEL)sel;当调用不存在的实例方法时,会调用次方法。

* - (id)forwardingTargetForSelector:(SEL)aSelector;将你调用的不存在的方法重定向到一个其他声明了这个方法的类,只需要你返回一个有这个方法的target。

* - (void)forwardInvocation:(NSInvocation *)anInvocation; 将你调用的不存在的方法打包成NSInvocation传给你。做完你自己的处理后,调用invokeWithTarget:方法让某个target触发这个方法。

6、关联对象

//首先定义一个全局变量,用它的地址作为关联对象的key

static char associatedObjectKey;

//设置关联对象

objc_setAssociatedObject(target, &associatedObjectKey, @"添加的字符串属性", OBJC_ASSOCIATION_RETAIN_NONATOMIC);

//获取关联对象

NSString *string = objc_getAssociatedObject(target, &associatedObjectKey); NSLog(@"AssociatedObject = %@", string);

7、方法交换

* 通常写在load方法中,保证执行时间靠前,且只执行一次

* // swizzing method for instance

#define swizzing_sel_instance(_cls_, _sel_1, _sel_2) {\

Method method1 = class_getInstanceMethod(_cls_, _sel_1);\

Method method2 = class_getInstanceMethod(_cls_, _sel_2);\

ASSERT(method1 != NULL);\

ASSERT(method2 != NULL);\

LOG(@"swizzing (%@), -%@(0x%08lx) ==> -%@(0x%08lx), ", NSStringFromClass(_cls_), NSStringFromSelector(_sel_1), (long)method1, NSStringFromSelector(_sel_2), (long)method2);\

if (method1 && method2) {\

method_exchangeImplementations(method1, method2);\

}\

}

```

# 内存管理

```

1、只有OC对象才需要管理,非OC对象不需要内存管理(OC对象放在堆内存里,非OC对象放在栈内存里,栈内存里的东西系统会自动管理)

2、自动释放池底层实现:

3、string的内存管理

* NSString *str1 = @"abcde";字符串“abcde”在常量区(有且只有一份),str1指针在栈区。

* NSString *str2 = [NSString stringWithFormat:@"hijkl"];

* 通过stringWithFormat创建的字符串放在堆中(即使字符串内容相同,还是会再次开辟新空间),str2在栈中。

* 通过stringWithFormat创建的字符串,会返回一个autorelease的实例,会在自动释放池自动释放。

* 通过initWithFormt创建的字符串,则需要自己进行手动管理内存

4、copy和Mutablecopy

* copy返回immutable对象,mutableCopy返回mutable对象(所以不可以对mutabe对象使用copy操作)

* NSmutableArray *mArray = [array mutableCopy];(此处的内容拷贝,仅仅是拷贝array这个对象,array集合内部的元素仍然是指针拷贝)

* [immutableObject copy] // 浅复制

* [immutableObject mutableCopy] //单层深复制

* [mutableObject copy] //单层深复制

* [mutableObject mutableCopy] //单层深复制

* 使用总结:

* 修饰可变类型的属性时,如NSMutableArray、NSMutableDictionary、NSMutableString,用strong

* 修饰不可变类型的属性时,如NSArray、NSDictionary、NSString,用copy。

5、Block内存管理

* 默认情况下,Block的内存是在栈中(程序自动管理)

* 如果对block做了copy操作,block的内存会搬到堆中,对引用对象做一次+1操作

* block内部可以一直引用__block修饰的变量,static修饰的变量,全局变量(Block不可以修改外部变量的值,指的是栈中指针的内存地址,__block修饰的变量之所以可以被修改,是因为__block会将栈中的内存地址放到堆中,所以可以修改【static和全局变量在全局初始化区】)

* unsafe_unretained和weak的区别:两者都不持有对象,当引用计数为0时,weak会被置为nil,unsafe_unretained不置为nil(有野指针风险)

```

# 多线程# 动画

```

1、GCD

* 同步:阻塞当前线程

* 异步:不会阻塞当前线程

* 串行:FIFO 依次取出,一个一个执行(主队列)DISPATCH_QUEUE_SERIAL

* 并行:也是FIFO,但是放到不同线程去执行(全局队列)DISPATCH_QUEUE_CONCURRENT

* dispatch_barrier_async:当你传入的 queue 是通过 DISPATCH_QUEUE_CONCURRENT 参数自己创建的 queue 时,这个方法会阻塞这个 queue(注意是阻塞 queue ,而不是阻塞当前线程),一直等到这个 queue 中排在它前面的任务都执行完成后才会开始执行自己

* dispatch_barrier_sync:自定义的并发队列(DISPATCH_QUEUE_CONCURRENT),它和上一个方法一样的阻塞 queue,不同的是 这个方法还会 阻塞当前线程

2、NSOperation

* NSOperation 只是一个抽象类,所以不能封装任务。但它有 2 个子类用于封装任务。分别是:NSInvocationOperation 和 NSBlockOperation ( 默认在当前队列同步执行)

* NSOperationQueue可以设置maxConcurrentOperationCount,可以添加依赖

3、线程同步:防止多个线程抢夺同一块资源

* 互斥锁:@synchronized(self)

* NSLock

取消GCD任务 ?

```

# 常见设计模式

# Runloop

```

1、Runloop的寄生于线程:一个线程只能有唯一对应的runloop;但这个根runloop里可以嵌套子runloops;

2、同一时间一个runloop只能在一个mode,切换mode只能退出runloop,再重进指定mode(隔离modeItems使之互不干扰);

3、timerWithTimeInterval:需要手动加到runloop的mode中

scheduledTimerWithTimeInterval:默认已经添加到主线程的runLoop的DefaultMode中

4、mode类型

* kCFRunLoopDefaultMode: 默认 mode,通常主线程在这个 Mode 下运行。

* UITrackingRunLoopMode: 追踪mode,保证Scrollview滑动顺畅不受其他 mode 影响。

* UIInitializationRunLoopMode: 启动程序后的过渡mode,启动完成后就不再使用。

* GSEventReceiveRunLoopMode: Graphic相关事件的mode,通常用不到。

* kCFRunLoopCommonModes: 占位mode,作为标记DefaultMode和CommonMode用。

5、应用

* 当tableview的cell上有需要从网络获取的图片的时候,滚动tableView,异步线程会去加载图片,加载完成后主线程就会设置cell的图片,但是会造成卡顿。可以让设置图片的任务在CFRunLoopDefaultMode下进行,当滚动tableView的时候,RunLoop是在 UITrackingRunLoopMode 下进行,不去设置图片,而是当停止的时候,再去设置图片。

[self.myImageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@""] afterDelay:ti inModes:@[NSDefaultRunLoopMode]];

* 常驻子线程,保持子线程一直处理事件

为了保证线程长期运转,可以在子线程中加入RunLoop,并且给Runloop设置item,防止Runloop自动退出。

self.downloadRunloop = CFRunLoopGetCurrent();

*  当调用 NSObject 的 performSelecter:afterDelay:后,实际上其内部会创建一个 Timer 并添加到当前线程的 RunLoop 中。所以如果当前线程没有 RunLoop,则这个方法会失效。

```

# 事件响应链条

```

UIApplication-->UIWindow-->递归找到最合适处理的控件-->控件调用touches方法-->判断是否实现touches方法-->没有实现默认会将事件传递给上一个响应者-->找到上一个响应者-->找不到方法作废

1、根据main函数的参数加载UIApplication->AppDelegate->UIWindow->UIViewController->superView->subViews

关系为:UIApplication.keyWindow.rootViewController.view.subView

事件传递机制:

1.当iOS程序中发生触摸事件后,系统会将事件加入到UIApplication管理的一个任务队列中

2.UIApplication将处于任务队列最前端的事件向下分发。即UIWindow。

3.UIWindow将事件向下分发,即UIView。

4.UIView首先看自己是否能处理事件,触摸点是否在自己身上。如果能,那么继续寻找子视图。

5.遍历子控件,重复以上两步。

6.如果没有找到,那么自己就是事件处理者。如果

7.如果自己不能处理,那么不做任何处理。

其中 UIView不接受事件处理的情况主要有以下三种

1)alpha <0.01

2)userInteractionEnabled = NO

3.hidden = YES

2、hittest

UIWindow实例对象会首先在它的内容视图上调用hitTest:withEvent:,此方法会在其视图层级结构中的每个视图上调用pointInside:withEvent:(该方法用来判断点击事件发生的位置是否处于当前视图范围内,以确定用户是不是点击了当前视图),如果pointInside:withEvent:返回YES,则继续逐级调用,直到找到touch操作发生的位置,这个视图也就是要找的hit-test view。

```

# 性能优化 (内存占用,耗电,UI渲染)

1、例如一个黑色半透明的可以设置为一个灰色不透明的View替代.原因是这会使系统用一个最优的方式渲染这些views:如果一个图层是完全不透明的,则系统直接显示该图层的颜色即可。而如果图层是带透明效果的,则会引入更多的计算,因为需要把下面的图层也包括进来,进行混合后颜色的计算

2、永远不要使主线程承担过多。因为UIKit在主线程上做所有工作,渲染,管理触摸反应,回应输入等都需要在它上面完成

3、如果要在UIImageView中显示一个来自bundle的图片,你应保证图片的大小和UIImageView的大小相同,尽可能的先在子线程把图片缩放后再进行赋值。

4、尽量使用懒加载,不要一次性创建所有subview,等用到的时候再进行加载。(NSDateFormatter和NSCalendar初始化就比较慢)

5、处理内存警告

在app delegate中使用applicationDidReceiveMemoryWarning: 的方法

在你的自定义UIViewController的子类(subclass)中覆盖didReceiveMemoryWarning

注册并接收 UIApplicationDidReceiveMemoryWarningNotification 的通知

6、如果你用小图平铺来创建背景,你就需要用UIColor的colorWithPatternImage来做了,它会更快地渲染也不会花费很多内存:

self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background"]];

7、如果你要加载一个大图片而且是一次性使用,那么就没必要缓存这个图片,用imageWithContentsOfFile足矣,这样不会浪费内存来缓存它。

然而,在图片反复重用的情况下imageNamed是一个好得多的选择。


# tableView CollectionView 性能调优

提前计算并缓存好高度(布局),因为heightForRowAtIndexPath:是调用最频繁的方法;

异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口;

滑动时按需加载[快速滑动只展示cell,不加载图片],这个在大量图片展示,网络加载的时候很管用!(SDWebImage已经实现异步加载,配合这条性能杠杠的)。

除了上面最主要的三个方面外,还有很多几乎大伙都很熟知的优化点:

正确使用reuseIdentifier来重用Cells

尽量使所有的view opaque,包括Cell自身

尽量少用或不用透明图层

如果Cell内现实的内容来自web,使用异步加载,缓存请求结果

减少subviews的数量

在heightForRowAtIndexPath:中尽量不使用cellForRowAtIndexPath:,如果你需要用到它,只用一次然后缓存结果

尽量少用addView给Cell动态添加View,可以初始化时就添加,然后通过hide来控制是否显示

NSLog向​​输出添加时间戳和标识符,而println则不会;

NSLog同步日志语句,以便如果您同时发出来自不同线程的日志,则它们将不会彼此重叠; println可能会导致混乱的输出,如果同时从单独的线程执行,而不做一些同步

iOS启动做了哪些事?

1.main 函数

2.UIApplicationMain

创建UIApplication对象

创建UIApplication的delegate对象

delegate对象开始处理(监听)系统事件(没有storyboard)

程序启动完毕的时候, 就会调用代理的application:didFinishLaunchingWithOptions:方法

在application:didFinishLaunchingWithOptions:中创建UIWindow

创建和设置UIWindow的rootViewController

显示窗口

3.根据Info.plist获得最主要storyboard的文件名,加载最主要的storyboard(有storyboard)

创建UIWindow

创建和设置UIWindow的rootViewController

显示窗口

mas_equalTo有自动包装的功能,equalTo没有自动包装功能。

用mas_equalTo可以把基本数据类型转换为对象类型,这个过程叫装箱,比如自动将1包装成@1。

load和initialize

load和initialize方法都会在实例化对象之前调用,以main函数为分水岭,前者在main函数之前调用,后者在之后调用。这两个方法会被自动调用,不能手动调用它们。

load和initialize方法都不用显示的调用父类的方法而是自动调用,即使子类没有initialize方法也会调用父类的方法,而load方法则不会调用父类。

load方法通常用来进行Method Swizzle,initialize方法一般用于初始化全局变量或静态变量。

load和initialize方法内部使用了锁,因此它们是线程安全的。实现时要尽可能保持简单,避免阻塞线程,不要再使用锁。

autoreleasePool

自动释放池可以延长对象的声明周期,如果一个事件周期很长,比如有一个很长的循环逻辑,那么一个临时变量可能很长时间都不会被释放,一直在内存中保留,那么内存的峰值就会一直增加,但是其实这个临时变量是我们不再需要的。这个时候就通过创建新的自动释放池来缩短临时变量的生命周期来降低内存的峰值。

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

推荐阅读更多精彩内容

  • 内容均转自标哥的技术博客 只是按照自己的习惯进行简单的整理 1、对数组中的元素去重复 1.第一种方法:开辟新的内存...
    Kk太阳阅读 5,599评论 0 21
  • runtime 和 runloop 作为一个程序员进阶是必须的,也是非常重要的, 在面试过程中是经常会被问到的, ...
    made_China阅读 1,200评论 0 7
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,678评论 0 9
  • 接《iOS知识点大总结一》 三十四、主线程操作UI(对UI进行更新只能在主线程进行) 解释:所谓的在主线程更新UI...
    RunningMan_Fly阅读 1,403评论 0 0
  • 一、每一个人要有自己的定位 说简单点,就是要清楚自己要做的事情是什么,并为之努力。 每一位来钢琴中心...
    SEER思阅读 412评论 0 0