第一章:熟悉Objective-C
1. 特性:动态语言,运行时确定绑定关系
2. 减少头文件的引用;记住一个“向前声明”;避免类相互引用;使用#import
3. 多用字面量写法(现代语法),少用等价长方法
4. 多用类型变量,少用#define预处理指令,如:
- 类内使用
static const NSInteger kTime = 0.3
- 类外使用,名称使用类名为前缀
xxx.h
extern nsstring *const classname_a;
xxx.m
nsstring const classname_a = @"value";
5. 用枚举表示状态、选项、状态码,配合Switch使用。
typedef NS_ENUM(NSInteger, UIButtonRole) {
UIButtonRoleNormal,
UIButtonRolePrimary,
UIButtonRoleCancel,
UIButtonRoleDestructive
} API_AVAILABLE(ios(14.0));
typedef NS_OPTIONS(NSUInteger, SDRectCorner) {
SDRectCornerTopLeft = 1 << 0,
SDRectCornerTopRight = 1 << 1,
SDRectCornerBottomLeft = 1 << 2,
SDRectCornerBottomRight = 1 << 3,
SDRectCornerAllCorners = ~0UL // 无符号长整型0
};
第二章:对象、消息、运行期
OC编程 = 对象(基本结构单元)+ 消息传递(messaging)
6. 属性
- 封装对象数据
-
点语法 - 访问对象数据,编译器在编译时,生成存取方法和变量:
Property
=_str
+- (NSString *)str
+- (void)getStr:(NSString *)str
- 属性特质(修饰符),控制编译器生成存取方法
7.在对象内部尽量直接访问实例变量
- 读数据 - 直接使用实例变量
- 写数据 - 使用属性
- 初始化方法或者dealloc方法中直接使用实例变量来读、写数据
- 懒加载时,使用属性来读取数据
8. 对象等同性
- 对比:
==
指针相等
hash
值(先判断) +(BOOL)isEqual:(id)object;
重写isEqual方法:
- (BOOL)isEqual:(id)object {
if (self == object) {//指针相等
return YES;
}
if (![object isKindOfClass:[Person class]]) {//同类
return NO;
}
return [self isEqualToPerson:(Person *)object];
}
- (BOOL)isEqualToPerson:(Person *)person {//类中数据相等(即属性)
if (!person) {
return NO;
}
BOOL haveEqualNames = (!self.name && !person.name) || [self.name isEqualToString:person.name];
BOOL haveEqualBirthdays = (!self.birthday && !person.birthday) || [self.birthday isEqualToDate:person.birthday];
return haveEqualNames && haveEqualBirthdays;
}
重写hash方法
//In reality, a simple XOR over the hash values of critical properties is sufficient 99% of the time(对关键属性的hash值进行位或运算作为hash值)
- (NSUInteger)hash {
return [self.name hash] ^ [self.birthday hash];
}
- 特定类等同判定方法
- (BOOL)isEqualToDictionary:(NSDictionary<KeyType, ObjectType> *)otherDictionary;
- (BOOL)isEqualToArray:(NSArray<ObjectType> *)otherArray
- (BOOL)isEqualToString:(NSString *)aString;
9. 以“类簇”模式隐藏实现细节
原理解释:通过一个对象(类)来存储不同类型的数据变量,其内部不能修改。
注:请不要尝试去创建NSString、NSArray或NSDictionary的子类。如果必须添加或修改某个方法,可以使用类别的方式。
10. 关联对象(AssociatedObject)- 把两个对象关联起来
- 分类 - 扩展方法
- 关联对象 - 扩展属性
// 关联对象:policy 内存管理策略
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
id _Nullable value, objc_AssociationPolicy policy)
// 获取关联对象
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
// 删除关联对象
objc_removeAssociatedObjects(id _Nonnull object)
11. 消息传递
// self - receiver接收者、op - Selector选择子
objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
发送消息 = 调用方法
//
id returnValue = [someObject messageName:parameter];
id returnValue = objc_msgSend(someObject, @selector(messageName:), paramater);
12. 消息转发机制
例:[obj test];
- > objc_msgSend(obj, test)
(runtime方法)
runtime的执行步骤:
- obj(isa) - > class
- 在class method list中找到test
- 没有找到test,找superclass
- 找到test后,执行IMP
相关API:
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");
代码实现过程:
- 定义Property,声明为@dynamic,没有实现方法
- 在方法
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
添加转发代码。
13. 方法调配技术 - 运行时,交换类方法
涉及方法:
OBJC_EXPORT IMP _Nonnull
method_setImplementation(Method _Nonnull m, IMP _Nonnull imp) ;
OBJC_EXPORT void
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2) );
以下是腾讯云SDK的捕获方法调用异常代码。
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self changeImplementation];
});
}
+ (void)changeImplementation {
Class class = object_getClass((id)self);
Method originMethod = class_getClassMethod(class, @selector(exceptionWithName:reason:userInfo:));
Method replacedMethod = class_getClassMethod(class, @selector(qcloud_exceptionWithName:reason:userInfo:));
method_exchangeImplementations(originMethod, replacedMethod);
}
+(NSException *)qcloud_exceptionWithName:(NSExceptionName)name reason:(NSString *)reason userInfo:(NSDictionary *)userInfo{
NSException *exp = [self qcloud_exceptionWithName:name reason:reason userInfo:userInfo];
[QualityDataUploader trackSDKExceptionWithException:exp];
return exp;
}
14. 理解类对象的用意
SomeClass的子类从NSObject中继承而来,其继承体系:
SomeClass实例所属的“类继承体系”
每个类仅有一个“类对象”,每个“类对象”仅有一个与之相关的“元类”。
- 定义id对象
typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
-
此结构体存放类的“元数据metadata”:
isa - 对象所属类型
super_class - 元类
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
- 类型查询体系
// 类或者派生类的实例
- (BOOL)isKindOfClass:(Class)aClass;
// 特定类的
- (BOOL)isMemberOfClass:(Class)aClass;
16. 提供“全能初始化方法”
Person.h
@interface Person : NSObject
@property (nonatomic, assign) float age;
@property (nonatomic, strong) NSString *name;
// 全能初始化方法
- (id)initWithAge:(CGFloat)age
withName:(NSString *)name;
@end
Person.m
@implementation Person
- (id)initWithAge:(CGFloat)age
withName:(NSString *)name{
if (self = [super init]) {
_age = age;
_name = name;
}
return self;
}
// 覆写超类的init方法
- (instancetype)init{
return [self initWithAge:0 withName:@""];
// @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"replace init with new function" userInfo:nil];
}
@end
17. 自定义对象的description,po调试使用,或者对象类型强转打印
- (NSString *)description{
return [NSString stringWithFormat:@"%@ is %ld years old", _name, (long)_age];
}
断点debug打印:
(lldb) po person
Lucy is 20 years old
对象类型强转后打印:
(lldb) po person
<Person: 0x600003e79960>
(lldb) po ((Person *)person).name
Lucy
(lldb) po ((Person *)person).age
20
18. 尽量使用不可变对象
尽量创建不可变对象,对象内部确实需要修改时,才把扩展属性有readonly换成readwrite(或缺省),外部使用时,使用方法公开、修改回调。
19. function命名方式
// 返回类型 with 传参1...2...3
+ (instancetype)stringWithString:(NSString *)string;
返回类型 with 传参1...2...3 属性赋值
+ (instancetype)stringWithCharacters:(const unichar *)characters length:(NSUInteger)length;
// “是否有”
- (BOOL)hasPrefix:(NSString *)str;
// "是否相等"
- (BOOL)isEqualToString:(NSString *)aString;
20. 区分公共方法和私有方法
公共方法名尽量不要修改,修改后外部调用会出错。
给私有方法前边加上p_
,如:
- (void)p_privateMethod;
或者
#pragma mark public
// 外部访问方法
#pragma mark private
// 内部方法
21. “异常(NSException)”处理机制
极端情况下抛出异常,不用考虑恢复,所以异常代码简单就行。
- (QCloudSignature *)signatureForData:(id)signData {
@throw [NSException exceptionWithName:QCloudErrorDomain reason:@"请在子类中实现该函数" userInfo:nil];
}
22. 理解NSCopying、NSMutableCopying协议
@protocol NSCopying
- (id)copyWithZone:(nullable NSZone *)zone;
@end
@protocol NSMutableCopying
- (id)mutableCopyWithZone:(nullable NSZone *)zone;
@end
类遵循了NSCopying协议
,其对象就支持copy操作。
具体代码实现:
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
return [self doCopyWithZone:zone];
}
- (id)doCopyWithZone:(nullable NSZone *)zone {
TVideoSegmentItem *model = [[TVideoSegmentItem allocWithZone:zone] init];
[[[self class] getPropertyNameList:[model class]] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
id value = [self valueForKey:obj];
if ([value isKindOfClass:[NSMutableDictionary class]] ||
[value isKindOfClass:[NSDictionary class]]) {
NSMutableDictionary *newObj = [[NSMutableDictionary alloc] initWithDictionary:value copyItems:YES];
[model setValue:newObj forKey:obj];
}
else if ([value isKindOfClass:[NSArray class]] ||
[value isKindOfClass:[NSMutableArray class]]) {
NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:value copyItems:YES];
[model setValue:newArray forKey:obj];
}
else if ([value isKindOfClass:[NSMutableString class]] ||
[value isKindOfClass:[NSString class]]) {
NSMutableString* newObj = [value mutableCopy];
[model setValue:newObj forKey:obj];
}
else {
[model setValue:[self valueForKey:obj] forKey:obj];
}
}];
return model;
}
+ (NSArray *)getPropertyNameList:(Class)cls {
NSMutableArray *propertyNameListArray = [NSMutableArray array];
unsigned int count = 0;
objc_property_t *properties = class_copyPropertyList(cls, &count);
for (NSInteger i = 0 ; i < count; i++) {
const char *propertyCharName = property_getName(properties[i]);//c的字符串
NSString *propertyOCName = [NSString stringWithFormat:@"%s",propertyCharName];//转化成oc 字符串
[propertyNameListArray addObject:propertyOCName];
}
NSArray *dataArray = [NSArray arrayWithArray:propertyNameListArray];
return dataArray;
}
注:
// NSCopying
[NSMuatbleArray copy] => array
// NSMutableCopying
[NSArray mutableCopy] => mArray
深拷贝与浅拷贝对比图
浅拷贝后内容与原内容均指向相同对象,而深拷贝后的内容是原内容对象的拷贝的一份。注意其内容对象的可变性未变。
网友评论