BloodMagic 1.0.0

BloodMagic 1.0.0

测试已测试
Lang语言 Objective C++Objective C++
许可证 MIT
发布最后发布2014年12月

AlexDenisov 维护。



许可证 MIT   构建状态    

Objective-C 是一种强大的语言,但有时它缺乏自定义属性,如下所示

@property (nonatomic, strong, bm_lazy) ProgressViewService *progressView;
@property (nonatomic, strong, bm_partial) HeaderView *headerView;
@property (nonatomic, strong, bm_final) NSString *almostImmutable;

@property (nonatomic, strong, storable) NSString *authToken;

@property (nonatomic, strong, copyable) NSString *username;
@property (nonatomic, strong, copyable) NSString *email;

@property (nonatomic, strong, anything_you_want) AwesomeView *someAwesomeView;

我们不通过在 clang 上进行破解来实现这些属性,但幸运的是,我们可以通过 BloodMagic 的咒语来实现这些效果

常见问题解答

博客文章

演示AlexDenisov

演示Ievgen Solodovnykov

踏上黑暗之旅

通过 CocoaPods 进行简单安装

  pod 'BloodMagic', :git => 'https://github.com/railsware/BloodMagic.git'

或者您可以使用为 iOSOSX 构建的框架。

只需将框架拖放到您的项目中,并别忘了将 -all_load-ObjC 添加到 OTHER_LINKER_FLAGS

可用咒语

惰性初始化

依赖注入

部分视图

单次赋值属性

首选项(NSUserDefaults 包装器)

BloodMagic 被设计为可扩展的,因此很快还将提供更多咒语。

惰性初始化

  pod 'BloodMagic/Lazy', :git => 'https://github.com/railsware/BloodMagic.git'

按需初始化对象。

如果您使用 Objective-C,那么您应该熟悉以下代码

@interface ViewController : UIViewController

@property (nonatomic, strong) ProgressViewService *progressViewService;

@end
- (ProgressViewService *)progressViewService
{
    if (_progressViewService == nil) {
      _progressViewService = [ProgressViewService new];
    }

    return _progressViewService;
}

但我们能够自动执行此例程

只需将 BMLazy 协议添加到您的类中

@interface ViewController : NSObject
  <BMLazy>

@property (nonatomic, strong, bm_lazy) ProgressViewService *progressViewService;

@end

并将任何属性标记为 @dynamic

@implementation ViewController

@dynamic progressViewService;

@end

对象 progressViewService 将在第一次调用时初始化

self.progressViewService
// or
yourViewController.progressViewService

或者当您尝试获取密钥的值时

[self valueForKey:@"progressViewService"]
// or
[yourViewController valueForKey:@"progressViewService"]

默认情况下,它将使用类的方法的 +new 创建实例。

在这种情况下,progressViewService 将像通常属性一样被释放。

依赖注入

  pod 'BloodMagic/Lazy', :git => 'https://github.com/railsware/BloodMagic.git'

在创建 Lazy Initialization 咒语时发现了一个有趣的副作用 - 一种依赖注入。

例如,如果您需要以特殊方式初始化 progressViewService,可以创建一个特殊的初始化器。

BMInitializer *initializer = [BMInitializer lazyInitializer];
initializer.propertyClass = [ProgressViewService class]; // optional, uses NSObject by default
initializer.containerClass = [ViewController class]; // optional, uses NSObject by default
initializer.initializer = ^id (id sender){
    return [[ProgressViewService alloc] initWithViewController:sender];
};
[initializer registerInitializer];

注意: containerClass 在子类中不适用。要实现类似行为,您应该显式指定 containerClass

这个魔法在处理单例容器时非常有用。

BMInitializer *initializer = [BMInitializer lazyInitializer];
initializer.propertyClass = [RequestManager class];
initializer.initializer = ^id (id sender){
    static id singleInstance = nil;
    static dispatch_once_t once;
    dispatch_once(&once, ^{
      singleInstance = [RequestManager new];
    });
    return singleInstance;
};
[initializer registerInitializer];

因此,无论是 RequestManager 还是使用它的类,都不会意识到它是单例。

SRP(单一责任原则)的拥护者一定会赞同 ;)

此外,您还可以使用 @protocol

BMInitializer *initializer = [BMInitializer lazyInitializer];
initializer.protocols = @[ @protocol(ProgressViewServiceProtocol) ];
initializer.initializer = ^id (id sender){
    return [[ProgressViewService alloc] initWithViewController:sender];
};
[initializer registerInitializer];
懒加载钩子

BMLazy 模块提供了一个钩子系统来捕获对象创建。要启用这些钩子,只需创建一个名为 propertyNameInjected: 的实例方法即可。

例如:

@implementation ViewController

@lazy(progressViewService)

- (void)progressViewServiceInjected:(ProgressViewService *service)
{
    service.title = self.title;
}

@end

部分视图

  pod 'BloodMagic/Partial', :git => 'https://github.com/railsware/BloodMagic.git'

按需实例化 xib 视图,类似于 Lazy 模块。如果您有可重用的视图,此魔法可能非常有用。

例如:

您需要在不同部件中显示相同的用户信息(例如:在表格单元格中(UsersListViewController)和在某个标题视图中(UserProfileViewController))。在这种情况下,创建一个与 UserView 类关联的 UserView.xib,并在整个应用程序中使用它,是非常合理的。

所以这可能会看起来像这样:

// Cell used from UsersListViewController
// Created manually
@implementation UserViewCell
{
    UserView *_userView;
}

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        NSString *nibName = NSStringFromClass([UserView class]);
        UINib *nib = [UINib nibWithNibName:nibName bundle:nil];
        _userView = [[nib instantiateWithOwner:nil options:nil] lastObject];
        [self addSubview:_userView];
    }
    return self;
}

@end

// View used from UserProfileViewController
// Created from xib
@implementation UserHeaderView
{
    UserView *_userView;
}

- (void)awakeFromNib
{
    [super awakeFromNib];
    NSString *nibName = NSStringFromClass([UserView class]);
    UINib *nib = [UINib nibWithNibName:nibName bundle:nil];
    _userView = [[nib instantiateWithOwner:nil options:nil] lastObject];
    [self addSubview:_userView];
}

@end

这两种情况使用了相同的代码,BloodMagic 什么都不做,只是隐藏了这些样板代码。

#import <BloodMagic/Partial.h>

@interface UserViewCell ()
    <BMPartial>

@property (nonatomic, strong, bm_partial) UserView *userView;

@end

@implementation UserViewCell

@dynamic userView;

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self addSubview:self.userView];
    }
    return self;
}

@end

// ...

@interface UserHeaderView ()
    <BMPartial>

@property (nonatomic, strong, bm_partial) UserView *userView;

@end

@implementation UserHeaderView

@dynamic userView;

- (void)awakeFromNib
{
    [super awakeFromNib];
    [self addSubview:self.userView];
}

@end

一次赋值属性

  pod 'BloodMagic/Final', :git => 'https://github.com/railsware/BloodMagic.git'

Java 提供了 final 关键字,它确定了一个值不能被改变。

从现在起,BloodMagic 通过BloodMagic使这一功能在 Objective-C 中也变得可用。

#import <BloodMagic/Final.h>

@interface FinalizedObject : NSObject
    <BMFinal>

@property (nonatomic, strong, bm_final) NSString *almostImmutableProperty;

@end

@implementation FinalizedObject

@dynamic almostImmutableProperty;

@end

// ...

FinalizedObject *object = [FinalizedObject new];
object.almostImmutableProperty = @"Initial value"; // all is OK
object.almostImmutableProperty = @"Another value"; // exception will be thrown

偏好设置

pod 'BloodMagic/Preference', :git => 'https://github.com/railsware/BloodMagic.git'

享受处理 NSUserDefaults 的最简单方式。

#import <BloodMagic/Preference.h>

@interface Settings : NSObject
    <BMPreference>

@property (nonatomic, strong, bm_preference) NSString *nickname;

@end

@implementation Settings

@dynamic nickname;

@end

// ...

Settings *settings = [Settings new];
settings.nickname = @"AlexDenisov"; // @"AlexDenisov" goes to [NSUserDefaults standardUserDefaults] with key "nickname"
NSLog(@"My name is: %@", settings.nickname); // reads object for key "nickname" from [NSUserDefaults standardUserDefaults]

副作用(也就是错误)

BloodMagic 可能会有副作用。如果您发现了,请创建一个问题或给我们发送拉取请求。

这些行动将帮助我们保护您免受破坏。