var createFiber = function (tag, pendingProps, key, mode) { // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors returnnew FiberNode(tag, pendingProps, key, mode); };
functionworkLoopSync() { // Already timed out, so perform work without checking if we need to yield. while (workInProgress !== null) { console.log('perform unit: ', workInProgress); performUnitOfWork(workInProgress); } }
// Grab a strong reference to the object, otherwise it can crash while doing // nasty stuff on deallocation __attribute__((objc_precise_lifetime)) id anObject = self.object;
/** Specialization of FBObjectiveCObject for NSTimer. Standard methods that FBObjectiveCObject uses will not fetch us all objects retained by NSTimer. One good example is target of NSTimer. */ @interfaceFBObjectiveCNSCFTimer : FBObjectiveCObject @end
从类声明中可以看到,FBObjectiveCNSCFTimer 继承自上一篇文章提到的普通OC对象 FBObjectiveCObject ,这也很好理解,在 Foundation 框架中,NSTimer 也是继承自 NSObject 的。
info 是一个隐藏了具体类型的指针,按苹果的代码风格,估计里面藏了什么秘密!这个 info 字段就是下面的 retain 、 release 、 copyDescription 字段中的参数。
retain 是一个函数指针,用于对 info 执行 retain 操作。
relase 是一个函数指针,用于对 info 执行 release 操作。
copyDescription 是一个函数指针,用于生成一个字符串描述。
version 和其他几个函数指针没什么提供不了什么有效信息,我们还是得从 info 字段入手。 先看下 info 字段的文档。
1
An arbitrary pointer to program-defined data, which can be associated with the message port at creation time. This pointer is passed to all the callbacks defined in the context.
能看出 info 字段里确实存储着程序设置进去的数据,但是还是没说具体类型!OC Runtime 代码也没有找到蛛丝马迹。只能直接相信 FBRetainCycleDetector 的源码了。
/** FBObjectiveCGraphElement specialization that can gather all references kept in ivars, as part of collection etc. */ @interfaceFBObjectiveCObject : FBObjectiveCGraphElement @end
/** * Returns a description of the \c Ivar layout for a given class. * * @param cls The class to inspect. * * @return A description of the \c Ivar layout for \e cls. */ OBJC_EXPORT const uint8_t * _Nullable class_getIvarLayout(Class _Nullable cls) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
/** * Returns a description of the layout of weak Ivars for a given class. * * @param cls The class to inspect. * * @return A description of the layout of the weak \c Ivars for \e cls. */ OBJC_EXPORT const uint8_t * _Nullable class_getWeakIvarLayout(Class _Nullable cls) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
所以,拿到 ivar 列表之后,我们只需遍历一遍所有的 ivar ,判断该 ivar 对应的布局位置是否为强引用即可。
// NSMapTable.h /* return an NSPointerFunctions object reflecting the functions in use. This is a new autoreleased object that can be subsequently modified and/or used directly in the creation of other pointer "collections". */ @property (readonly, copy) NSPointerFunctions *keyPointerFunctions; @property (readonly, copy) NSPointerFunctions *valuePointerFunctions;
// 是否强引用 value - (BOOL)_objectRetainsEnumerableValues { if ([self.object respondsToSelector:@selector(valuePointerFunctions)]) { NSPointerFunctions *pointerFunctions = [self.object valuePointerFunctions]; if (pointerFunctions.acquireFunction == NULL) { returnNO; } if (pointerFunctions.usesWeakReadAndWriteBarriers) { returnNO; } } // 默认为强引用(如 NSArray) returnYES; }
// 是否强引用key - (BOOL)_objectRetainsEnumerableKeys { if ([self.object respondsToSelector:@selector(pointerFunctions)]) { // NSHashTable and similar // If object shows what pointer functions are used, lets try to determine // if it's not retaining objects NSPointerFunctions *pointerFunctions = [self.object pointerFunctions]; if (pointerFunctions.acquireFunction == NULL) { returnNO; } if (pointerFunctions.usesWeakReadAndWriteBarriers) { // It's weak - we should not touch it returnNO; } }
if ([self.object respondsToSelector:@selector(keyPointerFunctions)]) { NSPointerFunctions *pointerFunctions = [self.object keyPointerFunctions]; if (pointerFunctions.acquireFunction == NULL) { returnNO; } if (pointerFunctions.usesWeakReadAndWriteBarriers) { returnNO; } } // 默认为强引用(如 NSDictionary) returnYES; }
总结: 通过遍历集合元素(包括 key 和 value ),并判断其是否为强引用,可以获取集合类强引用的所有元素。
/** * Sets an associated value for a given object using a given key and association policy. * * @param object The source object for the association. * @param key The key for the association. * @param value The value to associate with the key key for object. Pass nil to clear an existing association. * @param policy The policy for the association. For possible values, see “Associative Object Behaviors.” * * @see objc_setAssociatedObject * @see objc_removeAssociatedObjects */ OBJC_EXPORT void objc_setAssociatedObject(id _Nonnull object, constvoid * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy) OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
其中的 policy 用于设置引用类型,有如下取值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* Associative References */
/** * Policies related to associative references. * These are options to objc_setAssociatedObject() */ typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) { OBJC_ASSOCIATION_ASSIGN = 0, /**< Specifies a weak reference to the associated object. */ OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. * The association is not made atomically. */ OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied. * The association is not made atomically. */ OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object. * The association is made atomically. */ OBJC_ASSOCIATION_COPY = 01403/**< Specifies that the associated object is copied. * The association is made atomically. */ };
/** * Initialize this FlutterEngine with a `FlutterDartProject`. * * If the FlutterDartProject is not specified, the FlutterEngine will attempt to locate * the project in a default location (the flutter_assets folder in the iOS application * bundle). * * @param labelPrefix The label prefix used to identify threads for this instance. Should * be unique across FlutterEngine instances, and is used in instrumentation to label * the threads used by this FlutterEngine. * @param project The `FlutterDartProject` to run. */ - (instancetype)initWithName:(NSString*)labelPrefix project:(nullable FlutterDartProject*)project;
- (void)launchEngine:(NSString*)entrypoint libraryURI:(NSString*)libraryOrNil { // Launch the Dart application with the inferred run configuration. self.shell.RunEngine([_dartProject.get() runConfigurationForEntrypoint:entrypoint libraryOrNil:libraryOrNil]);
```objc @interfaceFlutterDartProject : NSObject /** * Initializes a Flutter Dart project from a bundle. */ - (instancetype)initWithPrecompiledDartBundle:(nullableNSBundle*)bundle NS_DESIGNATED_INITIALIZER;
/** * Returns the file name for the given asset. If the bundle with the identifier * "io.flutter.flutter.app" exists, it will try use that bundle; otherwise, it * will use the main bundle. To specify a different bundle, use * `-lookupKeyForAsset:asset:fromBundle`. * * @param asset The name of the asset. The name can be hierarchical. * @return the file name to be used for lookup in the main bundle. */ + (NSString*)lookupKeyForAsset:(NSString*)asset;
# Adds the App.framework as an embedded binary and the flutter_assets as # resources. EmbedFlutterFrameworks() { # Embed App.framework from Flutter into the app (after creating the Frameworks directory #if it doesn't already exist). local xcode_frameworks_dir="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" RunCommand mkdir -p -- "${xcode_frameworks_dir}" RunCommand rsync -av --delete --filter "- .DS_Store/" "${BUILT_PRODUCTS_DIR}/App.framework" "${xcode_frameworks_dir}"
# Embed the actual Flutter.framework that the Flutter app expects to run against, #which could be a local build or an arch/type specific build.
# Copy Xcode behavior and don't copy over headers or modules. RunCommand rsync -av --delete --filter "- .DS_Store/" --filter "- Headers/" --filter "- Modules/" "${BUILT_PRODUCTS_DIR}/Flutter.framework" "${xcode_frameworks_dir}/" if [[ "$ACTION" != "install" || "$ENABLE_BITCODE" == "NO" ]]; then # Strip bitcode from the destination unless archiving, or if bitcode is disabled entirely. RunCommand "${DT_TOOLCHAIN_DIR}"/usr/bin/bitcode_strip "${BUILT_PRODUCTS_DIR}/Flutter.framework/Flutter" -r -o "${xcode_frameworks_dir}/Flutter.framework/Flutter" fi
# Sign the binaries we moved. if [[ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" ]]; then RunCommand codesign --force --verbose --sign "${EXPANDED_CODE_SIGN_IDENTITY}" -- "${xcode_frameworks_dir}/App.framework/App" RunCommand codesign --force --verbose --sign "${EXPANDED_CODE_SIGN_IDENTITY}" -- "${xcode_frameworks_dir}/Flutter.framework/Flutter" fi
/** * Initializes this FlutterViewController with the specified `FlutterEngine`. * * The initialized viewcontroller will attach itself to the engine as part of this process. * * @param engine The `FlutterEngine` instance to attach to. * @param nibNameOrNil The NIB name to initialize this UIViewController with. * @param nibBundleOrNil The NIB bundle. */ - (instancetype)initWithEngine:(FlutterEngine*)engine nibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil NS_DESIGNATED_INITIALIZER;
/** * Initializes a new FlutterViewController and `FlutterEngine` with the specified * `FlutterDartProject`. * * @param projectOrNil The `FlutterDartProject` to initialize the `FlutterEngine` with. * @param nibNameOrNil The NIB name to initialize this UIViewController with. * @param nibBundleOrNil The NIB bundle. */ - (instancetype)initWithProject:(FlutterDartProject*)projectOrNil nibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithEngine:(FlutterEngine*)engine nibName:(nullableNSString*)nibName bundle:(nullableNSBundle*)nibBundle { NSAssert(engine != nil, @"Engine is required"); self = [super initWithNibName:nibName bundle:nibBundle]; if (self) { _viewOpaque = YES; if (engine.viewController) { FML_LOG(ERROR) << "The supplied FlutterEngine " << [[engine description] UTF8String] << " is already used with FlutterViewController instance " << [[engine.viewController description] UTF8String] << ". One instance of the FlutterEngine can only be attached to one " "FlutterViewController at a time. Set FlutterEngine.viewController " "to nil before attaching it to another FlutterViewController."; } // 此处省略 ... } returnself; }
/// Returns the result of combining the elements of the sequence using the given closure. @inlinable publicfuncreduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element)throws -> Result) rethrows -> Result
reduce(into:_:)
1 2
/// Returns the result of combining the elements of the sequence using the given closure. @inlinable publicfuncreduce<Result>(into initialResult: Result, _ updateAccumulatingResult: (inout Result, Element)throws -> ()) rethrows -> Result
两个方法的作用完全一样,都是 Returns the result of combining the elements of the sequence using the given closure ,翻译过来就是 使用传入的closure合并序列并返回 。在使用上两个方法有什么区别呢?
TLDR: 具体作用没区别。只是当返回结果为Array、Dictionary等 Coy On Write 类型的数据时,优先使用reduce(into:_:) 以提升性能。
reduce(_:_:)
1 2 3 4 5 6 7 8 9 10 11 12 13
/// - Parameters: /// - initialResult: The value to use as the initial accumulating value. /// `initialResult` is passed to `nextPartialResult` the first time the /// closure is executed. /// - nextPartialResult: A closure that combines an accumulating value and /// an element of the sequence into a new accumulating value, to be used /// in the next call of the `nextPartialResult` closure or returned to /// the caller. /// - Returns: The final accumulated value. If the sequence has no elements, /// the result is `initialResult`. /// /// - Complexity: O(*n*), where *n* is the length of the sequence. @inlinable publicfuncreduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element)throws -> Result) rethrows -> Result
以下面的代码为例,介绍reduce(_:_:)的执行流程。
1 2 3 4 5
let numbers = [1, 2, 3, 4] let numberSum = numbers.reduce(0, { x, y in x + y }) // numberSum == 10
/// - Parameters: /// - initialResult: The value to use as the initial accumulating value. /// - updateAccumulatingResult: A closure that updates the accumulating /// value with an element of the sequence. /// - Returns: The final accumulated value. If the sequence has no elements, /// the result is `initialResult`. /// /// - Complexity: O(*n*), where *n* is the length of the sequence. @inlinable publicfuncreduce<Result>(into initialResult: Result, _ updateAccumulatingResult: (inout Result, Element)throws -> ()) rethrows -> Result
以下面的代码为例,介绍reduce(into:_:)的执行流程。
1 2 3 4 5
let letters = "abracadabra" let letterCount = letters.reduce(into: [:]) { counts, letter in counts[letter, default: 0] += 1 } // letterCount == ["a": 5, "b": 2, "r": 2, "c": 1, "d": 1]
This method(reduce(into:_:)) is preferred over reduce(_:_:) for efficiency when the result is a copy-on-write type, for example an Array or a Dictionary.
A Unicode string value that is a collection of characters. A string is a series of characters, such as “Swift”, that forms a collection. Strings in Swift are Unicode correct and locale insensitive, and are designed to be efficient. The String type bridges with the Objective-C class NSString and offers interoperability with C functions that works with strings.
IMP class_swizzleSelector(Class clazz, SEL selector, IMP newImplementation) { // If the method does not exist for this class, do nothing Method method = class_getInstanceMethod(clazz, selector); if (! method) { returnNULL; } // Make sure the class implements the method. If this is not the case, inject an implementation, calling 'super' constchar *types = method_getTypeEncoding(method); class_addMethod(clazz, selector, imp_implementationWithBlock(^(__unsafe_unretainedidself, va_list argp) { struct objc_super super = { .receiver = self, .super_class = class_getSuperclass(clazz) }; id (*objc_msgSendSuper_typed)(struct objc_super *, SEL, va_list) = (void *)&objc_msgSendSuper; return objc_msgSendSuper_typed(&super, selector, argp); }), types); // Can now safely swizzle return class_replaceMethod(clazz, selector, newImplementation, types); }
IMP class_swizzleSelector_stret(Class clazz, SEL selector, IMP newImplementation) { // If the method does not exist for this class, do nothing Method method = class_getInstanceMethod(clazz, selector); if (! method) { returnNULL; } // Make sure the class implements the method. If this is not the case, inject an implementation, only calling 'super' constchar *types = method_getTypeEncoding(method); class_addMethod(clazz, selector, imp_implementationWithBlock(^(__unsafe_unretainedidself, va_list argp) { struct objc_super super = { .receiver = self, .super_class = class_getSuperclass(clazz) }; // Sufficiently large struct typedefstruct LargeStruct_ { char dummy[16]; } LargeStruct; // Cast the call to objc_msgSendSuper_stret appropriately LargeStruct (*objc_msgSendSuper_stret_typed)(struct objc_super *, SEL, va_list) = (void *)&objc_msgSendSuper_stret; return objc_msgSendSuper_stret_typed(&super, selector, argp); }), types); // Can now safely swizzle return class_replaceMethod(clazz, selector, newImplementation, types); }
During normal operations, iTunes communicates with the iPhone using something called “usbmux” – this is a system for multiplexing several “connections” over one USB pipe. Conceptually, it provides a TCP-like system – processes on the host machine open up connections to specific, numbered ports on the mobile device. (This resemblance is more than superficial – on the mobile device, usbmuxd actually makes TCP connections to localhost using the port number you give it.)
<?xml version="1.0" encoding="UTF-8"?> <pmd-cpd> <duplicationlines="7"tokens="23"> <fileline="24" path="..Demo/ViewController.m"/> <fileline="36" path="..Demo/ViewController.m"/> <codefragment><![CDATA[- (int)hello0 { int a = 0, b = 0; int c = a + b; return c; }
明显的性能提升 大会keynote上写着APP启动时间提升了 (up to)40%。虽然有个 up to_,但是如果真能有这个级别的提升,那还真挺令人激动的,毕竟大家一直在探索各种降低启动时间的方法。从另一个方面想,_iOS 11 到底是有多慢…
Siri Shortcut 使用 siri 完成一系列预定事件,标志着Siri终于支持workflow,看着是个不错的功能。但是明显能感觉到苹果在推进 Siri 的路上走得并不快。而且从之前接入SiriKit的经验来看,预计会有不少坑,比如识别的语法太固定,识别的场景太狭窄,而且之前接入SiriKit之后出现各种各样的审核问题和奇怪的崩溃问题让人很心累。希望这一次 Siri 可以做得更好。
While doing some research for this post I found a commit to libdispatch that ensures that blocks dispatched with dispatch_sync are always executed on the current thread. This means if you use dispatch_sync to dispatch a block from the main queue to a concurrent background queue, the code executing on the background queue will actually be executed on the main thread. While this might not be entirely intuitive, it makes sense: since the main queue needs to wait until the dispatched block completed, the main thread will be available to process blocks from queues other than the main queue.
还记得文章一开始,我们说32位机器下,OC中的 BOOL 类型实际是 signed char,可以表示-128~127。在执行到 check 方法的第一句时,由于 [self returnValue] 返回的是int类型的数值,所以会进行以下的 int -> signed char(BOOL)类型转换:
BOOL yesOrNo = (BOOL)256;
而 signed char 占一个字节,也就是8 bit。 256转换成二进制的末16位是 00000001 00000000,强制转换为 signed char 后,取末8位的值 00000000,也就是0。到这里,大家应该意识到,所有末8位全为0的数字,如512、1024…,在32位机器上被强制转换位 BOOL 类型时,都会被转换位 NO(0)。之所以会发生这种“神奇的现象”,是因为32位机器上的 BOOL 类型在“作祟”。 指针类型也一样。继续拿上面的代码举例,这次我们把 returnValue 方法改造下: