ruby.taobao.org 停止更新了,使用 cocoapods 的童鞋,更新到 ruby china 的源
$ gem sources --add https://gems.ruby-china.org/ --remove https://rubygems.org/
$ gem sources -lhttps://gems.ruby-china.org # 确保只有 gems.ruby-china.org
$ gem sources -l
*** CURRENT SOURCES ***
https://gems.ruby-china.org
# 请确保只有 gems.ruby-china.org
$ gem install rails
$ gem install rails时出现这样的错误:
yitudevdeiMac:~ yitudev$ gem install rails
Building native extensions. This could take a while...
ERROR: While executing gem ... (Errno::EACCES)
Permission denied @ dir_s_mkdir - /usr/local/lib/ruby/gems/2.3.0/extensions/x86_64-darwin-15/2.3.0/nokogiri-1.6.8.1
yitudevdeiMac:~ yitudev$ gem install rails
Building native extensions. This could take a while...
进行操作:
yitudevdeiMac:~ yitudev$ sudo gem install cocoapods-core
再$ gem install rails。当pod setup 进入Setting up CocoaPods master repo 等待的时候表示正在下载了,此时你可通过新开一个终端窗口,输入"cd ~/.cocoapods/"命令行跳到cocoapods文件夹内,执行"du -sh *"查看正在下载的文件夹的大小。
CocoaPods 的简单快速安装方法(Setting up CocoaPods master repo 卡着不动,是因为淘宝镜像已经不能用了)
- 使用CocoaPods来添加第三方类库,无论是执行pod install还是pod update都卡在了Analyzing dependencies不动原因在于当执行以上两个命令的时候会升级CocoaPods的spec仓库,加一个参数可以省略这一步,然后速度就会提升不少。(最近进行install 和 update,有时即使加了--no-repo-update还是死慢)
pod install --verbose --no-repo-update
pod update --verbose --no-repo-update
NULL、nil、Nil、NSNull区别
标志 | 值 | 含义 |
---|---|---|
NULL | (void *)0 | C 指针的字面空值 |
nil | (id)0 | Objective-C 对象的字面空值 |
Nil | (Class)0 | Objective-C 类的字面空值 |
NSNull | [NSNull null] | 用来表示空值的 Objective-C 对象 |
- NULL一般用于表示 C 指针空值,如:
int *pointerToInt = NULL;
char *pointerToChar = NULL;
struct TreeNode *rootNode = NULL;
- nil:
NSString *someString = nil;
NSURL *someURL = nil;
id someObject = nil;
if (anotherObject == nil)
- Nil:
Class someClass = Nil;
Class anotherClass = [NSString class];
- NSNull
是一个 Objective-C 对象,是一个用于表示空值的类,而且它只有一个单例方法:+[NSNull null],一般用于在集合对象中保存一个空的占位对象。
// 当 NSArray 里遇到 nil 时,就说明这个数组对象的元素截止了,即 NSArray 只关注 nil 之前的对象,nil 之后的对象会被抛弃。
NSArray *array = [NSArray arrayWithObjects:@"one", @"two", nil];
// 错误的使用
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:nil forKey:@"someKey"];
// 正确的使用
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:[NSNull null] forKey:@"someKey"];
取消延迟执行函数
- 法一:performSelector
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
// 取消某一个延时调用请求
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument;
// 取消performSelector的所有被延迟执行的方法
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;
- 法二:创建定时器
// 创建定时器的time-类方法,需要手动fire开启定时器,将执行方法封装到NSInvocation中
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
// 创建定时器的time-类方法,需要手动fire开启定时器
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
// 创建定时器的scheduled-类方法,不需要手动fire开启定时器,将执行方法封装到NSInvocation中
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
// 创建定时器的scheduled-类方法,不需要手动fire开启定时器
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
// 创建定时器的实例方法,会在指定时间开启定时器
- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(id)ui repeats:(BOOL)rep;
// 开启定时器
- (void)fire;
// 使定时器失效,将定时器从循环池移除掉
- (void)invalidate;
self.timer = [NSTimer scheduledTimerWithTimeInterval:3.0f target:self selector:@selector(prtintMyWords:) userInfo:@{@"words":@"hello world"} repeats:NO];
[self.timer invalidate];
- 法三:使用GCD
取消GCD的延迟执行还不知道如何操作,可参考Dispatch-Cancel
// 创建延迟时间,从当前时间开始,3秒后执行。 3秒需要转化为纳秒,因为该函数是以纳秒为基础进行的
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, 3.0*NSEC_PER_SEC);
// 执行延迟函数
dispatch_after(delay, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self printString:@"hello world"];
});
- 注意:
1、performSelector和scheduledTimerWithTimeInterval方法都是基于runloop的。我们知道,当一个应用启动时,系统会开启一个主线程,并且把主线程的runloop激活,也就是run起来,并且主线程的runloop是不会停止的。所以,当这两个方法在主线程可以被正常调用。但情况往往不是这样的。实际编码中,我们更多的逻辑是放在子线程中执行的。而子线程的runloop是默认关闭的。这时如果不手动激活runloop,performSelector和scheduledTimerWithTimeInterval的调用将是无效的。
2、NSTimer、performSelector的创建与撤销必须在同一个线程操作。
3、当一个timer被schedule的时候,timer会持有target对象,NSRunLoop对象会持有timer。当invalidate被调用时,NSRunLoop对象会释放对timer的持有,timer会释放对target的持有。除此之外,我们没有途径可以释放timer对target的持有。所以解决内存泄露就必须撤销timer,若不撤销,target对象将永远无法释放。
4、若使用dispatch_after,系统会帮我们处理线程级的逻辑,这样也我们更易于享受系统对线程所做的优化。除此之外,我们不用关心runloop的问题。并且调用的对象也不会被强行持有,这样上述的内存问题也不复存在。当然,需要注意block会持有其传入的对象,但这可以通过weakself解决。所以在这种延迟操作方案中,使用dispatch_after更佳。
版本号如何控制
GNU 风格的版本号管理策略:一般的版本号划分为如下3个部分——“主版本号 . 子版本号 . 修正版本号”,简单直观清晰。
1、项目初版本时,版本号可以为 0.1 或 0.1.0, 也可以为 1.0 或 1.0.0 (例如: 1.0.0 )
2、当项目在进行了局部修改或 bug 修正时,主版本号和子版本号都不变,修正版本号加 1 (例如: 1.0.1 )
3、当项目在原有的基础上增加了部分功能时,主版本号不变,子版本号加 1,修正版本号复位为 0 (例如: 1.1.0 )
4、当项目在进行了重大修改或局部修正累积较多,而导致项目整体发生全局变化时,主版本号加 1 (例如: 2.0.0 )
5、另外,编译版本号一般是编译器在编译过程中自动生成的,我们只定义其格式,并不进行人为控制。
直接调用方法和使用performSelector调用的区别
1、两种方式执行的效果其实和发送消息是等价的。
2、performSelector允许发送未在运行时确定的消息;也就是说,只要这个消息能够被转发到正确的接收者,能够被最后的接收者识别,都是可以正确运行的。否则,就会在运行时报错“unrecognized selector sent to instance”。如果没有实现该方法,只能在运行时才会报错,编译阶段发现不了。
3、Obejct-C的动态特性是允许在运行时向某个类添加方法的,这个时候就一定需要使用performSelector。那么为了避免运行时出现错误,在使用performSelector之前一定要使用如下的检查方法来进行判断。我们一般都忽略的用该方法进行判断。
- (BOOL)respondsToSelector:(SEL)aSelector;
#pragma 处理防止编译器警告
- 首先#pragma在本质上是声明,常用的功能就是注释,尤其是给Code分段注释;而且它还有另一个强大的功能是处理编译器警告,但却没有上一个功能用的那么多。
clang diagnostic 是#pragma 第一个常用命令:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-相关命令"
// 你自己的代码
#pragma clang diagnostic pop
-
常见的用法
1、方法弃用告警
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[TestFlight setDeviceIdentifier:[[UIDevice currentDevice] uniqueIdentifier]];
#pragma clang diagnostic pop
2、不兼容指针类型
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wincompatible-pointer-types"
//
#pragma clang diagnostic pop
3、循环引用
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
self.completionBlock = ^ {
...
};
#pragma clang diagnostic pop
4、未使用变量
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-variable"
int a;
#pragma clang diagnostic pop
5、参数不固定的情况:以DKNightVersion为例
DKImagePicker DKImagePickerWithNames(NSString *normalName, ...) {
NSArray<DKThemeVersion *> *themes = [DKColorTable sharedColorTable].themes;
NSMutableArray<NSString *> *names = [[NSMutableArray alloc] initWithCapacity:themes.count];
[names addObject:normalName];
NSUInteger num_args = themes.count - 1;
va_list names_list;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wvarargs"
va_start(names_list, num_args);
#pragma clang diagnostic pop
for (NSUInteger i = 0; i < num_args; i++) {
NSString *name = va_arg(names_list, NSString *);
[names addObject:name];
}
va_end(names_list);
return [DKImage pickerWithNames:names];
}
va_arg(va_list ap, type),参数ap应该首先被宏va_start 或 va_copy初始化,但又必须在被宏va_end调用之前使用。每次调用va_arg都会改变ap值使得后续的参数值能被依次添加。参数type应该是一个类型名,并且用type*能够得到该类型的指针类型。如果type为空,或者type和实际参数不匹配, 那么除了以下两种情况,这个宏的行为是未定义的。
用法:
(1)首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针;
(2)然后用VA_START宏初始化刚定义的VA_LIST变量;
(3)然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型(如果函数有多个可变参数的,依次调用VA_ARG获取各个参数);
(4)最后用VA_END宏结束可变参数的获取。
在对象内部尽量直接访问实例变量
- 在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,应该通过属性来写。
- 在初始化及dealloc方法中,总是应该直接通过实例变量来读写数据。
消息传递、转发机制
- objc_msgSend依据接收者和选择子的类型来调用适当的方法。该方法需要在接收者所属的类中搜寻其“方法列表”,如能找到与选择子名称相符的方法,就跳转至其实现代码。若找不到,沿着继承体系继续向上查找,等找到合适的方法后再跳转。如最终还是找不到相符的方法,就执行“消息转发”操作。
- 消息转发分两步:
1、先征询接收者所属的类,看其是否能动态添加方法,以处理当前这个“未知的选择子”,这叫做“动态方法解析”。
2、涉及“完整的消息转发机制”,分两步:接收者看有没有其他对象能处理这条消息,若有,则运行期系统会把消息转给那个对象,消息转发结束。若没有“备援的接收者”,则启动完整的消息转发机制,运行期系统会把消息有关的全部细节封装到NSInvocation中,再给接收者最后一次机会,令其设法解决当前还未处理的这条消息。
isMemberOfClass:与isKindOfClass:区别
- “isMemberOfClass:”能够判断出对象是否为某个特定类的实例;
- “isKindOfClass:”能够判断对象是否为某类或其派生类的实例。
- 尽量使用类型信息查询方法来确定对象类型,而不要直接比较类对象,因为某些对象可能实现了消息转发功能。