2016.8.18
UITextField 两个属性:
- inputView 自定义键盘
- inputAccessoryView 键盘附属视图
iOS 推送
APNS 的 推送机制
当你的iPhone收到推送信息后到底会发生什么呢?总共有三种可能性:
app在前台运行. 接收到推送信息时屏幕上不会有任何显示,也不会有提示音,但你的app delegate会收到这个推送信息。你可以在这里加入代码来处理接收到的信息。
app不在前台运行。iPhone可能停留在主界面或者另一个app正在运行.一个提示窗口会弹出,可能伴随着提示音。用户可以点击Close按钮来关闭这个窗口或者点击View按钮来打开你的app。如果用户点击的时Close按钮,那你的app不会处理这个推送的信息。
iPhone在锁屏状态下. 同样一个提示窗口弹出,并伴随着提示音,但是这个窗口不会有Close和View按钮。屏幕解锁后会自动进入你的app。
参考
小知识 1.0
cocopods 一些 问题
出现 Setting up CocoaPods master repo,说明 Cocoapods 在将它的信息下载到 ~/.cocoapods 里;
cd 到该目录里,用 du -sh *
命令来查看文件大小,
每隔几分钟查看一次,这个目录最终大小是100多M( 本人这里大概 800M + ),就是完成了
其他
将坐标系上下翻转。对于底层的绘制引擎来说,屏幕的左下角是(0, 0)坐标。而对于上层的 UIKit 来说,左上角是 (0, 0) 坐标。所以我们为了之后的坐标系描述按 UIKit 来做,所以先在这里做一个坐标系的上下翻转操作。翻转之后,底层和上层的 (0, 0) 坐标就是重合的了。
应用间跳转
打包静态库相关
UIImageView 上下和左右翻转
1 | # 左右 |
其他
深入研究 Block 用 weakSelf、strongSelf、@weakify、@strongify 解决循环引用
UITableViewStyleGrouped和plain的两种格式的区别
快速用Storyboard创建一个UICollectionView
macOS: 使用新版plutil命令操作Plist文件更方便
PhotoKit
照片库中有两种资源可供获取:PHAsset 和 PHCollection,前者代表图像或视频对象,后者是前者的集合或自身类型的集合。PHCollection是个基类,有PHAssetCollection和 PhCollectionList两个子类,分别代表Photos里的相册和文件夹。以往使用Photos时,并没有注意到可以建立文件夹,似乎是从Photos才支持这个功能,而 PHCollectionList里可嵌套PHAssetCollection和自身类型,还支持多重嵌套。获取PhAsset以及PHAssetCollection的过程类似于Core Data,如下所示,只能通过类方法来返回PHFetchResult,遍历返回的结果来获取需要的资源。
小知识 2.0
关于为什么我在 使用 SDWebImage 的过程中,为何没有出现 tableView 卡顿和内存泄露的原因
首先,我们理解一个概念:图片加载的三级缓存:1.内存缓存 优先加载,速度最快。2.本地缓存 次优先加载 速度稍快 3.网络缓存 最后加载 速度由网络速度决定(浪费流量)
第二,结合图片的三级缓存,SDWebImage 的 图片加载原理为:1.异步加载 2根据 URL 开始处理图片 3.从缓存查找图片是否已经下载 (先从内存缓存开始搜索,然后是本地缓存,注意:从本地缓存读取到照片后,并不立即返回,而是加载到内存缓存)4.缓存找不到,就从URL下载,并且本地和内存缓存同步
第三,我在使用的时候,是使用了cell的重用的,sd_setImage的方法是在刷新cell的地方调用的,而因为三级缓存的机制,无论怎样重用,都不会重复加载,但是如果滑动速度很快,而且Cell的个数足够多的话,会导致内存暴增,这时候,应该采用“不定时”调用清除内存缓存的方法
参考
使用SDWebImage加载大量图片后造成内存泄露的解决办法
使用SDWebImage和YYImage下载高分辨率图,导致内存暴增的解决办法
其他
iOS开发CoreGraphics核心图形框架之七——图像处理
苹果官方Apple Pay开发文档(中文版):开始使用Apple Pay
iOS利用voip push实现类似微信(QQ)电话连续响铃效果
我所知道的java中的socket值得是套接字,就是你的tcp之类的请求, websocket是在http协议的基础上对socket的升级封装,作用是实现你的客户端和服务端的通信,就相当于你做聊天的话,如果是不用这个东西,你的客户端是不是要做到和服务端的连接请求,一直不断的连他,看看有没有数据,然后拿到这个数据, websocket的话就是你的客户端和服务端建立起来一个链接,比如我发送一个消息给客户端,然后客户端自动把这个消息发给你了,不需要你一直链接服务端。mq的话是做消息队列的,里面两种模式,这个相当于,你给我发了很多的消息,我可以一个一个的点开,慢慢处理事情,就差不多是这么个意思
为什么会需要消息队列(MQ)?
主要原因是由于在高并发环境下,由于来不及同步处理,请求往往会发生堵塞,比如说,大量的insert,update之类的请求同时到达MySQL,直接导致无数的行锁表锁,甚至最后请求会堆积过多,从而触发too many connections错误。通过使用消息队列,我们可以异步处理请求,从而缓解系统的压力。
用mq可以避免消息的遗失,就打比方很多人同事给我发东西,我可能好多消息就没接收到,或者接受到了立马就被挤下去了,mq可以解决这个问题
几道题目
1.什么是arc?(arc是为了解决什么问题诞生的?)
答:首先解释ARC: automatic reference counting 自动引用计数。
ARC几个要点:
在对象被创建时 retain count +1,在对象被release时 retain count -1.当retain count 为0 时,销毁对象。
程序中加入autoreleasepool的对象会由系统自动加上autorelease方法,如果该对象引用计数为0,则销毁。
那么ARC是为了解决什么问题诞生的呢?这个得追溯到MRC手动内存管理时代说起。
MRC下内存管理的缺点:
当我们要释放一个堆内存时,首先要确定指向这个堆空间的指针都被release了。(避免提前释放)
释放指针指向的堆空间,首先要确定哪些指针指向同一个堆,这些指针只能释放一次。(MRC下即谁创建,谁释放,避免重复释放)
模块化操作时,对象可能被多个模块创建和使用,不能确定最后由谁去释放。
多线程操作时,不确定哪个线程最后使用完毕
2.请解释以下keywords的区别: assign vs weak, __block vs __weak
答:assign 适用于基本数据类型,weak 是适用于 NSObject 对象,并且是一个弱引用。
assign 其实也可以用来修饰对象,那么我们为什么不用它呢?因为被 assign 修饰的对象在释放之后,指针的地址还是存在的,也就是说指针并没有被置为nil。如果在后续的内存分配中,刚好分到了这块地址,程序就会崩溃掉。
而 weak 修饰的对象在释放之后,指针地址会被置为 nil。所以现在一般弱引用就是用weak。
首先 __block 是用来修饰一个变量,这个变量就可以在 block 中被修改(参考block实现原理)
__block:使用__block修饰的变量在 block 代码快中会被 retain(ARC下,MRC下不会retain)
__weak:使用 __weak 修饰的变量不会在 block 代码块中被 retain
同时,在 ARC 下,要避免 block 出现循环引用 __weak typedof(self)weakSelf = self
3.__block在arc和非arc下含义一样吗?
答:是不一样的。
在MRC中__block variable在block中使用是不会retain的
但是ARC中__block则是会Retain的。
取而代之的是用__weak或是__unsafe_unretained來更精确的描述weak reference的目的
其中前者只能在iOS5之后可以使用,但是比较好 (该物件release之后,此pointer会自动设成nil)
而後者是ARC的环境下为了相容4.x的解決方案。
4.使用atomic一定是线程安全的吗?
答:不是的。
atomic原子操作,系统会为setter方法加锁。 具体使用 @synchronized(self){//code }
nonatomic不会为setter方法加锁。
atomic:线程安全,需要消耗大量系统资源来为属性加锁
nonatomic:非线程安全,适合内存较小的移动设备
5.描述一个你遇到过的retain cycle例子。(别撒谎,你肯定遇到过)
答:循环引用
6.+(void)load; +(void)initialize;有什么用处?
答:在OC中,runtime会自动调用每个类的两个方法。+load会在类初始加载时调用,+initialize会在第一次调用类的类方法或实例方法之前被调用。这两个方法是可选的,且只有在实现了它们时才会被调用。
共同点:两个方法都只会被调用一次。
7.为什么其他语言里叫函数调用, objective c里则是给对象发消息(或者谈下对runtime的理解)
答:http://blog.csdn.net/hanangellove/article/details/45033453
8.什么是method swizzling?
答:见我的简书
9.UIView和CALayer是啥关系?
答:UIView是iOS系统中界面元素的基础,所有的界面元素都继承自它。它本身完全是由CoreAnimation来实现的 (Mac下似乎不是这样)。它真正的绘图部分,是由一个叫CALayer(Core Animation Layer)的类来管理。 UIView本身,更像是一个CALayer的管理器,访问它的跟绘图和跟坐标有关的属性,例如frame,bounds等 等,实际上内部都是在访问它所包含的CALayer的相关属性。
UIView有个layer属性,可以返回它的主CALayer实例,UIView有一个layerClass方法,返回主layer所使用的 类,UIView的子类,可以通过重载这个方法,来让UIView使用不同的CALayer来显示,例如通过
- (class) layerClass {
return ([CAEAGLLayer class]);
}
使某个UIView的子类使用GL来进行绘制。
10.如何高性能的给UIImageView加个圆角?(不准说layer.cornerRadius!)
答:用 Quartz2D
11.使用drawRect有什么影响?(这个可深可浅,你至少得用过。。)
12.ASIHttpRequest或者SDWebImage里面给UIImageView加载图片的逻辑是什么样的?(把UIImageView放到UITableViewCell里面问更赞)
13.麻烦你设计个简单的图片内存缓存器(移除策略是一定要说的)
14.讲讲你用Instrument优化动画性能的经历吧(别问我什么是Instrument)
15.loadView是干嘛用的?
16.viewWillLayoutSubView你总是知道的。。
答:横竖屏切换的时候,系统会响应一些函数,其中 viewWillLayoutSubviews 和 viewDidLayoutSubviews。
注意:viewWillLayoutSubviews只能用在ViewController里面,在view里面没有响应。
17.GCD里面有哪几种Queue?你自己建立过串行queue吗?背后的线程模型是什么样的?
答:主队列 dispatch_main_queue(); 串行 ,更新UI
全局队列 dispatch_global_queue(); 并行,四个优先级:background,low,default,high
自定义队列 dispatch_queue_t queue ; 可以自定义是并行:DISPATCH_QUEUE_CONCURRENT或者串行DISPATCH_QUEUE_SERIAL
18.用过coredata或者sqlite吗?读写是分线程的吗?遇到过死锁没?咋解决的?
19.http的post和get啥区别?(区别挺多的,麻烦多说点)
20.我知道你大学毕业过后就没接触过算法数据结构了,但是请你一定告诉我什么是Binary search tree? search的时间复杂度是多少?我很想知道!
关于CGBitmapContextCreate的认识
一般情况下,一个像素点所占内存为4个字节
1 | CGContextRef CGBitmapContextCreate ( |
参数data指向绘图操作被渲染的内存区域,这个内存区域大小应该为(bytesPerRow*height)个字节。如果对绘制操作被渲染的内存区域并无特别的要求,那么可以传递NULL给参数data。
参数width代表被渲染内存区域的宽度。
参数height代表被渲染内存区域的高度。
参数bitsPerComponent被渲染内存区域中组件在屏幕每个像素点上需要使用的bits位,举例来说,如果使用32-bit像素和RGB颜色格式,那么RGBA颜色格式中(例如R这应该是一个组件吧,我暂时的理解)在屏幕每个像素点上需要使用的bits位就为32/4=8。
参数bytesPerRow代表被渲染内存区域中每行所使用的(字节)bytes位数(一般情况下,4 * 宽度)。
参数colorspace用于被渲染内存区域的“位图上下文”(一般情况下,RGB空间)。
参数bitmapInfo指定被渲染内存区域的“视图”是否包含一个alpha(透视)通道以及每个像素相应的位置,除此之外还可以指定组件式是浮点值还是整数值。
HTTP权威指南(阅读笔记)
HTTP:Web的基础
Web浏览器,服务器和相关的Web应用程序都是通过HTTP相互通信的。HTTP是现代全球因特网中使用的公共语言
浏览器 向 服务器 HTTP 解析过程
1.浏览器从URL中解析出服务器的主机名
2.浏览器将服务器的主机名转换成服务器的IP地址
3.浏览器将端口号(如果有的话)从URL中解析出来
4.浏览器建立一条与Web服务器的TCP连接
5.浏览器向服务器发送一条HTTP请求报文
6.服务器向浏览器回送一条HTTP响应报文
7.关闭连接,浏览器显示文档
几个词语:
代理:位于客户端和服务器之间的HTTP中间实体
缓存:HTTP的仓库,使常用页面的副本可以保存在离客户端更近的地方
网关:连接其他应用程序的特殊Web服务器
隧道:对HTTP通信报文进行盲转发的特殊代理
Agent代理:发起自动HTTP请求的半智能Web客户端
CoreGraphics学习笔记
概述:
CoreGraphics也称为Quartz 2D ,是UIKit下的主要绘图系统,频繁的用于绘制自定义视图。Core Graphics是高度集成于UIView和其他UIKit部分的。Core Graphics数据结构和函数可以通过前缀CG来识别。
注意:
视图可以通过子视图、图层或实现drawRect:方法来表现内容,如果说实现了drawRect:方法,那么最好就不要混用其他方法了,如图层和子视图。自定义绘图大部分是由UIKit或者Core Graphics来实现的。
2D绘图一般可以拆分成以下几个操作:
1.线条
2.路径
3.文本
4.图片
5.渐变
重要:由于像素是依赖于目标的,所以2D绘图并不能操作单独的像素,我们可以从上下文(Context)读取它。
绘图就好比在画布上拿着画笔机械的进行画画,通过制定不同的参数来进行不同的绘制。
一. 绘入字符串
调用NSString类里的实例方法drawAtPoint: withFont:方法可以进行绘制。
我们可以遍历设备支持的字体来随机选择一个,然后绘制到drawRect中。
二. 连续绘制线条,并设置交界样式
上下文在绘制一条直线后会定格在刚才绘制的线的末端,这样我们可以再通过CGContextAddLineToPoint方法来设置立即进行下一条线的绘制。在线的交界处,我们可以设置交界的样式(CGLineJoin),这个枚举中有三种样式:
kCGLineJoinMiter——尖角
kCGLineJoinBevel——平角
kCGLineJoinRound——圆形
三.绘制线性渐变效果
绘制渐变效果首相要创建一个CGGradientRef,创建它需要调用一个方法CGGradientRef CGGradientCreateWithColors(
CGColorSpaceRef space,
CFArrayRef colors,
const CGFloat locations[]
);
需要一个色彩空间梯度,还有颜色的数组以及只读位置数组。
首先先把之前填充颜色的代码注释掉,然后开始绘制渐变。
几个问题:
1.绘图需要 CGContextRef
CGContextRef即图形上下文。可以这么理解,我们绘图是需要一个载体或者说输出目标,它用来显示绘图信息,并且决定绘制的东西输出到哪个地方。可以形象的比喻context就像一个“画板”,我们得把图形绘制到这个画板上。所以,绘图必须要先有context。
2.怎么拿到context?
第一种方法是利用cocoa为你生成的图形上下文。当你子类化了一个UIView并实现了自己的drawRect:方法后,一旦drawRect:方法被调用,Cocoa就会为你创建一个图形上下文,此时你对图形上下文的所有绘图操作都会显示在UIView上。
第二种方法就是创建一个图片类型的上下文。调用UIGraphicsBeginImageContextWithOptions函数就可获得用来处理图片的图形上下文。利用该上下文,你就可以在其上进行绘图,并生成图片。调用UIGraphicsGetImageFromCurrentImageContext函数可从当前上下文中获取一个UIImage对象。记住在你所有的绘图操作后别忘了调用UIGraphicsEndImageContext函数关闭图形上下文。
简言之:
1.重写UIView的drawRect方法,在该方法里便可得到context;
- 调用UIGraphicsBeginImageContextWithOptions方法得到context;
3.注意
并不是说一提到绘图,就一定得重写drawRect方法,只是因为通常情况下我们一般采用在drawRect方法里获取context这种方式。
4.drawRect方法什么时候触发
1.当view第一次显示到屏幕上时;
2.当调用view的setNeedsDisplay或者setNeedsDisplayInRect:方法时。
步骤:
1.先在drawRect方法中获得上下文context
2.绘制图形(线,图形,图片等)
3.设置一些修饰属性
4.渲染到上下文,完成绘图
web学习
iphone X 及iOS11
iOS11 UItableView新特性:performsFirstActionWithFullSwipe,一滑到底
宏
算法小记
1.选择排序法:第一轮从下标为 0 到下标为 n-1 的元素中选取最小值,若小于第一个数,则交换;第二轮从下标为 1 到下标为 n-1 的元素中选取最小值,若小于无序列的第一个,也就是下标为1的值,则交换;以此类推
2.冒泡排序法:将前后每两个数进行比较,较大的数往后排,一轮下来最大的数就排到最后去了。然后再进行第二轮比较,第二大的数也排到倒数第二了,以此类推
socket
c++ primer 读后笔记
拷贝控制
当定义一个类时,我们显式地或隐式地指定在此类型地对象,移动,赋值和销毁时做什么。一个类通过定义五种特殊地成员函数来控制这些操作,包括:
拷贝构造函数(copy constructor)
拷贝赋值运算符(copy-assignment operator)
移动构造函数(move constructor)
移动赋值运算符(move-assignment operator)
析构函数(destructor)
拷贝构造函数和移动构造函数 定义了 当用同类型地另一个对象初始化本对象时做什么
拷贝和移动赋值运算符 定义了 将一个对象赋予同类型地另一个对象时做什么
析构函数 定义了 当此类型对象销毁时做什么
以上操作 称为 拷贝控制操作
拷贝构造函数:如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数
c++中的explicit关键字只能用于修饰只有一个参数的类构造函数,它的作用是表明该构造函数是显式的,而非隐式的,跟它对应的另一个关键字式implicit,意思是隐式的,类构造函数默认情况下即声明为implicit(隐式)
1.1 合成拷贝构造函数
如果我们没有为一个类定义拷贝构造函数,编译器会为我们定义一个
与合成默认构造函数不同,即使我们定义了其他构造函数,编译器也会为我们合成一个拷贝构造函数
直接初始化:要求编译器使用普通的函数匹配来选择与我们提供的参数最匹配的构造函数
拷贝初始化:要求编译器将右侧运算对象拷贝到正在创建的对象中,如果需要的话,还要进行类型转换
拷贝初始化不仅在我们用=定义变量时会发生,在下列情况 也会发生:
- 将一个对象作为实参传递给一个非引用类型的形参
- 从一个返回类型为非引用类型的函数返回一个对象
- 用花括号列表初始化一个数组中的元素或一个聚合类中的成员
- 某些类类型还会对它们所分配的对象使用拷贝初始化,例如调用标准库的insert或push成员时,容器会对其元素进行拷贝初始化,而emplace成员创建的元素则是进行直接初始化
1.2 参数和返回值
在函数调用过程中,具有非引用类型的参数要进行拷贝初始化
当一个函数具有非引用的返回类型时,返回值会被用来初始化调用方的结果
1.3 重载赋值运算符
重载运算符本质上是函数,其名字由operator关键字后接表示要定义的运算符的符号组成
赋值运算符就是一个名为operator=的函数,也有返回类型和参数列表
重载运算符的参数表示运算符的运算对象
1.4 析构函数
构造函数初始化对象的非static数据成员,还可能做一些其他工作
析构函数释放对象使用的资源,并销毁对象的非 static数据成员
析构函数是类的一个成员函数,名字由波浪号+类名构成,没有返回值,也不接受参数
在一个构造函数中,成员的初始化是在函数体执行之前完成的,且按照它们在类中出
在一个析构函数中,首先执行函数体,然后销毁成员,成员按照初始化顺序的逆序销毁
在对象最后一次使用之后,析构函数的函数体可执行类设计者希望执行的任何收尾工作。通常,析构函数释放对象在生存期分配的所有资源
什么时候调用析构函数:
1.变量在离开其作用域时被销毁
2.当一个对象被销毁时,其成员被销毁
3.容器被销毁时,其元素被销毁
4.对于动态分配的对象,当对指向它的指针应用delete运算符时被销毁
5.对于临时对象,当创建它的完整表达式结束时被销毁
重载运算与类型转换
基本概念:
重载的运算符是具有特殊名字的函数:它们的名字由关键字operator和其后要定义的运算符号共同组成。和其他函数一样,重载的运算符也包含返回类型,参数列表以及函数体
重载运算符函数的参数数量与该运算符作用的运算对象数量一样多。一元运算符有一个参数,二元运算符有两个
对于二元运算符来说,左侧运算对象传递给第一个参数,而右侧运算对象传递给第二个参数。除了重载的函数调用运算符operator()之外,其他重载运算符不能含有默认实参。
如果一个运算符函数是成员函数,则它的第一个(左侧)运算对象绑定到隐式的this指针上,因为,成员运算符函数的(显式)参数数量比运算符的运算对象总数少一个
当一个重载的运算符是成员函数时,this绑定到左侧运算对象。成员运算符函数的(显式)参数数量比运算对象的数量少一个
面向对象程序设计
面向对象程序设计三个理念:数据抽象,继承,动态绑定
继承和动态绑定对程序的编写有两方面的影响:
1.我们可以更容易地定义与其他类相似但不完全相同的新类。
2.在使用这些彼此相似的类编写程序时,我们可以在一定程度上忽略它们的区别
继承(inheritance):
通常在层次关系的根部有一个基类(base class),其他类则直接或间接地从基类继承而来,这些继承得到地类称为派生类(derived class)
在c++语言中,基类将类型相关的函数与派生类不做改变直接继承的函数区别对待。对于某些函数,基类希望它的派生类各自定义适合自身的版本,此时就将这些函数声明成虚函数(virtual function)
派生类必须通过使用类派生列表(class derivation list)明确指出它是从哪个(哪些)基类继承而来的。每个基类前面可以有访问说明符
动态绑定(dynamic binding): 在运行时选择函数的版本
基类通常都应该定义一个虚析构函数,即使该函数不执行任何实际操作也是如此
成员函数与继承:
派生类可以继承其基类的成员,然而当遇到与类型相关的操作时,派生类必须对其重新定义。
派生类需要对这些操作提供自己的新定义以覆盖(override)从基类继承而来的旧定义
在c++语言中,基类必须将它的两种成员函数区分开:
1.基类希望其派生类进行覆盖的函数
2.基类希望派生类直接继承而不要改变的函数
第一种,基类通常将其定义为虚函数(virtual)
当我们使用指针或引用调用虚函数时,该调用将被动态绑定,根据引用或指针所绑定的对象类型不同,该调用可能执行基类的版本,也可能执行某个派生类的版本
基类通过在其成员函数的声明语句之前加上关键字virtual使得该函数执行动态绑定。任何构造函数之外的非静态函数都可以是虚函数。
关键字virtual只能出现在类内部的声明语句之前而不能用于类外部的函数定义。
如果基类把一个函数声明成虚函数,则该函数在派生类中隐式地也是虚函数
成员函数如果没被声明为虚函数,则其解析过程发生在编译时而非运行时
访问控制与继承:
派生类可以继承定义在基类中的成员,但是派生类的成员函数不一定有权访问从基类继承而来的成员。
和其他使用基类的代码一样,派生类能访问共有成员,而不能访问私有成员
不过在某些时候基类中还有这样一种成员,基类希望它的派生类有权访问该成员,同时禁止其他用户访问。我们用受保护的(protected)访问运算符说明这样的成员
派生类对象及派生类向基类的类型转换:
一个派生类对象包含多个组成部分:一个含有派生类自己定义的(非静态)成员的子对象,以及一个与该派生类继承的基类对应的子对象,如果有多个基类,那么这样的子对象也有多个。
派生类构造函数:
每个类控制它自己的成员初始化过程
首先初始化基类的部分,然后按照声明的顺序依次初始化派生类的成员
阅读FLAnimatedImage源码笔记
NS_UNAVAILABLE 与 NS_DESIGNATED_INITIALIZER:
当面对多个初始化方法时,外部调用者往往会手足无措,不知道哪一个才是正确的初始化方法,对此,苹果提供了两个关键字:NS_UNAVAILABLE 与 NS_DESIGNATED_INITIALIZER 来帮助我们约束定义方式,使得接口描述更加清晰。
NS_DESIGNATED_INITIALIZER 与 NS_UNAVAILABLE 都能清晰的告知调用者应该如何调用方法。
如果是可以给出默认值初始化方法,那么使用 NS_DESIGNATED_INITIALIZER 就可以。
如果是必须要用某参数来初始化的,可以使用 NS_UNAVAILABLE。
如果需要在内部验证参数是否合法,如果不合法就一定不能成功的,也可以在实现的时候,验证并抛出异常。
具体选择使用哪一种方式,可以根据具体的情况来看。
参考链接:https://juejin.im/entry/58c11bb444d9040068e190fb
__has_attribute:编译器检查指定属性的可用性
1 | #ifndef NS_DESIGNATED_INITIALIZER |
以上确保初始化过程代码的健壮性
关键字:static extern const
NSProxy:NSProxy 是一个抽象基类,它为一些表现的像是其它对象替身或者并不存在的对象定义API。一般的,发送给代理的消息被转发给一个真实的对象或者代理本身引起加载(或者将本身转换成)一个真实的对象。NSProxy的基类可以被用来透明的转发消息或者耗费巨大的对象的lazy 初始化
Runtime
自动布局
数据结构
树:一棵树是一些节点的集合。这个集合可以是空集;若非空,则一棵树由称作根(root)的节点r以及0个或多个非空的(子)树T1,T2.。。组成,这些子树中的每一棵的根都来自根r的一条有向的边(edge)所连接
每一棵子树的根叫做根r的儿子(child),而r是每一棵子树的根的父亲(parent)。
一棵树是N个节点和N-1条边的集合,其中的一个节点叫做 根。
二叉树:二叉树(binary tree)是一棵树,其中每个节点都不能有多于两个的儿子