iOS知识总结-iOS基础
一、OC
1、Proprety
1.1、@proprety的本质是什么?
@property = ivar + getter + setter
属性由两部分组成:实例变量(ivar)、存取方法(access method = getter + setter)
属性定义后,编译器会自动添加实例变量并编写访问这些变量所需的方法,此过程叫做“自动合成”(autosynthesis)
1.2、默认的属性特性是什么?
声明属性时,如果是基础数据类型,默认取值是atomic,readwrite、assign;如果是对象类型,默认取值是atomic,readwrite,strong
1.3、atomic是线程安全的吗?
不是,atomic通过锁机制可以保证setter和getter的完整性,是读写安全的,但不是线程安全的。
一个线程在连续多次读取某属性值的过程中有别的线程在同时改写该值,那么即便将属性声明为 atomic,也还是会读到不同的属性值。
线程安全:多个线程访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他操作,调用这个对象的行为都可以获得正确的结果,那么这个对象就是线程安全的
锁:当多个线程同时操作同一块资源或者说同一个对象的时候,可能会造成各种意想不到的情况(比如数据错乱、资源争夺、崩溃等),而锁就是为了能够保证同一时刻只有一个线程在操作这个数据应运而生的
1.4、assign修饰对象数据类型会怎样?
编译通过,实际使用刚初始化就被释放,warning:
Assigning retained object to unsafe property; object will be released after assignment
调用崩溃,EXC_BAD_ACCESS
1.5、weak修饰基本数据类型会怎样?
编译报错:
Property with 'weak' attribute must be of object type
1.6、什么场景使用weak?
1、ARC中避免循环引用,如delegate
2、已经强引用的对象,如IBOutlet
1.7、weak实现原理?
runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 Hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil
1.8、什么场景使用copy?
1、NSString、NSArray、NSDictionary 等等经常使用copy保证其封装性
2、block 使用 copy 是从 MRC 遗留下来的,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 可以把它放到堆区;在ARC中,编译器自动对 block 进行了 copy 操作
注意:NSString、NSArray、NSDictionary 的可变版本不能使用copy,否则有崩溃风险
2、Category
2.1、Category有什么作用?
1、Category的主要作用是为已经存在的类添加方法
2、Category可以把类的实现分开在几个不同的文件里面
2.2、Category为什么不能添加成员变量?
2.3、Category可以添加属性吗?
可以,从Category的定义也可以看出Category的可为(可以添加实例方法,类方法,甚至可以实现协议,添加属性)和不可为(无法添加实例变量)
3、KVC
3.1、KVC原理
Apple注释已经写的很清楚:
/* Given a value and a key that identifies an attribute, set the value of the attribute. Given an object and a key that identifies a to-one relationship, relate the object to the receiver, unrelating the previously related object if there was one. Given a collection object and a key that identifies a to-many relationship, relate the objects contained in the collection to the receiver, unrelating previously related objects if there were any.
The default implementation of this method does the following:
1. Searches the class of the receiver for an accessor method whose name matches the pattern -set<Key>:. If such a method is found the type of its parameter is checked. If the parameter type is not an object pointer type but the value is nil -setNilValueForKey: is invoked. The default implementation of -setNilValueForKey: raises an NSInvalidArgumentException, but you can override it in your application. Otherwise, if the type of the method's parameter is an object pointer type the method is simply invoked with the value as the argument. If the type of the method's parameter is some other type the inverse of the NSNumber/NSValue conversion done by -valueForKey: is performed before the method is invoked.
2. Otherwise (no accessor method is found), if the receiver's class' +accessInstanceVariablesDirectly property returns YES, searches the class of the receiver for an instance variable whose name matches the pattern _<key>, _is<Key>, <key>, or is<Key>, in that order. If such an instance variable is found and its type is an object pointer type the value is retained and the result is set in the instance variable, after the instance variable's old value is first released. If the instance variable's type is some other type its value is set after the same sort of conversion from NSNumber or NSValue as in step 1.
3. Otherwise (no accessor method or instance variable is found), invokes -setValue:forUndefinedKey:. The default implementation of -setValue:forUndefinedKey: raises an NSUndefinedKeyException, but you can override it in your application.
Compatibility notes:
- For backward binary compatibility with -takeValue:forKey:'s behavior, a method whose name matches the pattern -_set<Key>: is also recognized in step 1. KVC accessor methods whose names start with underscores were deprecated as of Mac OS 10.3 though.
- For backward binary compatibility, -unableToSetNilForKey: will be invoked instead of -setNilValueForKey: in step 1, if the implementation of -unableToSetNilForKey: in the receiver's class is not NSObject's.
- The behavior described in step 2 is different from -takeValue:forKey:'s, in which the instance variable search order is <key>, _<key>.
- For backward binary compatibility with -takeValue:forKey:'s behavior, -handleTakeValue:forUnboundKey: will be invoked instead of -setValue:forUndefinedKey: in step 3, if the implementation of -handleTakeValue:forUnboundKey: in the receiver's class is not NSObject's.
*/
- (void)setValue:(nullable id)value forKey:(NSString *)key;
1、先搜索对应的setter
2、再搜索成员变量
3、调用-setValue:forUndefinedKey:
,抛出异常
/* Given a key that identifies an attribute or to-one relationship, return the attribute value or the related object. Given a key that identifies a to-many relationship, return an immutable array or an immutable set that contains all of the related objects.
The default implementation of this method does the following:
1. Searches the class of the receiver for an accessor method whose name matches the pattern -get<Key>, -<key>, or -is<Key>, in that order. If such a method is found it is invoked. If the type of the method's result is an object pointer type the result is simply returned. If the type of the result is one of the scalar types supported by NSNumber conversion is done and an NSNumber is returned. Otherwise, conversion is done and an NSValue is returned (new in Mac OS 10.5: results of arbitrary type are converted to NSValues, not just NSPoint, NRange, NSRect, and NSSize).
2 (introduced in Mac OS 10.7). Otherwise (no simple accessor method is found), searches the class of the receiver for methods whose names match the patterns -countOf<Key> and -indexIn<Key>OfObject: and -objectIn<Key>AtIndex: (corresponding to the primitive methods defined by the NSOrderedSet class) and also -<key>AtIndexes: (corresponding to -[NSOrderedSet objectsAtIndexes:]). If a count method and an indexOf method and at least one of the other two possible methods are found, a collection proxy object that responds to all NSOrderedSet methods is returned. Each NSOrderedSet message sent to the collection proxy object will result in some combination of -countOf<Key>, -indexIn<Key>OfObject:, -objectIn<Key>AtIndex:, and -<key>AtIndexes: messages being sent to the original receiver of -valueForKey:. If the class of the receiver also implements an optional method whose name matches the pattern -get<Key>:range: that method will be used when appropriate for best performance.
3. Otherwise (no simple accessor method or set of ordered set access methods is found), searches the class of the receiver for methods whose names match the patterns -countOf<Key> and -objectIn<Key>AtIndex: (corresponding to the primitive methods defined by the NSArray class) and (introduced in Mac OS 10.4) also -<key>AtIndexes: (corresponding to -[NSArray objectsAtIndexes:]). If a count method and at least one of the other two possible methods are found, a collection proxy object that responds to all NSArray methods is returned. Each NSArray message sent to the collection proxy object will result in some combination of -countOf<Key>, -objectIn<Key>AtIndex:, and -<key>AtIndexes: messages being sent to the original receiver of -valueForKey:. If the class of the receiver also implements an optional method whose name matches the pattern -get<Key>:range: that method will be used when appropriate for best performance.
4 (introduced in Mac OS 10.4). Otherwise (no simple accessor method or set of ordered set or array access methods is found), searches the class of the receiver for a threesome of methods whose names match the patterns -countOf<Key>, -enumeratorOf<Key>, and -memberOf<Key>: (corresponding to the primitive methods defined by the NSSet class). If all three such methods are found a collection proxy object that responds to all NSSet methods is returned. Each NSSet message sent to the collection proxy object will result in some combination of -countOf<Key>, -enumeratorOf<Key>, and -memberOf<Key>: messages being sent to the original receiver of -valueForKey:.
5. Otherwise (no simple accessor method or set of collection access methods is found), if the receiver's class' +accessInstanceVariablesDirectly property returns YES, searches the class of the receiver for an instance variable whose name matches the pattern _<key>, _is<Key>, <key>, or is<Key>, in that order. If such an instance variable is found, the value of the instance variable in the receiver is returned, with the same sort of conversion to NSNumber or NSValue as in step 1.
6. Otherwise (no simple accessor method, set of collection access methods, or instance variable is found), invokes -valueForUndefinedKey: and returns the result. The default implementation of -valueForUndefinedKey: raises an NSUndefinedKeyException, but you can override it in your application.
Compatibility notes:
- For backward binary compatibility, an accessor method whose name matches the pattern -_get<Key>, or -_<key> is searched for between steps 1 and 3. If such a method is found it is invoked, with the same sort of conversion to NSNumber or NSValue as in step 1. KVC accessor methods whose names start with underscores were deprecated as of Mac OS 10.3 though.
- The behavior described in step 5 is a change from Mac OS 10.2, in which the instance variable search order was <key>, _<key>.
- For backward binary compatibility, -handleQueryWithUnboundKey: will be invoked instead of -valueForUndefinedKey: in step 6, if the implementation of -handleQueryWithUnboundKey: in the receiver's class is not NSObject's.
*/
- (nullable id)valueForKey:(NSString *)key;
1、先搜索对应的getter
2、再检查是否响应NSOrderedSet的方法
3、再检查是否响应NSArray的方法
4、再检查是否响应NSSet的方法
5、再搜索成员变量
6、调用-valueForUndefinedKey:
,抛出异常
4、KVO
4.1、KVO实现原理?
Automatic key-value observing is implemented using a technique called isa-swizzling... When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class ...
KVO是通过isa-swizzling技术实现的。在运行时根据原类创建一个中间类,这个中间类是原类的子类,并动态修改当前对象的isa指向中间类。并且将class方法重写,返回原类的Class。所以苹果建议在开发中不应该依赖isa指针,而是通过class实例方法来获取对象类型。
KVO.png4.2、KVO观察者何时移除?
KVO的addObserver
和removeObserver
需要是成对的,如果重复remove则会导致NSRangeException类型的Crash,如果忘记remove则会在观察者释放后再次接收到KVO回调时Crash。
苹果官方推荐的方式是,在init
的时候进行addObserve
r,在dealloc
时removeObserver
,这样可以保证add和remove是成对出现的,是一种比较理想的使用方式。
5、Notification
5.1、通知触发的事件是同步还是异步执行的?是否都在主线程执行?
1、通知触发的方法是同步执行的
2、通知触发的方法就在发出通知的线程执行
5.2、通知观察者何时移除?
If your app targets iOS 9.0 and later or macOS 10.11 and later, you don't need to unregister an observer in its `dealloc` method. Otherwise, you should call this method or `removeObserver:`before `observer` or any object specified in `addObserverForName:object:queue:usingBlock:` or `addObserver:selector:name:object:` is deallocated.
iOS9及更高版本不需要手动remove,但通过Block方式添加的仍然需要
6、Block
二、Foundation
1、NSArray实现原理?
环形缓冲区,内容能在到达任意一端时绕向另一端。除非缓冲区满了,否则在任意一端插入或删除均不会要求移动任何内存。
环形缓冲区.png
2、NSArray遍历的性能?
遍历性能.png3、NSDictionary实现原理?
Hash表
4、NSSet
5、NSOrderedSet
三、UIKit
1、为什么只能在主线程更新UI
因为UIKit不是线程安全的