// 给MJPerson类添加分类
@interface MJPerson : NSObject
- (void)run;
@end@implementation MJPerson
- (void)abc{}
- (void)run{NSLog(@"MJPerson - run");}
+ (void)run2{}
@end// MJPerson的分类,给MJPerson添加test方法
@interface MJPerson (Test)
- (void)test;
@end @implementation MJPerson (Test)
- (void)run{ NSLog(@"MJPerson (Test) - run");}
- (void)test{ NSLog(@"test");} // 会存放在类对象中
+ (void)test2 {} // 会存放在元类对象中
@end// 调用拓展出来的方法
MJPerson *person = [[MJPerson alloc] init];
[person run];
[person test];
Category与extension本质的区别
+load方法(+load方法是根据方法地址直接调用,并不是经过objc_msgSend函数调用)
+load方法会在runtime加载类、分类时调用。每个类、分类的+load,在程序运行过程中只调用一次。其调用顺序如下:
注意:主动调用load方法,会和消息发送机制一样,会调到分类里面的load方法。
+Initialize方法
在分类中添加一个属性,只会生成声明,不会生成成员变量和set/get的实现。
@interface MJPerson (Test)
// 各类中添加属性
@property (assign, nonatomic) int weight;
// 只会生成方法的声明
//- (void)setWeight:(int)weight;
//- (int)weight;
@end// 实现get set方法 来使得分类中的方法可以赋值和访问
#define MJKey [NSString stringWithFormat:@"%p", self]
@implementation MJPerson (Test)NSMutableDictionary *names_;
NSMutableDictionary *weights_;
+ (void)load
{ weights_ = [NSMutableDictionary dictionary];names_ = [NSMutableDictionary dictionary];
}- (void)setName:(NSString *)name { names_[MJKey] = name; }
- (NSString *)name { return names_[MJKey]; }- (void)setWeight:(int)weight { weights_[MJKey] = @(weight); }- (int)weight{ return [weights_[MJKey] intValue]; }
关联对象
#import "MJPerson.h"
@interface MJPerson (Test)
@property (copy, nonatomic) NSString *name;
@property (assign, nonatomic) int weight;
@end#import "MJPerson+Test.h"
#import @implementation MJPerson (Test)
- (void)setName:(NSString *)name
{objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}- (NSString *)name
{// 隐式参数// _cmd == @selector(name) 只有get方法才可以这么写return objc_getAssociatedObject(self, _cmd);
}- (void)setWeight:(int)weight
{objc_setAssociatedObject(self, @selector(weight), @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}- (int)weight
{// _cmd == @selector(weight)return [objc_getAssociatedObject(self, _cmd) intValue];
}// 这样就可以像调用类属性一样来调用分类里面的属性
person.name = "Lewis"
关联对象本质
实现关联对象技术的核心如下:
block的定义、简单使用
// 格式
返回值 (^block)(参数) = ^{ block的内容 }// 例子
void (^block)(void) = ^{ NSLog(@"this is a block") }
// 调用
block()
block本质
block捕获变量
为了保证block内部能够正常访问外部的变量,block有个变量捕获机制。
<1>. 捕获局部auto类型变量(自动变量:离开作用域就会自动销毁)
<2>. 也可以捕获局部static类型变量
// 默认定义的局部变量,默认就是auto
// auto int age = 10
int age = 10 // 传递是age的值
static int height = 10 // 传递的是height的地址值
// 在创建这个block对象的时候,age和height的值已经存储到了block的内存中
void (^block)(void) = ^{ // age和height的值已经被捕获进来了NSLog(@"age is %d, height is %d", age, height);
};
age = 20;
height = 20;
// 通过*height取出height的值,只是通过指针取出指向的外部的值
block(); // 输出:age is 10, height is 20
注:1>.self(局部变量)也会被捕获。2>.如果block里面使用的成员变量,那么其实捕获的是self对象。
block的类型
block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型。
存放区域如下:
int a = 10;// 堆:动态分配内存,需要程序员申请申请,也需要程序员自己管理内存
void (^block1)(void) = ^{NSLog(@"Hello");
};
int age = 10;
void (^block2)(void) = ^{NSLog(@"Hello - %d", age);
};NSLog(@"%@ %@ %@", [block1 class], [block2 class], [^{NSLog(@"%d", age);
} class]);// 输出:__NSGlobalBlock__ __NSMallocBlock__ __NSStackBlock__
void (^block)(void);
void test2()
{// NSStackBlockint age = 10;block = [^{NSLog(@"block---------%d", age);} copy];// copy操作之后 block就会在堆上,就会变成NSMallocBlock// 如果没有copy操作,那么这个block捕获的是auto变量,所以是一个NSStackBlock,所以在作用域之外调用,打印出的age值不会是10,因为这个age已被释放[block release];
}void test()
{// Global:没有访问auto变量void (^block1)(void) = ^{NSLog(@"block1---------");};// Stack:访问了auto变量int age = 10;void (^block2)(void) = ^{NSLog(@"block2---------%d", age);};NSLog(@"%p", [block2 copy]);// NSLog(@"%@ %@", [block1 class], [block2 class]);
}int age = 10;int main(int argc, const char * argv[]) {@autoreleasepool {int a = 10;NSLog(@"数据段:age %p", &age);NSLog(@"栈:a %p", &a);NSLog(@"堆:obj %p", [[NSObject alloc] init]);NSLog(@"数据段:class %p", [MJPerson class]);}return 0;
}
block的自动copy操作
当在ARC机制下有下列情况的时候,会自动对block进行copy操作
ARC下block属性的建议写法
对象类型的auto变量、__block变量、__block修饰的对象
而__block修饰的对象会根据所指向对象的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用(注意:这里仅限于ARC时会retain,MRC时不会retain)
__block修饰符
循环引用的解决
首先概括一下这几个概念。其中SM(Streaming Multiprocessor)和SP(streaming Processor)是硬件层次的,其中一个SM可以包含多个SP。thread是一个线程,多个thread组成一个线程块block,多个block又组成一个线程网格grid。 现在就说一下一个kenerl函数是怎么执行的。一...
下面的程序输出什么?思考一下
#include
原文:https://blog.csdn.net/ygtlovezf/article/details/80528300 blktrace对于分析block I/O是个非常好的工具,本篇文章记录了如何使用blktrace。 blktrace原理 blktrace是对通用块层(block layer)的I/O跟踪机制,它能抓取详细的...
initializeGL函数由于是初始化,所以只调用一次 而resizeGL和paintGL会多次调用 resizeGL在正常的情况下,触发的条件是,窗口大小发生变化时,resizeGL被调用,之后会触发paint事件,从而调用paintGL()事件处理器。 paintGL在正常的情况下,只要你移动窗口的位置,就会马上触发pain...
思考:在析构函数中delete this指针,运行下面代码会产生什么样的结果呢?
#include
(1)如果用定时器的话,初始的时候注册一个定时器的回调函数,原型是 glutTimerFunc(unsigned int millis, void (*func)(int value), int value); 参数对应关系为:glutTimerFunc(毫秒数, 回调函数指针, 区别值); (2)写自己的回调函数 v...
0x00 前置信息 VLC是一个非常庞大的工程,我从它的架构及流程入手进行分析,涉及到一些很细的概念先搁置一边,日后详细分析。 0x01 源码结构(Android Java相关的暂未分析) # build-android-arm-linux-androideabi/:第三方库。 # modules/:模块代码。 # modules/...
因为函数参数是按值传递的,所以要想改变变量,必须传递地址。 二级指针实际上就是指针变量的地址,如果传递二级指针,函数声明必须写**。 (void**)&必须是本质上就是指针变量的地址才可以做这样的转换,并不是说把一个一级指针也可以转换,void**的本质是标识一个二级指针。 &data就是(默认数据类型 **)&data,(void...
文章目录1. 解决问题2. 应用场景3. 实现如下:C++实现C语言实现4. 缺点 1. 解决问题 在工厂方法模式中,我们卖衣服。此时我们为每一种衣服创建不同的工厂,帽子有一个工厂专门创建,裤子有一个工厂专门创建,T恤有一个工厂专门创建。这样的方式保证了代码设计的开闭原则(对扩展开发,对修改关闭),解决了简单工厂模式中暴露的...
转载于:http://blog.csdn.net/u012819339/article/details/50654764 实体作品请参看优酷视频。 若以上链接点击无效请把该链接地址复制到浏览器地址栏 http://v.youku.com/v_show/id_XODYzODczNzQ4.html 说明: 该作品为arvik于2014...
- (void)viewDidLoad {[super viewDidLoad];NSLog(@"我在玩手机");NSLog(@"手机没电了");[self chargeMyIphone:^{NSLog(@"出门逛街");}];NSLog(@"我在看电视"); }-(void)chargeMyIphone:(void(^)(void...
http://stackoverflow.com/questions/150446/how-do-i-detect-when-someone-shakes-an-iphone 我的实现(基于Eran Talmor): 没必要application.applicationSupportsShakeToEdit = YES; Set th...