The Irving

不图钱,甚至不想马上再拿一个总冠军。欧文就这样离开了骑士。

对于这种做法,我除了钦佩还能表达什么。

期待下赛季欧文穿着其他球队球衣出征。

iOS 手动创建 Framework

iOS 手动创建 Framework

MyFramework project

  1. new project -> Cocoa touch Framework

  2. add headers to MyFramework.h

// Test.h, Test.m
@interface Test : NSObject
- (NSString* )test;
@end
@implementation Test
- (NSString* )test {
return @"test success";
}
@end
// MyFramework.h
#import <MyFramework/Test.h>
  1. add headers to Public

    MyFramework target -> Build Phases -> Headers -> Public

  2. build target -> MyFramework output

Framework test Demo project

  1. copy MyFramework to project

    using the MyFramework.framework output from last step

  2. Link Framework

    target -> Build Phases -> Link Binary With Libraries -> add MyFramework.framework to list

  3. copy Framework to app “Framework” destination
    target -> Build Phases -> New Copy Files Phases

    • Destination -> “Framework”
    • add MyFramework.framework to list

test

#import <MyFramework/MyFramework.h>
- (void)viewDidLoad {
[super viewDidLoad];
Test* t = [Test new];
NSLog(@"%@", [t test]);
// Do any additional setup after loading the view, typically from a nib.
}

聚合

这是最后一步,上述步骤创建的 Framework 只支持模拟器的指令集,为了能够使 Framework 同时支持真机和模拟器,需要创建一个包含真机指令集和模拟器指令集的 Framework。

指令集表格参考:

arm64 = iPhone 6s/6SP, iPhone 6/6P, iPhone 5s, iPad Air, Retina iPad Mini
armv7s = iPhone 5, iPhone 5c, iPad 4
armv7 = iPhone 3GS, iPhone 4, iPhone 4S, iPod 3G/4G/5G, iPad, iPad 2, iPad 3, iPad Mini
i386 = 32 bit simulator
x86_64 = 64 bit simulator

创建支持模拟器和真机的 Framework:

lipo -create /.../Build/Products/Release-iphonesimulator/MyFramework.framework/MyFramework /.../Build/Products/Release-iphoneos/MyFramework.framework/MyFramework -output MyFramework

查看聚集后的 Framework 支持的指令集:

-> lipo -info MyFramework
-> Architectures in the fat file: MyFramework are: i386 x86_64 armv7 arm64

可以通过创建一个 aggregate 的 target 和脚本来简化这些步骤

Editor -> Add Target -> cross-platform -> Aggregate

新建一个 script,aggregate.sh,放在项目根目录

# Sets the target folders and the final framework product.
# 如果工程名称和Framework的Target名称不一样的话,要自定义FMKNAME
# 例如: FMK_NAME = "MyFramework"
FMK_NAME=${PROJECT_NAME}
# Install dir will be the final output to the framework.
# The following line create it in the root folder of the current project.
INSTALL_DIR=${SRCROOT}/Products/${FMK_NAME}.framework
# Working dir will be deleted after the framework creation.
WRK_DIR=build
DEVICE_DIR=${WRK_DIR}/Release-iphoneos/${FMK_NAME}.framework
SIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator/${FMK_NAME}.framework
# -configuration ${CONFIGURATION}
# Clean and Building both architectures.
xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphoneos clean build
xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphonesimulator clean build
# Cleaning the oldest.
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
# Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product.
lipo -create "${DEVICE_DIR}/${FMK_NAME}" "${SIMULATOR_DIR}/${FMK_NAME}" -output "${INSTALL_DIR}/${FMK_NAME}"
rm -r "${WRK_DIR}"
open "${INSTALL_DIR}"

在 aggregate target 的 Build Phases 里的 Run Script 内容中

"${SRCROOT}/aggregate.sh"

最后选择 Generic iOS Devicecmd+B 就能看到聚集后的 Framework

refs:
MyFramework repo

MyFramework test demo repo

Framework Programming Guide

浅谈iOS中Library和Framework

iOS-制作Framework

@weakify, @strongify 的实现思路

@weakify, @strongify 的实现思路

这篇文章介绍一下 “libextobj” 中 @weakify, @srongify 的实现思路。

为了方便测试,写了个测试用例,整体测试思路借鉴于 “libextobjc” 的一个测试用例。

先在 @autoreleasepool{} 声明几个对象,然后用 captureBlock capture 住,那么在结束这个 autoreleasepool 之后,测试对象是否存在。

开发者一般都知道,为了使 captureBlock 不 capture 住变量,会使用 __weak 来修饰这个变量,然后再 captureBlock 内部在用 __strong 修饰这个变量,不过经常这样写就显得繁琐。

下面介绍一下 “libextobj” 是如何巧妙简化这些步骤的。

这是测试用例的代码:

- (void)testWeakStrong {
void (^verifyMemoryManagement)(void);
void (^captureBlock)(void);
@autoreleasepool {
NSString* cycleRefObj = [@"cycleRef" mutableCopy];
NSString* nCycle = [@"ncycle" mutableCopy];
NSString* lnc = [@"libextobj no cycle" mutableCopy];
__weak typeof(nCycle) wnc = nCycle;
@weakify(lnc)
captureBlock = ^{
NSLog(@"%@", cycleRefObj);
__strong typeof(nCycle) snc = wnc;
NSLog(@"%@", snc);
@strongify(lnc) // redeclare lnc
NSLog(@"strongify %@", lnc);
};
captureBlock();
__weak typeof(cycleRefObj) wc = cycleRefObj;
verifyMemoryManagement = ^{
XCTAssertNotNil(wc);
XCTAssertNil(wnc);
@strongify(lnc);
XCTAssertNil(lnc);
};
}
verifyMemoryManagement();
}

nCycle 对象是手写 weak-strong-dancing 的变量,可以看到,这样写非常繁琐。
lnc 对象是用 “libextobj” 来处理 weak-strong-dancing 问题。
cycleRefObj 对象则没有处理被 block capture 的问题。

@weakify 宏展开

// 省略了非关键宏展开
#define ext_weakify_(INDEX, CONTEXT, VAR) \
CONTEXT __typeof__(VAR) VAR ## _weak_ = (VAR);

也就是说如果输入 @weakify(a),那么展开后就是:

// 这里的 `CONTEXT` 是由上一个宏传进来的 `__weak`
__weak __typeof__(a) a_weak_ = (a);

造成的结果就是会声明一个 __weak 修饰的变量 a_weak_

@strongify 宏展开

// 省略了非关键宏展开
__strong __typeof__(VAR) VAR = VAR ## _weak_;

那么 @strongify(a) 的展开结果是:

__strong __typeof__(a) a = a_weak_;

可以看到,它会引用上一个 __weak 修饰后的变量 a_weak_,并且在 block 作用域中重新声明变量 a,这里就不用担心 a 又被 capture 进来的问题了,因为它是新的局部变量。

宏拯救世界,因为@weakify@strongify 支持多参数,所以可以一次性设置,@weakify(a, b, c)