@property(nonatomic,strong)NSString*name;
@property(nonatomic,assign)intage;
@property(nonatomic,assign)UserSexsex;
-(id)initUserModelWithUserName:(NSString*)namewithAge:(int)age;
-(void)doLogin;
@end
这是修改后的代码:
//1.enum建议使用NS_ENUM和NS_OPTIONS宏来定义枚举类型
typedefNS_ENUM(NSInteger,USERSex){
USERSexMan,USERSexWoman
};
@interfaceMYUser:NSObject
@property(nonatomic,copy,readonly)NSString*name;
//3.age应该避免使用基本类型,建议使用Foundation数据类型
@property(nonatomic,assign,readonly)NSUIntegerage;
//4.既然该类中已经有一个“初始化方法”(initializer),用于设置“姓名”(Name)、“年龄”(Age)和“性别”(Sex)的初始值:那么在设计对应@property时就应该尽量使用不可变的对象:其三个属性都应该设为“只读”
@property(nonatomic,assign,readonly)USERSexsex;
//5.按照接口设计的惯例,如果设计了“初始化方法”(initializer),也应当搭配一个快捷构造方法。而快捷构造方法的返回值,建议为instancetype,为保持一致性,init方法和快捷构造方法的返回类型最好都用instancetype。
-(instancetype)initWithName:(NSString*)nameage:(int)agesex:(USERSex)sex;
+(instancetype)userWithName:(NSString*)nameage:(int)agesex:(USERSex)sex;
6.doLogIn方法不应写在该类中,doLogIn方法命名不规范:添加了多余的动词前缀
方法中不要用with来连接两个参数
7.enum中驼峰命名法和下划线命名法混用错误:枚举类型的命名规则和函数的命名规则相同:命名时使用驼峰命名法,勿使用下划线命名法。
违反iOS系统规则产生crash的三种类型:
1.内存报警闪退
当iOS检测到内存不足时,它的VM系统会发出警告通知,尝试回收一些内存,若情况没有得到足够的改善,iOS会终止后台应用以回收更多内存,最后若内存还是不足,那么正在运行的应用可能会被终止掉。在Debug模式下,可以主动将客户端执行的动作逻辑写入一个log文件中,当内存报警时,可提醒当前内存吃紧。可以通过Instruments工具中的Allocations和leaks模块库来发现内存分配问题和内存泄漏问题。
2.响应超时
当应用程序对一些特定的事件(比如启动、挂起、恢复、结束)响应不及时时,苹果的watchdog机制会把应用程序干掉,并生成一份响应的crash日志。
3.用户强制退出
一看到用户强制退出,首先想到的可能是双击home键关闭应用。不过这种场景一般是不会产生crash日志的,因为双击home键后,所有的应用程序都处于后台状态,而iOS随时都有可能关闭后台进程,当应用阻塞界面并停止响应时这种场景才会产生crash日志。这里指的用户强制退出场景是稍微比较复杂点的操作:先按住电源键,直到出现滑动关机的界面时,再按住home键,这时候当前应用程序会被终止掉,并产生一份响应事件的crash日志。
应用逻辑的Bug
大多数闪退崩溃日志的产生都是因为bug,这种bug的错误种类有很多,比如:
SEGV:无效内存地址,比如空指针,未初始化指针,栈溢出等;
SIGABRT:收到Abort信号,可能自身调用abort()或者收到外部发送过来的信号;
SIGBUS:总线错误,与SIGSEGV不同的是,SIGSEGV访问的是无效地址(比如虚存映射不到物理内存),而SIGBUS访问的是有效地址,但总线访问异常(比如地址对齐问题);
SIGILL:尝试执行非法的指令,可能不被识别或者没有权限;
SIGPIPE:管道另一端没有进程接手数据;
crash的收集:
打开Xcode->windows->devices选择devicelogs进行查看。一些三方库也可以
解决线上闪退
首先保证发布前充分测试,发布后若依然有闪退现象,查看崩溃日志,及时修复并发布
应用程序的状态:
Notrunning(未运行):程序没有启动;
Inactive(未激活):程序在前台运行,不过没有接收到事件。在没有事件处理情况下程序通常停留在这个状态;
Active(激活):程序在前台运行而且接收到了事件,这也是前台的一个正常的模式;
Suspend(挂起):程序在后台不能执行代码。系统会自动把程序变成这个状态而且不会发出通知。当挂起时,程序还是停留在内存中,当系统内存过低时,系统就会把挂起的程序清除掉,为前台程序提供更多的内存。
iOS的入口在main.m文件:
intmain(intargc,char*argv[])
{
@autoreleasepool{
returnUIApplicationMain(argc,argv,nil,NSStringFromClass([AppDelegateclass]));
}
main函数的两个参数,iOS中没有用到,包括这两个参数是为了与标准ANSIC保持一致。UIApplicationMain函数,前两个参数和main函数一样,重点是后两个:
后两个参数分别表示程序的主要类和代理类。若主要类为nil,将从Info.plist中获取,若Info.plist中不存在对应的key,则默认为UIApplication;代理类将在新建工程时创建。
根据UIApplicationMain函数,程序将进入APPDelegate.m,这个文件是Xcode新建工程时自动生成的。
__block不管是ARC还是MRC下都可以使用。可以修饰对象,还可以修饰基本数据类型;
__weak只能在ARC下使用,只能修饰对象,不能修饰基本数据类型;
被__block修饰的对象在Block中被重新赋值,__weak不可以。
采用交换的方法,把金条切2次,分别得到整根金条的1/7,2/7,4/7
第一天给1/7,第二天给2/7,收回1/7,第三天给1/7;
第四天给4/7,收回1/7和2/7,第五天给1/7;
第六天给2/7,收回1/7,第七天给1/7。
我们一直写类似[[NSObjectalloc]init];的表达式,而淡化了alloc和init的区别。一个OC的特性叫两步创建(twostagecreation)。这意味着申请分配内存和初始化是两个分离的操作。
alloc表示对象分配内存,这个过程涉及分配足够的可用内存来保存对象,写入isa指针,初始化retain计数,并且初始化所有实例变量。
init表示初始化对象,这意味着把对象转换了一个可用的状态,通常指把可用的值赋给了对象的实例变量。
alloc方法会返回一个合法的没有初始化的实例对象。每一个发送到实例的消息会被翻译为objc_msgSend()函数的调用,它的参数是指向alloc返回的对象的,名为self的指针。这样之后self已经可以执行所有方法了。
为了完成两步创建,第一个发送给新创建的实例的方法应该是约定俗成的init方法。注意在NSObject的init实现中,仅仅是返回了self。
关于init有一个另外的重要的约定:这个方法可以(并且应该)在不能成功完成初始化的时候返回nil;初始化可能因为各种原因失败,比如一个输入的格式错误,或者未能成功初始化一个需要的对象。
这样就能理解为什么需要总是调用self=[superinit];如果你的超类没有成功初始化它自己,你必须假设你在一个矛盾的状态,并且在你的实现中不要处理你自己的初始化逻辑,同时返回nil。如果你不这样做,你可能会得到一个不能用的对象,并且它的行为是不可预测的,最终可能导致你的APP发生crash。
@property(nonatomic,strong)NSString*target;
dispatch_queue_tqueue=dispatch_queue_create("parallel",DISPATCH_QUEUE_CONCURRENT);
for(inti=0;i<1000000;i++){
dispatch_async(queue,^{
self.target=[NSStringstringWithFormat:@"ksddkjalkjd%d",i];
});
崩溃。
因为这是个并行队列+异步的线程。假如队列A执行到步奏2,还没到步骤3时,队列B也执行到步骤2,那么这个对象就会被过度释放,导致向已释放内存对象发送消息而崩溃。
1.使用串行队列
将set方法改成在串行队列中执行就行,这样即使异步,但所有block操作追加在队列最后依次执行。
2.使用atomic
atomic关键字相当于在setter方法加锁,这样每次执行setter都是线程安全的,但这只是单独针对setter方法而言的狭义的线程安全。
3.使用weak关键字
weak的setter没有保留新值或者保留旧值的操作,所以不会引发重复释放。当然这个时候要看具体情况能否使用weak,可能值并不是所需要的值。
4.使用TaggedPointer
TaggedPointer是苹果在64位系统引入的内存技术。简单来说就是对于NSString(内存小于60位的字符串)或NSNumber(小于2^31),64位的指针有8个字节,完全可以直接用这个空间来直接表示值,这样的话其实会将NSString和NSNumber对象由一个指针转换成一个值类型,而值类型的setter和getter又是原子的,从而线程安全。
比如上述代码的字符串改短一些,就不会崩溃了。
优点:
1.一个类只能被实例化一次,提供了对唯一实例的受控访问;
2.节省系统资源;
3.允许可变数目的实例
缺点:
1.一个类只有一个对象,可能造成责任过重,在一定程度上违背了“单一职责原则”
2.由于单例模式中没有抽象层,因此单例类的扩展有很大困难;
Xcode编译项目后,我们会看到一个dsym文件,dsym是保存16进制函数地址映射信息的中转文件,我们调试的symbols都会包含在这个文件中,并且每次编译项目的时候都会生成一个新的dsym文件。
当我们软件release模式打包或上线后,不会像我们在Xcode中那样直观的看到用户崩溃的错误,这个时候我们就需要分析crashreport文件了,iOS设备中会有日志文件保存我们每个应用出错的函数内存地址,通过Xcode的organizer可以将iOS设备中的DeviceLog导出成crash文件,这个时候我们就可以通过出错的函数地址去查询dsym文件中程序对应的函数名和文件名。大前提是我们需要有软件版本对应的dsym文件,这也是为什么我们很有必要保存每个发布版本的Archives文件了。
每个xx.app和xx.app.dsym文件都有对应的UUID,crash文件也有自己的UUID,只要这三个文件的UUID一致,我们就可以通过他们解析出正确的错误函数信息了。1.查看xx.app文件的UUID,terminal中输入:dwarfdump--uuidxx.app/xx(xx是项目名)。2.查看xx.app.dsym文件的UUID,terminal中输入:dwarfdump--uuidxx.app.dsym。3.crash文件内第一行IncidentIdentifier就是UUID。
程序执行:
1.main函数2.执行UIApplicationMain函数3.创建UIApplication对象4.创建UIApplicationDelegate对象并复制5.读取配置文件info.plist,设置程序启动的一些属性6.创建应用程序的MainRunloop循环7.UIApplicationDelegate对象开始处理监听事件8.程序启动后,首先调用application.didFinishLaunchingWithOptions:方法9.如果info.plist中配置了启动的storyBoard文件名,则加载storyboard文件10.如果没配置,则根据代码创建UIWindow->rootViewController->显示。
1.动态库加载越多,启动越慢
2.objc类越多,启动越慢
3.C的constructor函数越多,启动越慢
4.C++静态对象越多,启动越慢
5.objc的+load越多,启动越慢。在objc类的数目一样多的情况下,需要加载的动态库越多,app启动就越慢。同样的,在动态库一样多的情况下,objc的类越多,app的启动也越慢。需要加载的动态库从1个上升到10个的时候,用户几乎感知不到任何分别,但从10个升到100个的时候就会变得十分明显。同理,100个类和1000个类,可能也很难察觉得出,但1000个类和10000个类的分别就开始明显了。同样的,尽量不要写attribute((constructor))的C函数,也尽量不要用到C++的静态对象。至于objc的+load方法,似乎大家已经习惯不用它了。任何情况下,能用dispatch_once()来完成的,就尽量不要用到以上的方法。
1.这行main()函数的耗时;
2.执行applicationWillFinishLaunching的耗时;
3.rootViewController及其子视图的加载、view及其子view的加载。
应该在400ms内完成main函数之前的加载
整体过程耗时不能超过20秒,否则系统会kill掉进程,app启动失败
0xbad22222:该编码表示VoIP应用因为过于频繁重启而被终止
0xdead10cc:读作”deadlock“,表示应用因为在后台运行时占用系统资源,如通讯录数据库不释放而被终止
0xdeadfa11:读作”deadfail“,表示应用是被用户强制退出的。根据苹果文档,强制退出发生在用户长按开关按钮直到出现”滑动关机“然后长按home键。
1.本地数据加密:对NSUserDefaults、sqlite存储文件数据加密,保护账号和关键信息;
2.URL编码加密:对程序中出现的URL进行编码加密,防止URL被静态分析;
3.对客户端传输数据提供加密方案,有效防止通过网络接口拦截获取数据;
4.对应用程序的方法名和方法体进行混淆,保证源码被逆向后无法解析代码;
5.对应用程序逻辑结构进行打乱混排,保证源码可读性降到最低;
6.借助第三方加固,例如网易云易盾。
相似点:
函数指针类型和Block类型都可以作为变量和函数参数的类型(typedef定义别名之后,这个别名就是一个类型)。
不同点:
函数指针只能指向预先定义好的函数代码块(可以是其他文件里面定义,通过函数参数动态传入),函数地址是在编译时就已经确定好的。
Block本质是OC对象,是NSObject的子类,可以接受消息。
函数里面只能访问全局变量,而Block代码块不光能访问全局变量,还拥有当前栈内存和堆内存变量的可读性(当然通过__block访问指示符修饰的局部变量还可以在block代码块里面进行修改)。
从内存角度看,函数指针只不过是指向代码区的一段可执行代码,而Block实际上是程序运行过程中在栈内存动态创建的对象,可以向其发送copy消息将block对象拷贝到堆内存,以延长其生命周期。
根据对象的isa指针找到类对象id,在查询类对象里面的methodLists方法函数列表,如果没有找到,再沿着superClass,寻找父类,再在父类methodLists方法列表里查询,最终找到SEL,根据id和SEL确认IMP(指针函数),再发送消息。
当发送消息时,我们会根据类里面的methodLists列表去查询我们要动用的SEL,当查询不到时,会一直沿着父类查询,当最终查询不到的时候,会报unrecognizedselector错误。
当系统查询不到方法的时候,会调用+(BOOL)resolveInstanceMethod:(SEL)sel动态解释的方法来给我们一次机会来添加调用不到的方法。或者我们可以再次使用-(id)forwardingTargetForSelector:(SEL)aSelector重定向的方法来告诉系统,该调用什么方法来保证不会崩溃。
instance_size:实例的内存大小,objc_ivar_list*ivars:属性列表
runloop:从字面意思看:运行循环、跑圈,其实它内部就是do-while循环,在这个循环内部不断地处理各种任务(比如Source、Timer、Observer)事件。runloop和线程的关系:一个线程对应一个RunLoop,主线程的RunLoop默认创建并启动,子线程的RunLoop需手动创建且手动启动(调用run方法)。RunLoop只能选择一个Mode启动,如果当前Mode中没有任何Source(Sources0、Sources1)、Timer,那么就直接退出RunLoop。
mode是runloop里面的运行模式,不同模式下的runloop处理的事件和消息有一定的差别。
系统默认注册了5个mode:
1.kCFRunLoopDefaultMode:app默认mode,通常主线程是在这个mode下运行的。
2.UITrackingRunLoopMode:界面跟踪mode,用于Scrollview追踪触摸滑动,保证界面滑动时不受其他mode影响。
3.UIInitializationRunLoopMode:在刚启动App时进入的第一个mode,启动完成后就不再使用。
4.GSEventReceiveRunLoopMode:接受系统事件的内部mode,通常用不到。
5.kCFRunLoopCommonModes:这是一个占位的mode,没有实际作用。
注意,iOS对以上5个mode进行了封装:NSDefaultRunLoopMode、NSRunLoopCommonModes。
load是在被添加到Runtime时开始执行,父类最先执行,然后是子类,最后是Category。又因为是直接获取函数指针来执行,不会像objc_msgSend一样会有方法查找的过程
initialize最终是通过objc_msgSend来执行的。objc_msgSend会执行一系列方法查找,并且Category的方法会覆盖。
load只在被添加到Runtime时执行1次,initialize收到第一条消息前,可能永远不会调用。如果存在继承关系时,有可能调用多次。
runtime会自动load所有引用到项目里的类,而initialize是懒加载,用到的时候才会执行。
SEL:OC在编译时,会根据方法的名字生成一个用来区分这个方法的唯一的一个ID,本质上就是一个字符串。只要方法名称相同,那么它们的ID就相同。
IMP:实际上就是一个函数指针,指向方法实现的首地址。
Method:它等于SEL+IMP+method_types,相当于在SEL和IMP之间建立了一个映射。
方法调用流程:
1.检查selector是否需要忽略;
2.检查target是否为nil,如果是nil就直接cleanup,然后return;
3.在target的Class中根据selector去找IMP;
4.在当前的class的方法缓存(cachemethodLists)里寻找,找到了跳到对应的方法实现,没找到继续往下执行;
5.从当前class的方法列表(methodLists)里查找,找到了添加到方法缓存列表里,然后跳到对应的方法实现(需要注意的是,在superClass中寻找IMP时,不论是在cachemethodLists还是methodLists中找到IMP,都会先存入当前class的cachemethodLists再跳转到对应的方法实现。),没找到继续往下执行;
6.从superClass的缓存列表和方法列表里查找,直到查找到根类;
7.若还是找不到IMP,则进入消息动态处理和消息转发流程;
消息动态处理:
1.+(BOOL)resolveInstanceMethod:(SEL)sel;
2.-(id)forwardingTargetForSelector:(SEL)aSelector;
如果上述两个时机都无法处理消息,则会进入消息转发流程:
-(NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector
-(void)forwardInvocation:(NSInvocation*)anInvocation
如果methodSignatureForSelector返回的NSMethodSignature是nil的话,不会继续执行forwardInvocation,转发流程终止,抛出无法处理的异常。
如果methodSignatureForSelector返回了方法签名,我们还有最后一次机会处理这个消息,那就是在forwardInvocation回调里进行处理。
优点:比其他两种更准确,最适合做UI不断刷新的事件,过度相对流畅、无卡顿感。
缺点:由于依托于屏幕刷新频率,若CPU不堪重负而影响了屏幕刷新,那么我们的触发事件也会受到相应的影响;
selector事件如果大于其触发间隔就会造成掉帧现象;
CADisplayLink不能被继承;
NSTimer
优点:使用相对灵活、应用广泛。
缺点:受runloopmode影响严重,使用不当会造成内存泄露。
GCD(dispatch_source_t)
优点:不受runloopmode影响,使用相对简单。
缺点:虽说不受runloopmode的影响,但其计时效应仍不失百分之百准确的。另外,它的触发事件也可能被阻塞,当GCD内部管理的所有线程都被占用时,其触发事件将被延迟。使用不当也会造成内存泄漏。
首先,先将App内容通过摘要算法得到摘要,再用私钥对摘要进行加密得到密文,将源文本、密文和私钥对应的公钥一并发布即可。验证方首先查看公钥是否是私钥方的,然后用公钥对密文进行解密得到摘要,将app用同样的摘要算法得到摘要,将两个摘要进行比对,如果相等那么一切正常。这个过程只要一步出问题就视为无效。
Debug是调试版本,主要是让程序员使用,在调试的过程中Debug会启动更多服务来监控错误,运行速度相对较慢,而且比较耗能。只有debug版的程序才能设置断点、单步执行、使用TRACE/ASSERT等调试输出语句。
Release是发布版本,主要是让用户使用,在使用过程中会去掉那些繁琐的监控服务,运行速度相对较快、而且比较节省内存。
因为这些产生的动画只是假象,并没有对layer进行改变。图层树里的呈现树实际上是模型图层的复制,但是它的属性值表示了当前外观效果,动画的过程实际上只是修改了呈现树,并没有对图层的属性进行改变,所以在动画结束后图层会恢复到原先状态。
类别中原则上只能增加方法(能添加属性的的原因只是通过runtime解决无setter/getter的问题而已);
类扩展不仅仅可以增加方法,还可以增加实例变量或属性,只是该实例变量默认是@private类型的(使用范围只能在自身类,而不是子类或其他地方);
KVO是基于Runtime机制实现的,当某个类的属性第一次被观察时,系统就会在运行期间动态的创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter方法。派生类在被重写的setter方法内实现真正的通知机制。比如,原类为Person,那么生成的派生类名为NSKVONotifying_Person。每个类对象中都有一个isa指针指向当前类,当一个类对象第一次被观察,那么系统会将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法。键值观察通知依赖于NSObject的两个方法:willChangeValueForKey:和didChangeValueForKey:。在一个被观察属性发生改变之前,willChangeValueForKey:一定会被调用,这时就会记录旧值,而当改变发生后,didChangeValueForKey:就会被调用,继而observeValueForKey:ofObject:change:context:也会被调用。
Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。这个weak表其实是一个hash表,key是所指对象的地址,value是weak指针的地址数组。初始化时,runtime会调用objc_initWeak函数来初始化一个新的weak指针指向对象的地址。添加引用时,objc_initWeak函数会调用objc_storeWeak()函数来更新指针指向,创建对应的弱引用表。释放时,调用clearDeallocating函数,它首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,然后从weak表中删除,最后清理对象的记录。
CALayer由于本身并不包含在UIKit中,所以它不能响应事件,考虑到它的动画操作功能,因此它的很多属性在修改时都能形成动画效果,这种效果就是隐式动画。之所以叫隐式是因为我们并没有指定任何动画的类型。我们仅仅改变了一个属性,然后CoreAnimation来决定如何并且何时去做动画。隐式动画代码编写起来更简单。
显示动画比隐式动画要复杂得多,它要求创建动画对象,设置它们的属性,然后将这些动画对象应用到你希望动画的对象上。当代码运行后,视图会暂时出现然后再次消失,这其实是显式动画的副产品,是隐式动画和显式动画的一个非常明显的区别。当你调用一个显式动画时,你动画的属性将默认重置为它的原始值,而隐式动画将保留在最终状态。
显示动画通过明确的调用begin,commit来提交动画。苹果用block在内部自动调用CATransaction的+begin和+commit方法,这样block中所有属性的改变都会被事务所包含。这样也可以避免开发者由于对+begin和+commit匹配的失误造成的风险。
被const修饰的变量是只读的,它修饰的变量值是不可变的。
static用来规定作用域和存储方式。对于局部变量,static改变了它的存储方式,使它变成了静态的局部变量,即编译时就为变量分配内存,调用结束后存储空间不释放,使得该局部变量有记忆功能,可以记忆上次的数据,不过由于仍是局部变量,因而只能在代码块内部使用(作用域不变)。
对于全局变量,被static修饰后仅限于当前文件引用,其他文件不能通过extern来引用
使用staticconst联合修饰的变量,可以定义一个只能在当前文件访问的全局常量,在同一个文件内可以取代#define。
staticconst修饰变量和宏定义的比较:
相同点:都不能再被修改,一处修改,其他都改了
#define属于预编译指令,预处理过程扫描源代码,对其进行初步的转换,产生新的源代码提供给编译器。所以预处理过程是先于编译器对源代码进行处理的。#define的本质其实就是文本替换。
form-urlencoded是默认的mime内容编码类型,是通用的,但是它在传输比较大的二进制或文本数据时效率很低。
multipart/form-data是上传文件、图片或二进制数据和非ASCII数据时使用。
黑盒测试:已知产品所应具有的功能,通过测试来检测每个功能是否都能正常使用,黑盒测试着眼于程序外部结构、不考虑内部逻辑结构、针对软件界面和软件功能进行测试。它是穷举输入测试,只有把所有可能的输入都作为测试情况使用,才能以这种方法查出程序中所有的错误。
白盒测试:全面了解程序内部逻辑结构、对所有逻辑路径进行测试。是穷举路径测试,在使用这一方案时,测试者必须检查程序的内部结构,从检查程序的逻辑着手,得出测试数据。
后台获取(BackgroundFetch):后台获取使用场景是用户打开应用之前就使app有机会执行代码来获取数据、刷新UI。这样在用户打开应用的时候,最新的内容将已经显现在用户眼前,而省去了所有的加载过程。
推送唤醒(RemoteNotifications):使用场景是使设备在接收到远端推送后让系统唤醒设备和我们的后台应用,并先执行一段代码来准备数据和UI,然后再提示用户有推送。这时用户如果解锁设备进入应用后将不会再有任何加载过程,新的内容将直接得到呈现。
CocoaPods的工作主要是通过ProjectName.xcworkspace来组织的,在打开ProjectName.xcworkspace文件后,发现Xcode会多出一个Pods工程。
1.库文件引入及配置:库文件的引入主要由Pods工程中的Pods-ProjectName-frameworks.sh脚本负责,在每次编译时,该脚本会帮你把预引入的所有第三方库文件打包成ProjectName.a静态库文件,放在我们原Xcode工程中Framework文件夹下,供工程使用。
如果Podfile使用了use_frameworks!,这是生成的是.framework的动态库文件。
2.Resource文件:Resource资源文件主要由Pods工程中的Pods-ProjectName-resources.sh脚本负责,在每次编译的时候,该脚本会帮你将所有三方库的Resource文件copy到目标目录中。
3.依赖参数设置:在Pods工程中的每个库文件都有一个相应的SDKName.xcconfig,在编译时,CocoaPods就是通过这些文件来设置所有的依赖参数的,编译后,在主工程的Pods文件夹下会生成两个配置文件,Pods-ProjectName.debug.xcconfig、Pods-ProjectName.release.xcconfig。
Handoff就是将手机上不方便做的事情通过iCloud转移到电脑上去做。
HomeKit是苹果2014年发布的智能家居平台,这些产品可以通过iPhone、iPad等控制灯光、室温、风扇以及其他家用电器。
VoiceOver是苹果的“读屏”技术,可以读出屏幕上的信息,以帮助盲人进行人机交互。
iBeacons是通过低功耗蓝牙技术进行一个十分精确的微定位和室内导航,设备可以接收一定范围由其他iBeacons发出来的信号。
Metal是一套用于iPhone、iPad上GPU编程的高效框架,一般做游戏的可能了解的比较多。OpenGL可跨多个平台使用,而且资料丰富,而Metal仅仅局限于iPhone或iPad,而且资料少的可怜。但Metal在运行性能上要比OpenGLES高效,它大大减少了资源的开销。
以NS为例,NS代表的是NeXTSTEP,是乔布斯在1985年离开苹果之后创建的电脑公司,该公司的产品包括了一款OC开发的操作系统,该操作系统里面有很多NS的缩写,后来在1996年Apple收购了NeXTSTEP,里面的一些东西就成了OSX和iOS的一部分,NS前缀的习惯也就保存了下来。
如今,iOS命名规范倡导一个类或方法的开头两个或三个大写字母指代公司或个人,或框架名称等容易和其他区分开来的。还有很重要的一点,OC没有命名空间的概念,使用这样大写字母前缀的方式可以有效的避免命名冲突的问题。
相同点:都可以作为方法的返回类型
不同点:instancetype可以返回和方法所在类相同类型的对象,id只能返回未知类型的对象;
instancetype只能作为返回值,不能像id那样作为参数。instancetype只适用于初始化方法和便利构造器的返回值类型。
对于init方法,id和instancetype是没有区别的。因为编译器会把id优化为instancetype。当明确返回的类型就是当前类时,使用instancetype能避免id带来的编译不出的错误情况。
Array可以保存基本类型和对象类型,ArrayList只能保存对象类型;
Array容量(即空间大小)是静态固定的,ArrayList是动态变化的;
ArrayList有更加丰富的方法,比如:addAll()、removeAll()、iterator()。
在OC中,只有对象才能设置为nil,而Swift中除了对象,int、struct、enum等任何可选类型都可设置为nil,而且nil只能用在可选类型的变量和常量;
在OC中,nil是一个指向不存在对象的指针,Swift中nil不是指针,而是一个确定的值,用来表示值缺失。
NSURLSession是NSURLConnection的替代者,是对NSURLConnection进行了重构优化后的新的网络访问接口;
NSURLConnection在下载文件时,先将整个文件下载到内存,然后再写入沙盒,如果文件比较大,就会出现内存暴涨的情况。而使用NSURLSession下载文件时,会默认下载到沙盒中的temp文件夹中,不会出现内存暴涨的情况,但是下载完成后会将temp中的临时文件删除,需要在初始化任务方法的时候,在completionHandler回调中增加保存文件的代码。
NSURLConnection实例化对象,开始默认请求就发送,不需要调用start方法,而cancel可以停止请求的发送,停止后不能继续访问,需要创建新的请求,而NSURLSession有三个控制方法:cancel、suspend、resume,暂停后可以通过resume继续恢复当前的请求任务;
NSURLSession的构造方法sessionWithConfiguration:delegate:delegateQueue中有一个NSURLSessionConfiguration类的参数可以设置配置信息,其决定了cookie,安全和高速缓存策略,最大主机连接数、资源管理、网络超时等配置,而NSURLConnection不能进行这个配置,相比于NSURLConnection依赖于一个全局的配置对象,缺乏灵活性而言,NSURLSession有了很大的改进;
使用NSURLSession进行断点下载更加便捷。
内存管理方式不一样,类是引用类型,分配在堆上,结构体是值类型,分配在栈上;
类可以继承,而结构体不能。
对于引用类型,对一个变量执行的操作会影响另一个变量所引用的对象。对于值类型,每个变量都有其自己的数据副本,对一个变量执行的操作不会影响另一个变量。
//内存警告时候用
-(void)applicationDidReceiveMemoryWarning:(UIApplication*)application{
[[SDWebImageManagersharedManager].imageCachecleanDisk];
[[SDWebImageManagersharedManager]cancelAll];
cleanDisk:清除过期缓存,计算当前缓存大小和设置的最大缓存数量比较,如果超出那么会继续删除(按照文件创建的先后顺序);
clearDisk:粗暴的直接删除,然后重新创建。
NSCache中的key不必实现copy,NSDictionary中的key必须实现copy,不需要实现NSCopying协议;
实现缓存时应选用NSCache而非NSDictionary,因为NSCache可以提供优雅的自动删减功能,而且是线程安全的。
手动保存的文件在documents文件里,NSUserDefaults保存的文件在Library/Preferences目录文件夹里。
forin遵循了NSFastEnumeration协议,它只有一个方法:
-(NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState*)stateobjects:(id*)stackbuffercount:(NSUInteger)len;
它直接从C数组中取对象。对于可变数组来说,它最多只需要两次就可以获取全部元素。如果数组还没有构成循环,那么第一次就获得了全部元素,跟不可变数组一样。但如果数组构成了循环,那么就需要两次,第一次获取对象数组的起始偏移到循环数组末端的元素,第二次获取存放在循环数组起始处的剩余元素。而for循序比forin速度慢一点,是因为for循环每次都要调用objectAtIndex:方法。
若我们遍历时不需要获取当前遍历操作所针对的下标,我们就可以选择forin。
当obj为实例变量时,[objclass]方法和objc_getClass(obj)方法一样,都是获得isa指针,即指向类对象的指针。
当obj为类对象时,object_getClass(obj)返回类对象中的isa指针,即指向元类对象的指针;[objclass]返回的则是其本身。