【IOS学习】基础知识积累
2018-03-06 11:25
302 查看
一.iOS开发之loadView、viewDidLoad及viewDidUnload的关系
1.第一次访问UIViewController的view时,view为nil,然后就会调用loadView方法创建view2.view创建完毕后会调用viewDidLoad方法进行界面元素的初始化3.当内存警告时,系统可能会释放UIViewController的view,将view赋值为nil,并且调用viewDidUnload方法4.当再次访问UIViewController的view时,view已经在3中被赋值为nil,所以又会调用loadView方法重新创建view5.view被重新创建完毕后,还是会调用viewDidLoad方法进行界面元素的初始化二.load和initialize的区别
@implementation Person// 只要程序启动就会将所有类的代码加载到内存中, 放到代码区(无论该类有没有被使用到都会被调用)
// load方法会在当前类被加载到内存的时候调用, 有且仅会调用一次
// 如果存在继承关系, 会先调用父类的load方法, 再调用子类的load方法
+ (void)load
{
NSLog(@"Person类被加载到内存了");
}
// 当当前类第一次被使用的时候就会调用(创建类对象的时候)
// initialize方法在整个程序的运行过程中只会被调用一次, 无论你使用多少次这个类都只会调用一次
// initialize用于对某一个类进行一次性的初始化
// initialize和load一样, 如果存在继承关系, 会先调用父类的initialize再调用子类的initialize
+ (void)initialize
{
NSLog(@"Person initialize");
}
@end
1、+load方法当类或分类添加到object-c runtime时被调用,子类的+load方法会在它所有父类的+load方法之后执行,而分类的+load方法会在它的主类的+load方法之后执行。但不同的类之间的+load方法的调用顺序是不确定的,所以不要在此方法中用另一个类。2、+load方法不像普通方法一样,它不遵循那套继承规则。如果某个类本身没有实现+load方法,那么不管其它各级超类是否实现此方法,系统都不会调用。+load方法调用顺序是:SuperClass -->SubClass --> CategaryClass。3、+initialize是在类或者它的子类接受第一条消息前被调用,但是在它的超类接收到initialize之后。也就是说+initialize是以懒加载的方式被调用的,如果程序一直没有给某个类或它的子类发送消息,那么这个类的+initialize方法是不会被调用的。4、+initialize方法和+load方法还有个区别,就是运行期系统完整度上来讲,此时可以安全使用并调用任意类中的任意方法。而且,运行期系统也能确保+initialize方法一定会在“线程安全的环境”中执行,这就是说,只有执行+initialize的那个线程可以操作类或类实例,其他线程都要阻塞等着+initialize执行完。5、+initialize方法和其他类一样,如果某个类未实现它,而其超类实现了,那么就会运行超类的实现代码。如果本身和超类都没有实现,超类的分类实现了,就会去调用分类的initialize方法。如果本身没有实现,超类和父类的分类实现了就会去调分类的initialize方法。不管是在超类中还是分类中实现initialize方法都会被调多次,调用顺序是SuperClass -->SubClass。
三. UIWindow使用
1) 同一层级的 最后一个显示出来,上一个被覆盖2)UIWindow在显示的时候是不管KeyWindow是谁,都是Level优先的,即Level最高的始终显示在最前面。3)谁最后设置的 makeKeyAndVisible 谁就是keyWindow 其他的也会显示出来 所有的window都可以监听键盘 和点击的事件 UIView的功能 负责渲染区域的内容,并且响应该区域内发生的触摸事件UIWindow
在iOS App中,UIWindow是最顶层的界面内容,我们使用UIWindow和UIView来呈现界面。UIWindow并不包含任何默认的内容,但是它被当作UIView的容器,用于放置应用中所有的UIView。从继承关系来看,UIWindow继承自UIView,所以UIWindow除了具有UIView的所有功能之外,还增加了一些特有的属性和方法,而我们最常用的方法,就是在App刚启动时,调用UIWindow的rootViewController(必须指定根控制器) 和 makeKeyAndVisible方法状态栏和键盘都是特殊的UIWindow。 UIWindow的主要作用有:1.作为UIView的最顶层容器,包含应用显示所有的UIView;2.传递触摸消息和键盘事件给UIView; UIWindow的层级:UIWindow的层级由一个UIWindowLevel类型属性windowLevel,该属性指示了UIWindow的层级,windowLevel有三种可取值。并且层级是可以做加减的self.window.windowLevel = UIWindowLevelAlert+1;
Normal ,StatusBar,Alert.输出他们三个层级的值,我们发现从左到右依次是0,1000,2000,也就是说Normal级别是最低的,StatusBar处于中级,Alert级别最高。而通常我们的程序的界面都是处于Normal这个级别的,系统顶部的状态栏应该是处于StatusBar级别,提醒用户等操作位于Alert级别。根据window显示级别优先原则,级别高的会显示在最上层,级别低的在下面,我们程序正常显示的view在最底层;
四.NS_ENUM & NS_OPTIONS
从枚举定义来看,NS_ENUM和NS_OPTIONS本质是一样的,仅仅从字面上来区分其用途。NS_ENUM是通用情况,NS_OPTIONS一般用来定义具有位移操作或特点的情况(bitmask)。五. 在任意页面,隐藏其他页面的键盘
1.当前viewController隐藏本页面的键盘
很容易。直接调用 [textfield resignFirstResponder]即可。2.目前前遇到一个需求
ControllerA出来时候,隐藏当前top 任意view的键盘。那么可以使用这个[objc] view plain copy[[[UIApplication sharedApplication] keyWindow] endEditing:YES];这是有根据的,OC sdk中有一个UIView的类别,就是endEditing。可以参考UITextField.h的头文件[objc] view plain copy@interface UIView (UITextField)
- (BOOL)endEditing:(BOOL)force; // use to make the view or any subview that is the first responder resign (optionally force)
@end
3.如果不方便获取当前view
可以使用该方法[objc] view plain copy[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponser) to:nil from:nil forEvent:nil];六.isKindOfClass和isMemberOfClass的区别
isKindOfClass和isMemberOfClass 都是NSObject的比较Class的方法 但两个有很大区别:isKindOfClass来确定一个对象是否是一个类的成员,或者是派生自该类的成员
isMemberOfClass只能确定一个对象是否是当前类的成员
例如:我们已经成NSObject派生了自己的类,isMemberOfClass不能检测任何的类都是基于NSObject类这一事实,而isKindOfClass可以。
[[NSMutableData data] isKindOfClass:[NSData class]]; // YES
[[NSMutableData data] isMemberOfClass:[NSData class]]; // NO
七.NSString属性什么时候用copy,什么时候用strong
由于NSMutableString是NSString的子类,所以一个NSString指针可以指向NSMutableString对象,让我们的strongString指针指向一个可变字符串是OK的。而上面的例子可以看出,当源字符串是NSString时,由于字符串是不可变的,所以,不管是strong还是copy属性的对象,都是指向源对象,copy操作只是做了次浅拷贝。当源字符串是NSMutableString时,strong属性只是增加了源字符串的引用计数,而copy属性则是对源字符串做了次深拷贝,产生一个新的对象,且copy属性对象指向这个新的对象。另外需要注意的是,这个copy属性对象的类型始终是NSString,而不是NSMutableString,因此其是不可变的。这里还有一个性能问题,即在源字符串是NSMutableString,strong是单纯的增加对象的引用计数,而copy操作是执行了一次深拷贝,所以性能上会有所差异。而如果源字符串是NSString时,则没有这个问题。所以,在声明NSString属性时,到底是选择strong还是copy,可以根据实际情况来定。不过,一般我们将对象声明为NSString时,都不希望它改变,所以大多数情况下,我们建议用copy,以免因可变字符串的修改导致的一些非预期问题。八.NSArray 各种遍历方式
编程中经常需要遍历collection元素,做法有标准的for循环,OC1.0的NSEnumerator和OC2.0的fast NSEnumerator。语言引入“块”这一特性后,又多出来几种新的遍历方式。 for循环大家很熟悉,很简单。但是遍历字典和set的时候就稍麻烦,for循环有一个优势,可以反向遍历。在删除数组中一个元素的时候采用反向遍历 OC 1.0的NSEnumerator是个抽象基类,定义了两个方法,供具体的子类实现[objc] view plain copy-(NSArray*) allObjects;
-(id) nextObject;
举例,遍历数字[objc] view plain copyNSArray *anArray=/*....*/;
NSEnumerator *enumerator = [anArray objectEnumerator];
id object;
while((object = [enumerator nextObject]) != nil){
//do something
}
快速遍历,就是for-in语句直接上代码,这种办法比上面两种都高效,安全,简单,强烈推荐!在删除数组元素的时候,同样可以反序执行。[objc] view plain copyNSArray *anArray=/*...*/;
for (id object in anArray){
//do something
}
//dictionary
NSDictionary* dic = /*...*/;
for(id key in dic){
id value = dic[key];
//do something
}
//set
NSSet *aSet = /*...*/;
for (id object in aSet){
//do something
}
//反向遍历
NSArray *anArray=/*...*/;
for (id object in [anArray reverseObjectEnumerator]){
//do something
}
基于块的变量方式 当前OC中,最新引入的一种做法句ishi基于块来遍历。这种做法比前三种效率都高,但是代码量比for-in多。[objc] view plain copyNSArray *anArray=/*...*/;
[anArray enumerateObjectUsingBlock]:
^(id object,NSUInter idx,BOOLBOOL *stop){
// do something
if(shouldStop){
*stop = yes;
}
}];
其他collection类似
九.NSLog 格式化输出数据
%@ 对象%d, %i 整数%u 无符整形%f 浮点/双字%x, %X 二进制整数%o 八进制整数%zu size_t%p 指针%e 浮点/双字 (科学计算)%g 浮点/双字%s C 字符串%.*s Pascal字符串%c 字符%C unichar%lld 64位长整数(long long)%llu 无符64位长整数%Lf 64位双字%e 是实数,用科学计数法计的十.iOS 中几种常用的锁总结
多线程编程中,应该尽量避免资源在线程之间共享,以减少线程间的相互作用。 但是总是有多个线程相互干扰的情况(如多个线程访问一个资源)。在线程必须交互的情况下,就需要一些同步工具,来确保当它们交互的时候是安全的。锁是线程编程同步工具的基础。iOS开发中常用的锁有如下几种:
@synchronized
NSLock 对象锁
NSRecursiveLock 递归锁
NSConditionLock 条件锁
pthread_mutex 互斥锁(C语言)
dispatch_semaphore 信号量实现加锁(GCD)
OSSpinLock (暂不建议使用,原因参见这里)
链接:https://www.jianshu.com/p/1e59f0970bf5
十一.内存泄露
ARC已经出来很久了,自动释放内存的确很方便,但是并非绝对安全绝对不会产生内存泄露。导致iOS对象无法按预期释放的一个无形杀手是——循环引用。循环引用可以简单理解为A引用了B,而B又引用了A,双方都同时保持对方的一个引用,导致任何时候引用计数都不为0,始终无法释放。若当前对象是一个ViewController,则在dismiss或者pop之后其dealloc无法被调用,在频繁的push或者present之后内存暴增,然后APP就duang地挂了。下面列举我们变成中比较容易碰到的三种循环引用的情形。(1)计时器NSTimer一方面,NSTimer经常会被作为某个类的成员变量,而NSTimer初始化时要指定self为target,容易造成循环引用。 另一方面,若timer一直处于validate的状态,则其引用计数将始终大于0。先看一段NSTimer使用的例子(ARC模式):
1 #import <Foundation/Foundation.h> 2 @interface Friend : NSObject 3 - (void)cleanTimer; 4 @end
1 #import "Friend.h" 2 @interface Friend () 3 { 4 NSTimer *_timer; 5 } 6 @end 7 8 @implementation Friend 9 - (id)init 10 { 11 if (self = [super init]) { 12 _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(handleTimer:) 13 userInfo:nil repeats:YES]; 14 } 15 return self; 16 } 17 18 - (void)handleTimer:(id)sender 19 { 20 NSLog(@"%@ say: Hi!", [self class]); 21 } 22 - (void)cleanTimer 23 { 24 [_timer invalidate]; 25 _timer = nil; 26 } 27 - (void)dealloc 28 { 29 [self cleanTimer]; 30 NSLog(@"[Friend class] is dealloced"); 31 }
在类外部初始化一个Friend对象,并延迟5秒后将friend释放(外部运行在非arc环境下)
1 Friend *f = [[Friend alloc] init]; 2 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5*NSEC_PER_SEC), dispatch_get_main_queue(), ^{ 4 [f release]; 5 });
我们所期待的结果是,初始化5秒后,f对象被release,f的dealloc方法被调用,在dealloc里面timer失效,对象被析构。但结果却是如此:
#import "Friend.h" @interface Friend () @property (nonatomic) NSArray *arr; @end @implementation Friend - (id)init { if (self = [super init]) { self.arr = @[@111, @222, @333]; self.block = ^(NSString *name){ NSLog(@"arr:%@", self.arr); }; } return self; }
我们看到,在block的实现内部又使用了Friend类的arr属性,xcode给出了warning, 运行程序之后也证明了Friend对象无法被析构:
网上大部分帖子都表述为"block里面引用了self导致循环引用",但事实真的是如此吗?我表示怀疑,其实这种说法是不严谨的,不一定要显式地出现"self"字眼才会引起循环引用。我们改一下代码,不通过属性self.arr去访问arr变量,而是通过实例变量_arr去访问,如下:
由此我们知道了,即使在你的block代码中没有显式地出现"self",也会出现循环引用!只要你在block里用到了self所拥有的东西!但对于这种情况,我们无法通过加__weak声明或者__block声明去禁止block对self进行强引用或者强制增加引用计数。但我们可以通过其他指针来避免循环引用(多谢xq_120的提醒),具体是这么做的:
1 self.arr = @[@111, @222, @333]; 2 __weak typeof(self) weakSelf=self; 3 self.block = ^(NSString *name){ 4 NSLog(@"arr:%@", weakSelf.arr); 5 };
2)MRC环境下:解决方式与上述基本一致,只不过将__weak关键字换成__block即可,这样的意思是告诉block:小子,不要在内部对self进行retain了! =========================================================(3)委托delegate在委托问题上出现循环引用问题已经是老生常谈了,本文也不再细讲,规避该问题的杀手锏也是简单到哭,一字诀:声明delegate时请用assign(MRC)或者weak(ARC),千万别手贱玩一下retain或者strong,毕竟这基本逃不掉循环引用了!
相关文章推荐
- iOS 基础知识大全之网络篇(可供零基础学习)
- ios基础知识学习
- cs193p斯坦福视频学习总结——iOS基础知识
- ios开发学习 --基础知识--系列教程
- iOS开发学习专题-基础知识(五) NSDate时间 NSUserDefaults本地存储 NSNotification系统通知的详细使用方式
- 【C/C++学习】C语言基础知识积累
- ios 学习之你画我话绘图五 构造路径基础知识
- IOS学习之路--OC的基础知识
- iOS转前端之HTML基础知识学习
- IOS 开发学习一 基础知识
- ios开发学习 --基础知识--系列教程
- ios学习笔记(一)基础知识
- iOS开发学习专题-基础知识(四) NSNumber容器 NSData数据 NSData转换的详细使用方式
- IOS开发学习中的基础知识究竟有多重要?
- ios学习笔记(一)基础知识
- ios学习--Runtime 基础知识
- IOS研究之IOS开发笔记基础知识学习
- iOS学习笔记-133.RunLoop01——基础知识
- IOS学习基础知识
- IOS学习之路--OC的基础知识