@interface EOCPerson : NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
//上面写出来的类于下面这种写法等效
- (NSString*)fullName;
-(void)setFullName:(NSString*)fullName;
EOCPerson *person = [[EOCPerson alloc] init];
//两种写法是一样的
person.firstName = @"Bob";
[person setFirstName:@"Bob"];
//两种写法是一样的
NSString *lastName = person.lastName;
NSString *lastName1 = [person lastName];
原子性
有效的属性值。若是不加锁的话(即nonatomic),那么其中一个线程正在改写某属性时,另一个线程突然闯入,就有可能读到不准确的数值。如果开发过iOS程序,你就会发现,所有的属性都声明为 nonatomic。这样做的历史原因是:在iOS程序中使用同步锁的开销较大,这会带来性能问题。一般情况下并不要求属性必须是“原子的”,因为这并不能保证“线程安全”,若要实现“线程安全”的操作,还需采用更为深层的锁定机制才行。例如:一个线程在连续多次读取某属性值的过程中有别的线程在改写该值,那么即便属性声明为atomic,也还是会读到不同的属性值。因此,开发iOS程序时一般都会使用nonatomic属性。但是在开发MacOS X程序时,使用atomic属性通常时没有问题的。
读写权限
3.内存管理语义
ARC是iOS 5推出的新功能,全称叫 ARC(Automatic Reference Counting)。简单地说,就是代码中自动加入了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。该机制在iOS 5/ Mac OS X 10.7 开始导入,利用 Xcode4.2 可以使用该机制。简单地理解ARC,就是通过指定的语法,让编译器(LLVM 3.0)在编译代码时,自动生成实例的引用计数管理部分代码。
iOS 属性关键字
Objective-C高级编程之引用计数,看我就够了
被无数人写过的assign,retain,strong,weak,unsafe_unretained,还有copy
可以指定存取方法的方法名
点语法和直接访问实例变量
在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,则应通过属性来写。
//点语法
self.firstName
//直接访问
_firstName
在初始化方法及dealloc方法中,总是应该直接通过实例变量来读写数据,因为子类可能会覆写。
有时会使用惰性初始化技术配置某份数据,这种情况下,需要通过属性来读取属性来读取数据。
如果使用来“惰性初始化”技术,那么必须通过存取方法来访问brain属性(在重写getter方法时,不能通过self.label对属性label进行访问,因为用点语法就相当于调用set或get方法,导致循环调用,在外部调用点语法时就形成无限循环。)
iOS 懒加载_Echo &的博客-CSDN博客
iOS - 判断对象相等,重写isEqual,hash_爱尔兰堤坝的博客-CSDN博客
NSObject协议中有两个用于判断等同性的关键方法:
-(BOOL)isEqual:(id)object;
//1. 首先判断两个指针是否相等
//2. 比较两个对象所属的类
//3. 检验每个属性是否相等
//4.实现hash方法
- (NSUInteger)hash;
-(BOOL)isEqual:(id)object
{
//1. 首先判断两个指针是否相等
//2. 比较两个对象所属的类
//3. 检验每个属性是否相等
//4.实现hash方法
if (self == object) return YES;
if ([self class] != [object class]) return NO;
Person *otherPerson = (Person*)object;
if (![_firstName isEqualToString:otherPerson.firstName]) {
return NO;
}
if (![_lastName isEqualToString:otherPerson.lastName]) {
return NO;
}
if (_age != otherPerson.age) {
return NO;
}
return YES;
}
//这种做法能提高效率,又能使生成的哈希码至少位于一定范围之内
-(NSUInteger)hash
{
NSUInteger firstNameHash = [_firstName hash];
NSUInteger lastNameHash = [_lastName hash];
NSUInteger ageHash = _age;
return firstNameHash^lastNameHash^ageHash;
}
如果“isEqual:”方法判定两个对象相等,那么其hash方法也必须返回同一个值。但是,如果两个对象的hash方法返回同一个值,那么“isEqual:”方法未必会认为两者相等。
#import "ViewController.h"
#import
static void *MyAlertViewKey = "MyAlertViewKey";
@interface ViewController ()<UIAlertViewDelegate>
@end
@implementation ViewController
//在创建警告框视图的时候直接把处理每个按钮的逻辑都写好,可以通过关联对象来做
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:@"信息" delegate:self cancelButtonTitle:@"NO" otherButtonTitles:@"YES", nil];
void (^block)(NSInteger) = ^(NSInteger buttonIndex){
if (buttonIndex == 0) {
NSLog(@"NO");
} else {
NSLog(@"YES");
}
};
objc_setAssociatedObject(alert, MyAlertViewKey, block, OBJC_ASSOCIATION_COPY);
[alert show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
void(^block)(NSInteger) = objc_getAssociatedObject(alertView, MyAlertViewKey);
block(buttonIndex);
}
此方法根据给定的键从某对象中获取相应的关联对象值
objc_getAssociatedObject(<#id _Nonnull object#>, <#const void * _Nonnull key#>);
此方法以给定的键和策略为弄对象设置关联对象值
objc_setAssociatedObject(<#id_Nonnull object#>, <#const void * _Nonnull key#>, <#id
_Nullable value#>, <#objc_AssociationPolicy policy#>);
此方法移除指定对象的全部关联对象
objc_removeAssociatedObjects(<#id _Nonnull object#>)
关联对象
runtime - 关联对象_CatStarXcode的博客-CSDN博客
可以通过“关联对象”机制来把两个对象连起来;
定义关联对象时可指定内存管理语义,用以模仿定义属性时所采用的“拥有关系(保留)”与“非拥有关系(不保留)”
只有在其他做法不可行时才应选用关联对象,因为这种做法通常会引入难于查找的bug;
在OC中,如果向某对象传递消息,那就会使用动态绑定机制来决定需要的方法
首先给对象发送信息,
void returnValue = [someObject messageName:parameter];
在本例中,someObject叫做“接受者”,messageName叫做“选择子”,选择子与参数合起来称为“消息”。编译器看到此消息后,将其转换为一条标准的C语言函数调用
所调用的函数乃是消息传递机制中的核心函数,叫做objc_msgSend,原型如下:
void objc_msgSend(id self, SEL cmd,...)
//编译器会将刚刚那个例子中的消息转换为如下函数
id returnValue = objc_msgSend(SomeObject, @selector(messageName:),parameter);
objc_msgSend函数会依据接受者与选择子的类型来调用适当的方法
objc_msgSend 函数会依据接收者与选择子的类型来调用适当的方法,为了完成此操作,该方法需要接收者所属的类中搜寻其“方法列表”(list of methods),如果能找到与选择子名称相符的方法,就跳至其实现代码。若是找不到,那就沿着继承体系继续向上查找,等找到合适的方法之后再跳转,如果最终还是找不到相符的方法。那就执行“消息转发”(message forwarding)操作;这么说来。想调用一个方法似乎需要很多步骤,
所幸objc_msgSend会将匹配结果缓存在“快速映射表”(fast map)里面。每个类都有这样一块缓存,若是稍后还向该类发送与选择子相同的消息,那么执行起来就很快了。
消息由接收者,选择子及参数构成,给某对象“发送消息”(invoke a message)也就相当于在该对象上“调用方法”(call a method);然后通过“动态消息派发系统“即objc_msgSend函数,查出对应的方法,并执行代码
当我们在.h文件中写了一个方法,但是却没写实现,你会发现你可以调用这个方法,编译器在编译器不会报错,它相信在运行期可以找到方法实现,因为并没有,所以在运行的时候会崩。
当对象收到无法解读的消息后,将启动消息转发机制,程序员可经由此过程告诉对象应该如何处理未知消息。
首先将调用其所属类的一个方法,这个方法表示这个类能否新增一个实例方法用来处理这个选择子,在继续向下执行转发机制之前,本类有机会新增一个处理此选择子的方法。
+ (BooL)resolveInstanceMethod:(SEL)sel
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSString *selectorString = NSStringFromSelector(sel);
Method method = class_getInstanceMethod([self class], @selector(rush));
if ([selectorString isEqualToString:@"rushB"]) {
class_addMethod(self, sel, method_getImplementation(class_getInstanceMethod([self class], @selector(rush))), method_getTypeEncoding(method));
return YES;
}
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types);
这个方法主要接受四个参数
动态方法解析
备援接收者
完整的消息转发
若对象 无法响应某个选择子,则进入消息转发流程;
通过运行期的动态方法解析功能,我们可以在需要用到某个方法时再将其加入类中
对象可以把其无法解读的某些选择子转交给其他对象来处理
经过上述两步之后,如果还是没有处理选择子,那就启动完整的消息转发机制;
方法调配:类的方法列表会把选择子的名称映射到相关的方法实现上。这种方法均以函数指针的形式来表示,这种指针叫做IMP
原型
id (*IMP) (id, SEL,...)
NSString类可以响应lowercaseString,uppercaseString,capitalizedString等选择子
可以用于调试,但很少有人在调试程序之外的场合用上述“方法调配”来永久改动某个类的功能。
选择子其实是方法的名称,不同类中方法名相同参数不同的俩个方法,他们的选择子是相同的。
// Method
struct objc_method {
SEL method_name;
char *method_types;
IMP method_imp;
};
方法名 method_name 类型为 SEL,前面提到过相同名字的方法即使在不同类中定义,它们的方法选择器也相同。
方法类型 method_types 是个 char 指针,其实存储着方法的参数类型和返回值类型,即是 Type Encoding 编码。(即类型编码)
method_imp 指向方法的实现,本质上是一个函数的指针
typedef struct objc_object {
Class isa;
} id;
每个对象结构体的首个成员是Class类的变量。该变量定义了对象所属的类,通常称为*“isa”指针**。
判断是否是这个类或者这个类的子类的实例
判断是否是这个类的实例