首页投稿(暂停使用,暂停投稿)

「Objective-C」基础

2016-05-16  本文已影响83人  _凉风_

一、Objective-C 简介

//BOOL类型本质
typedef signed char BOOL;

//BOOL变量有两种取值 YES/N
#define YES (BOOL)1
#define NO (BOOL)0

//BOOL类型输出「当做整数来用」
NSLog(@"%d %d",YES,NO);

二、Objective-C 特点

#pragma mark 「纯粹是Xcode的工具,非objc语法,可以写在任意一行,帮助阅读和查找函数」
不允许修改OC对象的 结构体成员

1. 对象中有一个 isa 指针,指向对象的类

基于 C 底层面向过程,弱语法,在运行过程中才会检测对象有没有实行相应的方法

2. __kindof

类型说明关键字「iOS9新增」
格式:- (__kindof ClassName *)function;
作用:告诉编译器返回值可能是 ClassName 或 ClassName的子类

3. 编译器特性: 点语法

p.name = @"haha" //编译器转为 [p setName:@"haha"];     但不能写成 p.setName
NSString *s = p.name; //编译器转为 [p name:@"haha"];   但不能写成 p.getName
p.test; //编译器转为 [p test];

<h2 id="propertyFirst">4. 编译器特性: @property @synthesize 关键字</h2>
@property - 上 基础

@property int age
// 编译器会自动替换为以下两行
- (void)setAge(int):age;
- (int)age;

// 参数不止一个 但 数据类型要相同 「一般很少这样连在一起」
@property int min, max;
@property (nonatomic, nonnull) NSString *name; // getter 和 setter方法都不能为空
@property (nonatomic) NSString *__nonnull name; // 和上一句等价,表示的 name值不能为空

// setter方法能为 nil,getter方法不能为 nil
// 因此,此属性定义后,要重写getter方法,确保getter方法不为nil,否则 编译器会警告
@property (nonatomic, null_resettable) NSString *name; 
@property (nonatomic, null_resettable) NSString *name;
NS_ASSUME_NONNULL_BEGIN
@interface class

@property NSString *__nullable name; 
@property NSString *mother; 

@property NSString *father; 
@end
NS_ASSUME_NONNULL_END

// 等价于

@interface class

@property NSString *__nullable name; 
@property NSString *__nonnull mother; 
@property NSString *__nonnull father; 
@end
// 规定数组只能装 NSString 类型数据
@property (nonatomic) NSArray<NSString *> *names; 

// 字典的key只能为NSString类型,value只能为NSNumber类型
@property (nonatomic) NSMutableDictionary<NSString *, NSNumber *> *names; 

// 自定义泛型,classType只是个占位符,可以随便起名
@interface className<classType> : NSObject
- (void)set:(classType)obj;
- (classType)get;
@end

// 使用自定义类型
// 不指名,默认为 id 类型
className *test0 = [[className alloc] init];
// 指明类型,必须为指明的类型
className<NSString *> *test1 = [[className alloc] init];

className<NSMutableString *> *test2 = [[className alloc] init];

// 类型转换
// 以下两行代码不会报警告
test0 = test1;
test1 = test0;

// 以下代码会报警告
// 为了防止警告,自定义泛型应添加 __covariant 为 @interface className<__covariant classType> : NSObject
test1 = test2; // __covariant:子类行 转为 父类型「仅限泛型的类型」
test2 = test1; // __cotravariant:父类行 转为 子类型「仅限泛型的类型」

@synthesize

@synthesize age = _age;

// 编译器会自动替换为以下代码

- (void)setAge(int):age {
    age = _age;
}
- (int)age {
    return _age;
}

@synthesize age;
// 编译器会自动替换为以下代码

- (void)setAge(int):age {
    self->age = age;
}
- (int)age {
    return age;
}

// 参数不止一个「一般很少这样连在一起」
synthesize min = _min, max = _max;

5. 常用函数

+ (void)load

+ (void)initialize

*- (NSString )description
NSLog 和 %@ 输出某个对象时

*+ (NSString )description
NSLog 和 %@ 输出某个类时

(void)NSLog 补充

char *s = "C语言字符串";
NSLog(@"%s", s); // 字符串含英文不能输出
NSString *o = @"Objective-c语言字符串";
NSLog(@"%@", o);
// 会造成死循环
- (void)test{    [self performSelector:_cmd];
}

6. SEL 数据类型

本质:消息机制中的消息就是 SEL
定义:<code>typedef struct objc_selector *SEL;</code>
作用:

使用:

// 方法一
SEL s1 = @seletor(方法名);
// 方法二
SEL s2 = NSSelectorFromString("方法名");
// 其他用法
// 将 SEL对象转换成 NSString 对象
NSString *s = NSStringFromSelector(@selector(方法名));
// 间接调用
[对象名 performSelector: @selector(方法名)];

7. @protocol 协议

作用:

基协议:

格式:

@protocol 协议名称
@required // 必须实现的方法「默认,不实现 编译器会警告」
// 方法声明
@optional // 可以不用实现的方法

// 方法声明
@end // 与 @protocol 对应
#import "协议名1.h" // 在 .h 文件这里也可以用 @protocol 协议名1, 协议名2
#import "协议名2.h" // 使用了提前声明的方法,在 .m文件要 #import "协议名1.h" ...
@interface 类名 : 父类 <协议名1, 协议名2>
@end
#import "协议名1.h"
#import "协议名2.h"
@protocol 协议名 <协议名1, 协议名2>
@end
类名 <协议名1, 协议名2> *变量名;
id<协议名1, 协议名2> 变量名; // 与上面一样效果
@property (nonmatic, strong) 类名<协议名1, 协议名2> *属性名;
@property (nonmatic, strong) id<协议名1, 协议名2> 属性名; // 与上面一样效果

用法:
协议可以定义在单独 .h 文件里,也可以定义在某个类中

8. 代理设计模式

原理:一个对象麻烦的事,代理给另一个对象做
原则:明确属性、方法,低耦合
代理的作用:

代理设计模式开发步骤:

代理的方法「如果代理有实现这个方法,才会调用这个方法」

if ([self.delegate respondsToSelector:@selector(loadMoreFooterDidClickLoadMoreBtn:)]) {
   [self.delegate loadMoreFooterDidClickLoadMoreBtn:self];
}
DelegateClass *d = [[DelegateClass alloc] init];
self.tableView = d;  // self → tableView → d
d.delegate = self; // d → delegate → self
// 相当于:self → tableView → d → delegate → self
self.tableView.delegate = self; // 循环引用

代理和通知的区别:

9. 通知

通知中心「NSNotificationCenter」:

初始化通知对象

/**
 *  @param aName     通知的名称
 *  @param anObject  通知的发布者
 *  @param aUserInfo 额外的信息(发布者传给接受着的信息内容)
 *  @return 初始化的通知对象
 */
+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject; 
+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;

- (instancetype)initWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo;

发布通知

/**
 *  @param aName     通知名称
 *  @param anObject  发布者
 *  @param aUserInfo 额外信息
 */

- (void)postNotification:(NSNotification *)notification;
- (void)postNotificationName:(NSString *)aName object:(id)anObject;
- (void)postNotificationName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;

注册通知监听器

/**
 *  @param observer  监听器「谁要监听这个通知」
 *  @param aSelector 收到通知后,回调监听器的方法,并把通知对像当参数传入
 *  @param aName     通知名称,若为nil,则全部监听器都可以收到这个通知
 *  @param anObject  通知发布者,若 aName 和 anObject 都为 nil则,监听器都收到所有的通知
 */
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject;

/**
 *  @param name  通知名称
 *  @param obj   通知发布者
 *  @param queue 决定block在那个操作队列中执行,若是 nil则在当前队列中执行
 *  @param block 收到对应通知时回调这个 block
 *
 *  @return 返回注册好的监听器
 */
- (id)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block;

取消通知监听器

// 通知中心提供了相应的方法来取消注册监听器
- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(NSString *)aName object:(id)anObject;

// 一般在监听器销毁之前取消注册(如在监听器中加入下列代码):
- (void)dealloc {
    // [super dealloc];  非ARC中需要调用此句
   [[NSNotificationCenter defaultCenter] removeObserver:self];
}

UIDevice 通知

10. KVC、KVO

KVC:Key Value Coding,常见作用:给模型赋值「模型必须与字典类的 属性名 相同」
KVC 原理:

  1. 遍历字典的所有的 Key
  2. 去所在类查找 有无 与Key值名称对应的
    • set方法:,若找到则,赋值
    • 没找到,则查找 _name属性,若找到 赋值
    • 没找到,则查找 name属性,若找到 赋值
    • 还没找到,报错
+ (instancetype)dealWithDic:(NSDictionary *)dict{
    Deal *deal = [[self alloc]init];
    [deal setValuesForKeysWithDictionary:dict];
    return deal;
}

KVO:Key Value Observing,常见作用:监听模型属性值的改变
原理:

缺点:由于时刻监听,耗费系统性能,能少用就少用

// 对象A 监听 对象B 的 属性名C是否改变「只要对象B 的属性C改变,则会通知对象A这件事」
// NSObjectA: 观察者「谁想监听」
[NSObjectB addObserver:NSObjectA forKeyPath:@"属性名C" options:(NSKeyValueObservingOptions) context:(nullable void *)];

// KVO监听方法:对象B 的属性C改变后,会调用此方法「是NSObject 的方法」
// 监听 object的 keyPath属性发生了改变「change 是根据上面 options的值产生的值改变的记录」
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
    NSLog(@"监听到 %@对像的 %@属性发生了 %@的改变",object,keyPath,change);
}

// 监听对象A销毁后要移除,否则再次传值给监听对象A的时候,会因找不到已经销毁的对象A而崩溃「是NSObject 的方法」
- (void)dealloc{
    [NSObjectB removeObserver:NSObjectA forKeyPath:@"属性名C"];
}
上一篇 下一篇

猜你喜欢

热点阅读